code:
name = mesecons_switch
depends = mesecons


code:
The torch is an inverter, it may take up to 1 second until the signal has passed through. The input is 2 blocks away in the direction of the stick, outputs are around the mesecon glow.
It doesn't work in an inactive block; it just retains its state until the block becomes active.

repo_name: Desour/PlayMinetestServer

license: mit


code:
--MESECON TORCHES

local rotate_torch_rules = function (rules, param2)
	if param2 == 5 then
		return mesecon.rotate_rules_right(rules)
	elseif param2 == 2 then
		return mesecon.rotate_rules_right(mesecon.rotate_rules_right(rules)) --180 degrees
	elseif param2 == 4 then
		return mesecon.rotate_rules_left(rules)
	elseif param2 == 1 then
		return mesecon.rotate_rules_down(rules)
	elseif param2 == 0 then
		return mesecon.rotate_rules_up(rules)
	else
		return rules
	end
end

local torch_get_output_rules = function(node)
	local rules = {
		{x = 1,  y = 0, z = 0},
		{x = 0,  y = 0, z = 1},
		{x = 0,  y = 0, z =-1},
		{x = 0,  y = 1, z = 0},
		{x = 0,  y =-1, z = 0}}

	return rotate_torch_rules(rules, node.param2)
end

local torch_get_input_rules = function(node)
	local rules = 	{{x = -2, y = 0, z = 0},
				 {x = -1, y = 1, z = 0}}

	return rotate_torch_rules(rules, node.param2)
end

minetest.register_craft({
	output = "mesecons_torch:mesecon_torch_on 4",
	recipe = {
	{"group:mesecon_conductor_craftable"},
	{"default:stick"},}
})

local torch_selectionbox =
{
	type = "wallmounted",
	wall_top = {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
	wall_bottom = {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
	wall_side = {-0.5, -0.1, -0.1, -0.5+0.6, 0.1, 0.1},
}

minetest.register_node("mesecons_torch:mesecon_torch_off", {
	drawtype = "torchlike",
	tiles = {"jeija_torches_off.png", "jeija_torches_off_ceiling.png", "jeija_torches_off_side.png"},
	inventory_image = "jeija_torches_off.png",
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	paramtype2 = "wallmounted",
	selection_box = torch_selectionbox,
	groups = {dig_immediate = 3, not_in_creative_inventory = 1},
	drop = "mesecons_torch:mesecon_torch_on",
	sounds = default.node_sound_defaults(),
	mesecons = {receptor = {
		state = mesecon.state.off,
		rules = torch_get_output_rules
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_torch:mesecon_torch_on", {
	drawtype = "torchlike",
	tiles = {"jeija_torches_on.png", "jeija_torches_on_ceiling.png", "jeija_torches_on_side.png"},
	inventory_image = "jeija_torches_on.png",
	wield_image = "jeija_torches_on.png",
	paramtype = "light",
	is_ground_content = false,
	sunlight_propagates = true,
	walkable = false,
	paramtype2 = "wallmounted",
	selection_box = torch_selectionbox,
	groups = {dig_immediate=3},
	light_source = minetest.LIGHT_MAX-5,
	description="Mesecon Torch",
	sounds = default.node_sound_defaults(),
	mesecons = {receptor = {
		state = mesecon.state.on,
		rules = torch_get_output_rules
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_abm({
	nodenames = {"mesecons_torch:mesecon_torch_off","mesecons_torch:mesecon_torch_on"},
	interval = 1,
	chance = 1,
	action = function(pos, node)
		local is_powered = false
		for _, rule in ipairs(torch_get_input_rules(node)) do
			local src = vector.add(pos, rule)
			if mesecon.is_power_on(src) then
				is_powered = true
			end
		end

		if is_powered then
			if node.name == "mesecons_torch:mesecon_torch_on" then
				minetest.swap_node(pos, {name = "mesecons_torch:mesecon_torch_off", param2 = node.param2})
				mesecon.receptor_off(pos, torch_get_output_rules(node))
			end
		elseif node.name == "mesecons_torch:mesecon_torch_off" then
			minetest.swap_node(pos, {name = "mesecons_torch:mesecon_torch_on", param2 = node.param2})
			mesecon.receptor_on(pos, torch_get_output_rules(node))
		end
	end
})

-- Param2 Table (Block Attached To)
-- 5 = z-1
-- 3 = x-1
-- 4 = z+1
-- 2 = x+1
-- 0 = y+1
-- 1 = y-1


code:
name = mesecons_torch
depends = mesecons


code: A receptor just like a switch, but it can be attached to walls.


code:
-- WALL LEVER
-- Basically a switch that can be attached to a wall
-- Powers the block 2 nodes behind (using a receiver)
mesecon.register_node("mesecons_walllever:wall_lever", {
	description="Lever",
	drawtype = "mesh",
	inventory_image = "jeija_wall_lever_inv.png",
	wield_image = "jeija_wall_lever_inv.png",
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	sunlight_propagates = true,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = { -8/16, -8/16, 3/16, 8/16, 8/16, 8/16 },
	},
	sounds = default.node_sound_wood_defaults(),
	on_rightclick = function (pos, node)
		if(mesecon.flipstate(pos, node) == "on") then
			mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node))
		else
			mesecon.receptor_off(pos, mesecon.rules.buttonlike_get(node))
		end
		minetest.sound_play("mesecons_lever", {pos=pos})
	end
},{
	tiles = {
		"jeija_wall_lever_lever_light_off.png",
		"jeija_wall_lever_front.png",
		"jeija_wall_lever_front_bump.png",
		"jeija_wall_lever_back_edges.png"
	},
	mesh="jeija_wall_lever_off.obj",
	on_rotate = mesecon.buttonlike_onrotate,
	mesecons = {receptor = {
		rules = mesecon.rules.buttonlike_get,
		state = mesecon.state.off
	}},
	groups = {dig_immediate = 2, mesecon_needs_receiver = 1}
},{
	tiles = {
		"jeija_wall_lever_lever_light_on.png",
		"jeija_wall_lever_front.png",
		"jeija_wall_lever_front_bump.png",
		"jeija_wall_lever_back_edges.png"
	},
	mesh="jeija_wall_lever_on.obj",
	on_rotate = false,
	mesecons = {receptor = {
		rules = mesecon.rules.buttonlike_get,
		state = mesecon.state.on
	}},
	groups = {dig_immediate = 2, mesecon_needs_receiver = 1, not_in_creative_inventory = 1}
})

minetest.register_craft({
	output = "mesecons_walllever:wall_lever_off 2",
	recipe = {
	    {"group:mesecon_conductor_craftable"},
		{"default:stone"},
		{"default:stick"},
	}
})


code:
name = mesecons_walllever
depends = mesecons, mesecons_receiver


code: Mesecons are the wires, use them to connect effectors with receptors. Wiring works through unloaded blocks; they’re loaded when the signal level changes.


code:
-- naming scheme: wire:(xp)(zp)(xm)(zm)(xpyp)(zpyp)(xmyp)(zmyp)_on/off
-- where x= x direction, z= z direction, y= y direction, p = +1, m = -1, e.g. xpym = {x=1, y=-1, z=0}
-- The (xp)/(zpyp)/.. statements shall be replaced by either 0 or 1
-- Where 0 means the wire has no visual connection to that direction and
-- 1 means that the wire visually connects to that other node.

-- #######################
-- ## Update wire looks ##
-- #######################

-- self_pos = pos of any mesecon node, from_pos = pos of conductor to getconnect for
local wire_getconnect = function (from_pos, self_pos)
	local node = minetest.get_node(self_pos)
	if minetest.registered_nodes[node.name]
	and minetest.registered_nodes[node.name].mesecons then
		-- rules of node to possibly connect to
		local rules = {}
		if (minetest.registered_nodes[node.name].mesecon_wire) then
			rules = mesecon.rules.default
		else
			rules = mesecon.get_any_rules(node)
		end

		for _, r in ipairs(mesecon.flattenrules(rules)) do
			if (vector.equals(vector.add(self_pos, r), from_pos)) then
				return true
			end
		end
	end
	return false
end

-- Update this node
local wire_updateconnect = function (pos)
	local connections = {}

	for _, r in ipairs(mesecon.rules.default) do
		if wire_getconnect(pos, vector.add(pos, r)) then
			table.insert(connections, r)
		end
	end

	local nid = {}
	for _, vec in ipairs(connections) do
		-- flat component
		if vec.x ==  1 then nid[0] = "1" end
		if vec.z ==  1 then nid[1] = "1" end
		if vec.x == -1 then nid[2] = "1" end
		if vec.z == -1 then nid[3] = "1"  end

		-- slopy component
		if vec.y == 1 then
			if vec.x ==  1 then nid[4] = "1" end
			if vec.z ==  1 then nid[5] = "1" end
			if vec.x == -1 then nid[6] = "1" end
			if vec.z == -1 then nid[7] = "1" end
		end
	end

	local nodeid = 	  (nid[0] or "0")..(nid[1] or "0")..(nid[2] or "0")..(nid[3] or "0")
			..(nid[4] or "0")..(nid[5] or "0")..(nid[6] or "0")..(nid[7] or "0")

	local state_suffix = string.find(minetest.get_node(pos).name, "_off") and "_off" or "_on"
	minetest.set_node(pos, {name = "mesecons:wire_"..nodeid..state_suffix})
end

local update_on_place_dig = function (pos, node)
	-- Update placed node (get_node again as it may have been dug)
	local nn = minetest.get_node(pos)
	if (minetest.registered_nodes[nn.name])
	and (minetest.registered_nodes[nn.name].mesecon_wire) then
		wire_updateconnect(pos)
	end

	-- Update nodes around it
	local rules = {}
	if minetest.registered_nodes[node.name]
	and minetest.registered_nodes[node.name].mesecon_wire then
		rules = mesecon.rules.default
	else
		rules = mesecon.get_any_rules(node)
	end
	if (not rules) then return end

	for _, r in ipairs(mesecon.flattenrules(rules)) do
		local np = vector.add(pos, r)
		if minetest.registered_nodes[minetest.get_node(np).name]
		and minetest.registered_nodes[minetest.get_node(np).name].mesecon_wire then
			wire_updateconnect(np)
		end
	end
end

mesecon.register_autoconnect_hook("wire", update_on_place_dig)

-- ############################
-- ## Wire node registration ##
-- ############################
-- Nodeboxes:
local box_center = {-1/16, -.5, -1/16, 1/16, -.5+1/16, 1/16}
local box_bump1 =  { -2/16, -8/16,  -2/16, 2/16, -13/32, 2/16 }

local nbox_nid =
{
	[0] = {1/16, -.5, -1/16, 8/16, -.5+1/16, 1/16}, -- x positive
	[1] = {-1/16, -.5, 1/16, 1/16, -.5+1/16, 8/16}, -- z positive
	[2] = {-8/16, -.5, -1/16, -1/16, -.5+1/16, 1/16}, -- x negative
	[3] = {-1/16, -.5, -8/16, 1/16, -.5+1/16, -1/16}, -- z negative

	[4] = {.5-1/16, -.5+1/16, -1/16, .5, .4999+1/16, 1/16}, -- x positive up
	[5] = {-1/16, -.5+1/16, .5-1/16, 1/16, .4999+1/16, .5}, -- z positive up
	[6] = {-.5, -.5+1/16, -1/16, -.5+1/16, .4999+1/16, 1/16}, -- x negative up
	[7] = {-1/16, -.5+1/16, -.5, 1/16, .4999+1/16, -.5+1/16}  -- z negative up
}

local tiles_off = { "mesecons_wire_off.png" }
local tiles_on = { "mesecons_wire_on.png" }

local selectionbox =
{
	type = "fixed",
	fixed = {-.5, -.5, -.5, .5, -.5+4/16, .5}
}

-- go to the next nodeid (ex.: 01000011 --> 01000100)
local nid_inc = function() end
nid_inc = function (nid)
	local i = 0
	while nid[i-1] ~= 1 do
		nid[i] = (nid[i] ~= 1) and 1 or 0
		i = i + 1
	end

	-- BUT: Skip impossible nodeids:
	if ((nid[0] == 0 and nid[4] == 1) or (nid[1] == 0 and nid[5] == 1)
	or (nid[2] == 0 and nid[6] == 1) or (nid[3] == 0 and nid[7] == 1)) then
		return nid_inc(nid)
	end

	return i <= 8
end

local function register_wires()
	local nid = {}
	while true do
		-- Create group specifiction and nodeid string (see note above for details)
		local nodeid = 	  (nid[0] or "0")..(nid[1] or "0")..(nid[2] or "0")..(nid[3] or "0")
				..(nid[4] or "0")..(nid[5] or "0")..(nid[6] or "0")..(nid[7] or "0")

		-- Calculate nodebox
		local nodebox = {type = "fixed", fixed={box_center}}
		for i=0,7 do
			if nid[i] == 1 then
				table.insert(nodebox.fixed, nbox_nid[i])
			end
		end

		-- Add bump to nodebox if curved
		if (nid[0] == 1 and nid[1] == 1) or (nid[1] == 1 and nid[2] == 1)
		or (nid[2] == 1 and nid[3] == 1) or (nid[3] == 1 and nid[0] == 1) then
			table.insert(nodebox.fixed, box_bump1)
		end

		-- If nothing to connect to, still make a nodebox of a straight wire
		if nodeid == "00000000" then
			nodebox.fixed = {-8/16, -.5, -1/16, 8/16, -.5+1/16, 1/16}
		end

		local rules = {}
		if (nid[0] == 1) then table.insert(rules, vector.new( 1,  0,  0)) end
		if (nid[1] == 1) then table.insert(rules, vector.new( 0,  0,  1)) end
		if (nid[2] == 1) then table.insert(rules, vector.new(-1,  0,  0)) end
		if (nid[3] == 1) then table.insert(rules, vector.new( 0,  0, -1)) end

		if (nid[0] == 1) then table.insert(rules, vector.new( 1, -1,  0)) end
		if (nid[1] == 1) then table.insert(rules, vector.new( 0, -1,  1)) end
		if (nid[2] == 1) then table.insert(rules, vector.new(-1, -1,  0)) end
		if (nid[3] == 1) then table.insert(rules, vector.new( 0, -1, -1)) end

		if (nid[4] == 1) then table.insert(rules, vector.new( 1,  1,  0)) end
		if (nid[5] == 1) then table.insert(rules, vector.new( 0,  1,  1)) end
		if (nid[6] == 1) then table.insert(rules, vector.new(-1,  1,  0)) end
		if (nid[7] == 1) then table.insert(rules, vector.new( 0,  1, -1)) end

		local meseconspec_off = { conductor = {
			rules = rules,
			state = mesecon.state.off,
			onstate = "mesecons:wire_"..nodeid.."_on"
		}}

		local meseconspec_on = { conductor = {
			rules = rules,
			state = mesecon.state.on,
			offstate = "mesecons:wire_"..nodeid.."_off"
		}}

		local groups_on = {dig_immediate = 3, mesecon_conductor_craftable = 1,
			not_in_creative_inventory = 1}
		local groups_off = {dig_immediate = 3, mesecon_conductor_craftable = 1}
		if nodeid ~= "00000000" then
			groups_off["not_in_creative_inventory"] = 1
		end

		mesecon.register_node(":mesecons:wire_"..nodeid, {
			description = "Mesecon",
			drawtype = "nodebox",
			inventory_image = "mesecons_wire_inv.png",
			wield_image = "mesecons_wire_inv.png",
			paramtype = "light",
			paramtype2 = "facedir",
			is_ground_content = false,
			sunlight_propagates = true,
			selection_box = selectionbox,
			node_box = nodebox,
			walkable = false,
			drop = "mesecons:wire_00000000_off",
			mesecon_wire = true,
			sounds = default.node_sound_defaults(),
			on_rotate = false,
		}, {tiles = tiles_off, mesecons = meseconspec_off, groups = groups_off},
		{tiles = tiles_on, mesecons = meseconspec_on, groups = groups_on})

		if (nid_inc(nid) == false) then return end
	end
end
register_wires()

-- ##############
-- ## Crafting ##
-- ##############
minetest.register_craft({
	type = "cooking",
	output = "mesecons:wire_00000000_off 2",
	recipe = "default:mese_crystal_fragment",
	cooktime = 3,
})

minetest.register_craft({
	type = "cooking",
	output = "mesecons:wire_00000000_off 18",
	recipe = "default:mese_crystal",
	cooktime = 15,
})

minetest.register_craft({
	type = "cooking",
	output = "mesecons:wire_00000000_off 162",
	recipe = "default:mese",
	cooktime = 30,
})


code:
name = mesecons_wires
depends = mesecons


code: name = mesecons


code:
[mesecons]

mesecon.resumetime (Startup delay) int 4 1 10
mesecon.overheat_max (Device heat limit) int 20 1 100
mesecon.cooldown_time (Device cooldown time) float 2.0 0.1 10.0
mesecon.cooldown_granularity (Cooldown step length) float 0.5 0.0 1.0

[mesecons_blinkyplant]

mesecon.blinky_plant_interval (Plant blinking interval) int 3 1 5

[mesecons_detector]

mesecon.detector_radius (Player detector scanning radius) int 6 3 16
mesecon.node_detector_distance_max (Node detector distance limit) int 10 1 16

[mesecons_luacontroller]

mesecon.luacontroller_string_rep_max (string:rep result length limit) int 64000 1000 1000000
mesecon.luacontroller_digiline_maxlen (Digiline message size limit) int 50000 1000 1000000
mesecon.luacontroller_maxevents (Controller execution time limit) int 10000 1000 100000
mesecon.luacontroller_memsize (Controller memory limit) int 100000 10000 1000000

# Use node timer for interrupts (runs in active blocks only).
# IID is ignored and at most one interrupt may be queued if this setting is enabled.
mesecon.luacontroller_lightweight_interrupts (Lightweight interrupts) bool false

[mesecons_mvps]

# In pre-existing world, MVPS may not be labelled with the owner.
# Protection handling for them is configurable.
# - normal: allow legacy MVPS to work in unprotected areas only
# - compat: allow legacy MVPS to work everywhere
# - ignore: allow all MVPS to work everywhere
# - restrict: disallow legacy MVPS
# Note that new unowned (e.g. machine-placed) MVPS are always
# handled as in `normal` mode.
mesecon.mvps_protection_mode (MVPS [movestones, pistons] protection handling) enum compat normal,compat,ignore,restrict

[mesecons_movestones]

mesecon.movestone_speed (Speed) int 3 1 10
mesecon.movestone_max_push (Max push) int 50 1 100
mesecon.movestone_max_pull (Max pull) int 50 1 100

[mesecons_pistons]

mesecon.piston_max_push (Max push) int 15 1 100
mesecon.piston_max_pull (Max pull) int 15 1 100

[mesecons_pressureplates]

mesecon.pplate_interval (Check interval) float 0.1 0.1 1.0


code:
name: luacheck

on: [pull_request, push]

jobs:
  build:

    runs-on: ubuntu-latest

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


code:
globals = {
	"mesecons_debug",
	"mesecon",
	"minetest"
}

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

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

	"monitoring"
}


code:
# Mesecons Debug Collection

Allows to throttle mesecons activity per mapblock

# Overview

There is a cpu quota for every mapblock, if that quota is used up
the mesecons contraptions will be slowed down for that mapblock

The current mapblock-stats can be viewed with `/mesecons_hud on`

## Settings

* none yet

## Privs

* **mesecons_debug** Allows execution of mesecon debug chatcommands

## Commands

All of these commands require the `mesecons_debug` privilege.

* `/mesecons_hud [on|off]` enables or disables the hud
* `/mesecons_flush` Flushes the action queue
* `/mesecons_enable` Enable the mesecons queue
* `/mesecons_disable` Disables the mesecons queue
* `/mesecons_stats` shows some mesecons stats for the current position
* `/mesecons_whitelist_get` shows the list of whitelisted mapblocks
* `/mesecons_whitelist_add` adds the current mapblock to the whitelist
* `/mesecons_whitelist_remove` removes the current mapblock from the whitelist


code:
minetest.register_chatcommand("mesecons_hud", {
  description = "mesecons_hud on/off",
  func = function(name, params)
    local enable = params == "on"
    mesecons_debug.hud[name] = enable
    if enable then
      return true, "mesecons hud enabled"
    else
      return true, "mesecons hud disabled"
    end
  end
})

minetest.register_chatcommand("mesecons_stats", {
  description = "shows some mesecons stats for the current position",
  func = function(name)
    local player = minetest.get_player_by_name(name)
    if not player then
      return
    end

    local ctx = mesecons_debug.get_context(player:get_pos())
    return true, "Mapblock usage: " .. ctx.avg_micros .. " us/s " ..
      "(across " .. mesecons_debug.context_store_size .." mapblocks)"
  end
})

minetest.register_chatcommand("mesecons_enable", {
  description = "enables the mesecons globlastep",
  privs = {mesecons_debug=true},
  func = function()
    -- flush actions, while we are on it
    mesecon.queue.actions = {}
    mesecons_debug.enabled = true
    return true, "mesecons enabled"
  end
})

minetest.register_chatcommand("mesecons_disable", {
  description = "disables the mesecons globlastep",
  privs = {mesecons_debug=true},
  func = function()
    mesecons_debug.enabled = false
    return true, "mesecons disabled"
  end
})

minetest.register_chatcommand("mesecons_whitelist_get", {
  description = "shows the current mapblock whitelist",
  privs = {mesecons_debug=true},
  func = function()
    local whitelist = "mesecons whitelist:\n"
    local count = 0
    for hash, _ in pairs(mesecons_debug.whitelist) do
      whitelist = whitelist .. minetest.pos_to_string(minetest.get_position_from_hash(hash)) .. "\n"
      count = count + 1
    end
    whitelist = whitelist .. string.format("%d mapblocks whitelisted", count)

    return true, whitelist
  end
})

minetest.register_chatcommand("mesecons_whitelist_add", {
  description = "adds the current mapblock to the whitelist",
  privs = {mesecons_debug=true},
  func = function(name)
    local player = minetest.get_player_by_name(name)
    if not player then
      return
    end

    local ppos = player:get_pos()
    local blockpos = mesecons_debug.get_blockpos(ppos)
    local hash = minetest.hash_node_position(blockpos)

    mesecons_debug.whitelist[hash] = true
    mesecons_debug.save_whitelist()

    return true, "mapblock whitlisted"
  end
})

minetest.register_chatcommand("mesecons_whitelist_remove", {
  description = "removes the current mapblock from the whitelist",
  privs = {mesecons_debug=true},
  func = function(name)
    local player = minetest.get_player_by_name(name)
    if not player then
      return
    end

    local ppos = player:get_pos()
    local blockpos = mesecons_debug.get_blockpos(ppos)
    local hash = minetest.hash_node_position(blockpos)

    mesecons_debug.whitelist[hash] = nil
    mesecons_debug.save_whitelist()

    return true, "mapblock removed from whitelist"
  end
})


code:
local has_monitoring = minetest.get_modpath("monitoring")

local mapblock_count, penalized_mapblock_count

if has_monitoring then
	mapblock_count = monitoring.gauge("mesecons_debug_mapblock_count", "count of tracked mapblocks")
	penalized_mapblock_count = monitoring.gauge("mesecons_debug_penalized_mapblock_count", "count of penalized mapblocks")
end

-- blockpos-hash => context
local context_store = {}

mesecons_debug.get_context = function(pos)
  local blockpos = mesecons_debug.get_blockpos(pos)
  local hash = minetest.hash_node_position(blockpos)

  local ctx = context_store[hash]
  if not ctx then
    -- create a new context
    ctx = {
      -- usage in us
      micros = 0,
      -- average micros per second
      avg_micros = 0,
      -- time penalty
      penalty = 0,

			-- mtime
			mtime = minetest.get_us_time(),
    }
    context_store[hash] = ctx
  end

	-- update context

	-- whitelist flag
	ctx.whitelisted = mesecons_debug.whitelist[hash]

  return ctx
end

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

	local penalized_count = 0
	local now = minetest.get_us_time()
	local cleanup_time_micros = 300 * 1000 * 1000

  mesecons_debug.context_store_size = 0
  for hash, ctx in pairs(context_store) do
		local time_diff = now - ctx.mtime
		if time_diff > cleanup_time_micros then
			-- remove item
			context_store[hash] = nil

		else
			-- calculate stuff
			ctx.avg_micros = math.floor((ctx.avg_micros * 0.9) + (ctx.micros * 0.1))
	    ctx.micros = 0
	    if ctx.avg_micros > mesecons_debug.max_usage_micros then
				-- add penalty
	      ctx.penalty = math.min(ctx.penalty + 0.1, 20)
	    elseif ctx.penalty > 0 then
				-- remove penalty (slowly)
	      ctx.penalty = math.max(ctx.penalty - 0.01, 0)
	    end

	    mesecons_debug.context_store_size = mesecons_debug.context_store_size + 1
			if ctx.penalty > 0 then
				penalized_count = penalized_count + 1
			end

		end
  end

	if has_monitoring then
		mapblock_count.set(mesecons_debug.context_store_size)
		penalized_mapblock_count.set(penalized_count)
	end

end)


code:
minetest.register_chatcommand("mesecons_flush", {
	description = "flushes the mesecon actionqueue",
	privs = {mesecons_debug=true},
	func = function(name)
		minetest.log("warning", "Player " .. name .. " flushes mesecon actionqueue")
		mesecon.queue.actions = {}
	end
})


code:
function mesecons_debug.get_blockpos(pos)
	return {x = math.floor(pos.x / 16),
	        y = math.floor(pos.y / 16),
	        z = math.floor(pos.z / 16)}
end


code:
local HUD_POSITION = {x = 0.1, y = 0.8}
local HUD_ALIGNMENT = {x = 1, y = 0}

local hud = {}

minetest.register_on_joinplayer(function(player)
  local hud_data = {}
  hud[player:get_player_name()] = hud_data

  hud_data.txt = player:hud_add({
    hud_elem_type = "text",
    position = HUD_POSITION,
    offset = {x = 0,   y = 0},
    text = "",
    alignment = HUD_ALIGNMENT,
    scale = {x = 100, y = 100},
    number = 0xFF0000
  })

end)

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

local function get_info(player)
  local pos = player:get_pos()
  local blockpos = mesecons_debug.get_blockpos(pos)
  local ctx = mesecons_debug.get_context(pos)

  local percent = math.floor(ctx.avg_micros / mesecons_debug.max_usage_micros * 100)

  local txt = "Mesecons @ (" .. blockpos.x .. "/" .. blockpos.y .. "/" .. blockpos.z .. ") "

  if ctx.whitelisted then
    txt  = txt .. "whitelisted, no limits"
    return txt, 0x00FF00
  end

  txt = txt ..
    " usage: " .. ctx.avg_micros .. " us/s .. (" .. percent .. "%) " ..
    "penalty: " .. math.floor(ctx.penalty*10)/10 .. " s"

  if ctx.penalty <= 0.1 then
    return txt, 0x00FF00
  elseif ctx.penalty < 0.5 then
    return txt, 0xFFFF00
  else
    return txt, 0xFF0000
  end
end

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

  for _, player in ipairs(minetest.get_connected_players()) do
    local playername = player:get_player_name()
    local hud_data = hud[playername]
    local hud_enable = mesecons_debug.hud[playername]
    if hud_enable then
      local txt, color = get_info(player)
      player:hud_change(hud_data.txt, "text", txt)
      player:hud_change(hud_data.txt, "color", color)

    elseif hud_enable == false then
      mesecons_debug.hud[playername] = nil
      player:hud_change(hud_data.txt, "text", "")

    end

  end

end)


code:
local MP = minetest.get_modpath("mesecons_debug")

mesecons_debug = {
  enabled = true,
  context_store_size = 0,

  -- mapblock-hash -> true
  whitelist = {},

  -- playername => true
  hud = {},

  max_usage_micros = 45000
}

dofile(MP.."/functions.lua")
dofile(MP.."/whitelist.lua")
dofile(MP.."/privs.lua")
dofile(MP.."/flush.lua")
dofile(MP.."/context.lua")
dofile(MP.."/overrides.lua")
dofile(MP.."/luacontroller.lua")
dofile(MP.."/chatcommands.lua")
dofile(MP.."/hud.lua")

mesecons_debug.load_whitelist()

print("[OK] mesecons_debug loaded")


code:
local function override_node_timer(node_name)
  local def = minetest.registered_nodes[node_name]
  local old_node_timer = def.on_timer
  def.on_timer = function(pos)
    local ctx = mesecons_debug.get_context(pos)
    if ctx.penalty > 0 then
      -- defer
      local timer = minetest.get_node_timer(pos)
      local meta = minetest.get_meta(pos)
      local is_defered = meta:get_int("_defered") == 1

      if is_defered then
        -- already delayed
        meta:set_int("_defered", 0)
        return old_node_timer(pos)
      else
        -- start timer
        meta:set_int("_defered", 1)
        timer:start(ctx.penalty)
      end
    else
      -- immediate
      return old_node_timer(pos)
    end
  end
end

-- luaC
local BASENAME = "mesecons_luacontroller:luacontroller"
for a = 0, 1 do -- 0 = off  1 = on
for b = 0, 1 do
for c = 0, 1 do
for d = 0, 1 do
	local cid = tostring(d)..tostring(c)..tostring(b)..tostring(a)
	local node_name = BASENAME..cid
  override_node_timer(node_name)
end
end
end
end

-- blinky
override_node_timer("mesecons_blinkyplant:blinky_plant_off")
override_node_timer("mesecons_blinkyplant:blinky_plant_on")


code:
name = mesecons_debug
depends = mesecons, mesecons_blinkyplant, mesecons_luacontroller
optional_depends = monitoring


code:
-- execute()
local old_execute = mesecon.queue.execute
mesecon.queue.execute = function(self, action)
  if mesecons_debug.enabled then
    local t0 = minetest.get_us_time()
    old_execute(self, action)
    local t1 = minetest.get_us_time()
    local micros = t1 - t0

    local ctx = mesecons_debug.get_context(action.pos)
    ctx.micros = ctx.micros + micros
		ctx.mtime = t0

    --print("execute() func=" .. action.func .. " pos=" .. minetest.pos_to_string(action.pos) .. " micros=" .. micros)
  end
end

-- add_action()
local old_add_action = mesecon.queue.add_action
mesecon.queue.add_action = function(self, pos, func, params, time, overwritecheck, priority)
  if mesecons_debug.enabled then
    local ctx = mesecons_debug.get_context(pos)

    time = time or 0
    time = time + ctx.penalty

    old_add_action(self, pos, func, params, time, overwritecheck, priority)
    --print("add_action() pos=" .. minetest.pos_to_string(pos))
  end
end


code:
minetest.register_privilege("mesecons_debug", {
	description = "Allows execution of mesecon debug chatcommands"
})


code:
local filename = minetest.get_worldpath() .. "/mesecons_debug_whiltelist"

function mesecons_debug.save_whitelist()
  local file = io.open(filename,"w")
	local data = minetest.serialize(mesecons_debug.whitelist)
	if file and file:write(data) and file:close() then
		return
	else
		minetest.log("error","mesecons_debug: save failed")
		return
	end
end

function mesecons_debug.load_whitelist()
  local file = io.open(filename, "r")

  if file then
    local data = file:read("*a")
    mesecons_debug.whitelist = minetest.deserialize(data) or {}
  end
end


code:
mobs
lucky_block?
intllib?


code:
-- intllib
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP .. "/intllib.lua")

-- 0.4.17 or 5.0 check
local y_off = 20
if minetest.registered_nodes["default:permafrost"] then
	y_off = 10
end

-- horse shoes (speed, jump, break, overlay texture)
local shoes = {
	["mobs:horseshoe_steel"] = {7, 4, 2, "mobs_horseshoe_steelo.png"},
	["mobs:horseshoe_bronze"] = {7, 4, 4, "mobs_horseshoe_bronzeo.png"},
	["mobs:horseshoe_mese"] = {9, 5, 8, "mobs_horseshoe_meseo.png"},
	["mobs:horseshoe_diamond"] = {10, 6, 6, "mobs_horseshoe_diamondo.png"}
}

-- rideable horse

mobs:register_mob("mob_horse:horse", {
	type = "animal",
	visual = "mesh",
	visual_size = {x = 1.20, y = 1.20},
	mesh = "mobs_horse.x",
	collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.25, 0.4},
	animation = {
		speed_normal = 15,
		speed_run = 30,
		stand_start = 25,
		stand_end = 75,
		walk_start = 75,
		walk_end = 100,
		run_start = 75,
		run_end = 100,
	},
	textures = {
		{"mobs_horse.png"}, -- textures by Mjollna
		{"mobs_horsepeg.png"},
		{"mobs_horseara.png"}
	},
	fear_height = 3,
	runaway = true,
	fly = false,
	walk_chance = 60,
	view_range = 5,
	follow = {
		"farming:wheat", "default:apple", "farming:oat",
		"farming:barley", "farming:corn"},
	passive = true,
	hp_min = 12,
	hp_max = 16,
	armor = 200,
	lava_damage = 5,
	fall_damage = 5,
	water_damage = 1,
	makes_footstep_sound = true,
	drops = {
		{name = "mobs:leather", chance = 1, min = 0, max = 2}
	},

	do_custom = function(self, dtime)

		-- set needed values if not already present
		if not self.v2 then
			self.v2 = 0
			self.max_speed_forward = 6
			self.max_speed_reverse = 2
			self.accel = 6
			self.terrain_type = 3
			self.driver_attach_at = {x = 0, y = y_off, z = -2}
			self.driver_eye_offset = {x = 0, y = 3, z = 0}
		end

		-- if driver present allow control of horse
		if self.driver then

			mobs.drive(self, "walk", "stand", false, dtime)

			return false -- skip rest of mob functions
		end

		return true
	end,

	on_die = function(self, pos)

		-- drop saddle when horse is killed while riding
		-- also detach from horse properly
		if self.driver then

			minetest.add_item(pos, "mobs:saddle")

			mobs.detach(self.driver, {x = 1, y = 0, z = 1})

			self.saddle = nil
		end

		-- drop any horseshoes added
		if self.shoed then
			minetest.add_item(pos, self.shoed)
		end

	end,

	on_rightclick = function(self, clicker)

		-- make sure player is clicking
		if not clicker or not clicker:is_player() then
			return
		end

		-- feed, tame or heal horse
		if mobs:feed_tame(self, clicker, 10, true, true) then
			return
		end

		-- applying protection rune
		if mobs:protect(self, clicker) then
			return
		end

		-- make sure tamed horse is being clicked by owner only
		if self.tamed and self.owner == clicker:get_player_name() then

			local inv = clicker:get_inventory()
			local tool = clicker:get_wielded_item()
			local item = tool:get_name()

			-- detatch player already riding horse
			if self.driver and clicker == self.driver then

				mobs.detach(clicker, {x = 1, y = 0, z = 1})

				-- add saddle back to inventory
				if inv:room_for_item("main", "mobs:saddle") then
					inv:add_item("main", "mobs:saddle")
				else
					minetest.add_item(clicker:get_pos(), "mobs:saddle")
				end

				self.saddle = nil

			-- attach player to horse
			elseif (not self.driver and not self.child
			and clicker:get_wielded_item():get_name() == "mobs:saddle")
			or self.saddle then

				self.object:set_properties({stepheight = 1.1})
				mobs.attach(self, clicker)

				-- take saddle from inventory
				if not self.saddle then
					inv:remove_item("main", "mobs:saddle")
				end

				self.saddle = true
			end

			-- apply horseshoes
			if item:find("mobs:horseshoe") then

				-- drop any existing shoes
				if self.shoed then
					minetest.add_item(self.object:get_pos(), self.shoed)
				end

				local speed = shoes[item][1]
				local jump = shoes[item][2]
				local reverse = shoes[item][3]
				local overlay = shoes[item][4]

				self.max_speed_forward = speed
				self.jump_height = jump
				self.max_speed_reverse = reverse
				self.accel = speed
				self.shoed = item

				-- apply horseshoe overlay to current horse texture
				if overlay then
					self.texture_mods = "^" .. overlay
					self.object:set_texture_mod(self.texture_mods)
				end

				-- show horse speed and jump stats with shoes fitted
				minetest.chat_send_player(clicker:get_player_name(),
						S("Horse shoes fitted -")
						.. S(" speed: ") .. speed
						.. S(" , jump height: ") .. jump
						.. S(" , stop speed: ") .. reverse)

				tool:take_item() ; clicker:set_wielded_item(tool)

				return
			end
		end

		-- used to capture horse with magic lasso
		mobs:capture_mob(self, clicker, 0, 0, 80, false, nil)
	end,
})

mobs:spawn({
	name = "mob_horse:horse",
	nodes = {"default:dirt_with_grass", "ethereal:dry_dirt"},
	min_light = 14,
	interval = 60,
	chance = 16000,
	min_height = 10,
	max_height = 31000,
	day_toggle = true,
})

mobs:register_egg("mob_horse:horse", S("Horse"), "wool_brown.png", 1)

-- steel horseshoes
minetest.register_craftitem(":mobs:horseshoe_steel", {
	description = S("Steel HorseShoes (use on horse to apply)"),
	inventory_image = "mobs_horseshoe_steel.png",
})

minetest.register_craft({
	output = "mobs:horseshoe_steel",
	recipe = {
		{"", "default:steelblock", ""},
		{"default:steel_ingot", "", "default:steel_ingot"},
		{"default:steel_ingot", "", "default:steel_ingot"},
	}
})

-- bronze horseshoes
minetest.register_craftitem(":mobs:horseshoe_bronze", {
	description = S("Bronze HorseShoes (use on horse to apply)"),
	inventory_image = "mobs_horseshoe_bronze.png",
})

minetest.register_craft({
	output = "mobs:horseshoe_bronze",
	recipe = {
		{"", "default:bronzeblock", ""},
		{"default:bronze_ingot", "", "default:bronze_ingot"},
		{"default:bronze_ingot", "", "default:bronze_ingot"},
	}
})

-- mese horseshoes
minetest.register_craftitem(":mobs:horseshoe_mese", {
	description = S("Mese HorseShoes (use on horse to apply)"),
	inventory_image = "mobs_horseshoe_mese.png",
})

minetest.register_craft({
	output = "mobs:horseshoe_mese",
	recipe = {
		{"", "default:mese", ""},
		{"default:mese_crystal_fragment", "", "default:mese_crystal_fragment"},
		{"default:mese_crystal_fragment", "", "default:mese_crystal_fragment"},
	}
})

-- diamond horseshoes
minetest.register_craftitem(":mobs:horseshoe_diamond", {
	description = S("Diamond HorseShoes (use on horse to apply)"),
	inventory_image = "mobs_horseshoe_diamond.png",
})

minetest.register_craft({
	output = "mobs:horseshoe_diamond",
	recipe = {
		{"", "default:diamondblock", ""},
		{"default:diamond", "", "default:diamond"},
		{"default:diamond", "", "default:diamond"},
	}
})

-- lucky blocks
if minetest.get_modpath("lucky_block") then

lucky_block:add_blocks({
	{"dro", {"mobs:horseshoe_steel"}},
	{"dro", {"mobs:horseshoe_bronze"}},
	{"dro", {"mobs:horseshoe_mese"}},
	{"dro", {"mobs:horseshoe_diamond"}},
})

end


code:
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.

-- Get the latest version of this file at:
--   https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua

local function format(str, ...)
	local args = { ... }
	local function repl(escape, open, num, close)
		if escape == "" then
			local replacement = tostring(args[tonumber(num)])
			if open == "" then
				replacement = replacement..close
			end
			return replacement
		else
			return "@"..open..num..close
		end
	end
	return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end

local gettext, ngettext
if minetest.get_modpath("intllib") then
	if intllib.make_gettext_pair then
		-- New method using gettext.
		gettext, ngettext = intllib.make_gettext_pair()
	else
		-- Old method using text files.
		gettext = intllib.Getter()
	end
end

-- Fill in missing functions.

gettext = gettext or function(msgid, ...)
	return format(msgid, ...)
end

ngettext = ngettext or function(msgid, msgid_plural, n, ...)
	return format(n==1 and msgid or msgid_plural, ...)
end

return gettext, ngettext


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-06 00:07+0800\n"
"PO-Revision-Date: 2020-05-11 13:43+0200\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.2.1\n"
"Last-Translator: Hamlet <hamlatgitlab@riseup.net>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: it_IT\n"

#: init.lua
msgid "Horse"
msgstr "Cavallo"

#: init.lua
msgid "Horse shoes fitted -"
msgstr "Ferri di cavallo indossati -"

#: init.lua
msgid " speed: "
msgstr " velocità: "

#: init.lua
msgid " , jump height: "
msgstr " , altezza di salto: "

#: init.lua
msgid " , stop speed: "
msgstr " , velocità di arresto: "

#: init.lua
msgid "Horse shoes only work on horses!"
msgstr "I ferri di cavallo funzionano solo sui cavalli!"

#: init.lua
msgid "Steel HorseShoes (use on horse to apply)"
msgstr "Ferri di cavallo d'acciaio (usarli su un cavallo per applicarli)"

#: init.lua
msgid "Bronze HorseShoes (use on horse to apply)"
msgstr "Ferri di cavallo di bronzo (usarli su un cavallo per applicarli)"

#: init.lua
msgid "Mese HorseShoes (use on horse to apply)"
msgstr "Ferri di cavallo di mese (usarli su un cavallo per applicarli)"

#: init.lua
msgid "Diamond HorseShoes (use on horse to apply)"
msgstr "Ferri di cavallo di diamante (usarli su un cavallo per applicarli)"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-06 00:07+0800\n"
"PO-Revision-Date: 2018-02-06 00:14+0800\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.6\n"
"Last-Translator: MuhdNurHidayat (MNH48) <mnh48mail@gmail.com>\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Language: ms\n"

#: init.lua
msgid "Horse"
msgstr "Kuda"

#: init.lua
msgid "Horse shoes fitted -"
msgstr "Ladam telah dipasang pada kuda -"

#: init.lua
msgid " speed: "
msgstr " kelajuan: "

#: init.lua
msgid " , jump height: "
msgstr " , ketinggian lompat: "

#: init.lua
msgid " , stop speed: "
msgstr " , kelajuan berhenti: "

#: init.lua
msgid "Horse shoes only work on horses!"
msgstr "Ladam hanya boleh dipakaikan pada kuda!"

#: init.lua
msgid "Steel HorseShoes (use on horse to apply)"
msgstr "Ladam Kuda Keluli (guna pada kuda untuk pakaikan ia)"

#: init.lua
msgid "Bronze HorseShoes (use on horse to apply)"
msgstr "Ladam Kuda Gangsa (guna pada kuda untuk pakaikan ia)"

#: init.lua
msgid "Mese HorseShoes (use on horse to apply)"
msgstr "Ladam Kuda Mese (guna pada kuda untuk pakaikan ia)"

#: init.lua
msgid "Diamond HorseShoes (use on horse to apply)"
msgstr "Ladam Kuda Intan (guna pada kuda untuk pakaikan ia)"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-06 00:07+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: init.lua
msgid "Horse"
msgstr ""

#: init.lua
msgid "Horse shoes fitted -"
msgstr ""

#: init.lua
msgid " speed: "
msgstr ""

#: init.lua
msgid " , jump height: "
msgstr ""

#: init.lua
msgid " , stop speed: "
msgstr ""

#: init.lua
msgid "Horse shoes only work on horses!"
msgstr ""

#: init.lua
msgid "Steel HorseShoes (use on horse to apply)"
msgstr ""

#: init.lua
msgid "Bronze HorseShoes (use on horse to apply)"
msgstr ""

#: init.lua
msgid "Mese HorseShoes (use on horse to apply)"
msgstr ""

#: init.lua
msgid "Diamond HorseShoes (use on horse to apply)"
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-06 00:07+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: init.lua
msgid "Horse"
msgstr "马"

#: init.lua
msgid "Horse shoes fitted -"
msgstr "马蹄铁 -"

#: init.lua
msgid " speed: "
msgstr "速度："

#: init.lua
msgid " , jump height: "
msgstr "，跳跃高度"

#: init.lua
msgid " , stop speed: "
msgstr "，停止速度："

#: init.lua
msgid "Horse shoes only work on horses!"
msgstr "马蹄铁只对马有效！"

#: init.lua
msgid "Steel HorseShoes (use on horse to apply)"
msgstr "钢马蹄铁（用在马匹上）"

#: init.lua
msgid "Bronze HorseShoes (use on horse to apply)"
msgstr "青铜马蹄铁（用在马匹上）"

#: init.lua
msgid "Mese HorseShoes (use on horse to apply)"
msgstr "黄石马蹄铁（用在马匹上）"

#: init.lua
msgid "Diamond HorseShoes (use on horse to apply)"
msgstr "钻石马蹄铁（用在马匹上）"


code:
#Mod: mob_horse

Horse = 马
Horse shoes fitted - = 马蹄铁 -
 speed:  = 速度：
 , jump height:  = ，跳跃高度：
 , stop speed:  = ，停止速度：
Horse shoes only work on horses! = 马蹄铁只对马有效！
Steel HorseShoes (use on horse to apply) = 钢马蹄铁（用在马匹上）
Bronze HorseShoes (use on horse to apply) = 青铜马蹄铁（用在马匹上）
Mese HorseShoes (use on horse to apply) = 黄石马蹄铁（用在马匹上）
Diamond HorseShoes (use on horse to apply) = 钻石马蹄铁（用在马匹上）


code:
#Mod: mob_horse

Horse = 馬
Horse shoes fitted - = 馬蹄鐵 -
 speed:  = 速度：
 , jump height:  = ，跳躍高度：
 , stop speed:  = ，停止速度：
Horse shoes only work on horses! = 馬蹄鐵只對馬有效！
Steel HorseShoes (use on horse to apply) = 鋼馬蹄鐵（用在馬匹上）
Bronze HorseShoes (use on horse to apply) = 青銅馬蹄鐵（用在馬匹上）
Mese HorseShoes (use on horse to apply) = 黃石馬蹄鐵（用在馬匹上）
Diamond HorseShoes (use on horse to apply) = 鑽石馬蹄鐵（用在馬匹上）


code:
author = TenPlus1
description = Adds rideable horse
release = 3785
title = Mob Horse
name = mob_horse


code:
# MOB Horse

### Spawning
There are three different horse textures (white, brown, black) which will spawn on green grassy areas and dry dirt areas in Ethereal mapgen.

---
### Taming
Horses can be tamed with 10x wheat, apple, barley, oats of corn which then allows the player to pick up the horse using a lasso and ride by right-clicking with a saddle.

---
### Horseshoes
Horseshoes can be crafted using steel, bronze, mese and diamond (4x ingots - 2 down either side with 1x block top middle) and placed on a horse by right clicking with the item.  These can make horses run faster or jump higher while riding depending on tier.

---
### Dead Horse
When riding a horse monsters will generally attack the horse first to get to player riding it, when horse dies the player is dismounted and it will drop any shoes or saddles in use as well as some horse meat.

#### Lucky Blocks: 4