[Server v5.8.0] German-MineClone2-server ( german )

German
Post Reply
User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

ACHTUNG: jjk1 hat als Mod nun auch das recht jemanden zu bannen !

geht ihr also bitte nicht auf den Sack !

/me zwinkert Bau1 und Lebenshelfer zu

WICHTIG: sollte ich Beleidigungen, Parolen oder ähnliches von jemandem finden WIRD DESSEN NAME UND IP DAUERHAFT GEBANNT und kann sich auf anderen Servern von mir zu einem besseren bekehren lassen ...

sollte man iwan auf allen Server ( von mir ) life-time gebannt sein ... pech !

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

oh. interessant. Aber bevor ich das neue Recht benutze, übe ich lieber noch im singleplayer ;)

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

Walker wrote: geht ihr also bitte nicht auf den Sack !
Darf ich dich mal wieder korrigieren?: IHM ;)

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-09_04:01:07.tgz

Größe: 2.270.399.032 Bytes
Datum: 09.09.2019
Zeit: 04:01:07

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-13_06:03:36.tgz

Größe: 2.274.563.635 Bytes
Datum: 13.09.2019
Zeit: 06:03:36

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Der Server wurde auf die MineClone2 Version 0.54-2913acc upgedatet

Weitere Infos ( auf Englisch ):

Changelog 0.53 > 0.53.1: Verloren gegangen, aufgrund des Forumcrashs vor einiger Zeit
Changelog 0.53.1 > 0.53.2: Verloren gegangen, aufgrund des Forumcrashs vor einiger Zeit
Changelog 0.53.2 > 0.53.3: viewtopic.php?f=50&t=16407&start=1400#p348317
Changelog 0.53.3 > 0.53.4: viewtopic.php?f=50&t=16407&start=1500#p355524
Changelog 0.53.4 > 0.54: viewtopic.php?f=50&t=16407&start=1500#p356793

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

WICHTIG:
jjk1 ist nun auf ALLEN German-*-servern von mir Moderator !
behandelt sie ... ... ... IHN also mit respekt ;)

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

Da hat mal wieder jemand den creativ server vermüllt. Was sagt der log zu dem Block -11 16 -148?

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

nichts ?

ansonsten gehört der Bereich Exo und Rox, da dürfte also keiner, außer die beide, bauen können

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

jjk1 wrote:Da hat mal wieder jemand den creativ server vermüllt. Was sagt der log zu dem Block -11 16 -148?
Ich meinte den Kreativ Server ;)

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

-.- LOOOOOL

^^

ich habe erst im Creativ-Log nachgesehen, dann die Antwort hier geostet, DANN gemerkt "moment, ich bin doch im MineClone-Thread, warum lese ich den Creativ-Log !?" dann die antwort geändert, und jetzt dank dir, gemerkt, dass die erste Antowrt doch richtig war xDD

ALSO hier die erste und richtige Antwort ^^:

Code: Select all

2019-09-11 17:22:25: ACTION[Server]: zolder places node default:torch_ceiling at (-11,16,-148)
2019-09-13 19:20:37: ACTION[Server]: jjk1 punches object 231: LuaEntitySAO at (-11,16,-148)
2019-09-13 19:20:37: ACTION[Server]: LuaEntitySAO at (-11,16,-148) punched by player jjk1, damage 10 hp, health now 0 hp
2019-09-13 19:20:37: ACTION[Server]: jjk1 digs default:torch_ceiling at (-11,16,-148)
zolder ^^

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-16_04:01:31.tgz

Größe: 2.307.856.360 Bytes
Datum: 16.09.2019
Zeit: 04:01:31

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-20_06:06:43.tgz

Größe: 2.262.190.405 Bytes
Datum: 20.09.2019
Zeit: 06:06:43

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-23_04:01:22.tgz

Größe: 2.393.563.252 Bytes
Datum: 23.09.2019
Zeit: 04:01:22

cHyper-0815OL

Re: [Server] [v5.0.0] German-MineClone2-server

by cHyper-0815OL » Post

Gut, dass du regelmaessig Updates machst, da kann nix schiefgehen.

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

^^

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-27_06:00:18.tgz

Größe: 2.461.961.433 Bytes
Datum: 27.09.2019
Zeit: 06:00:18

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

Wuzzy wrote:Version 0.54.1 released!

Changelog:
  • Fix crash when fishing rod breaks
Du solltest drigend updaten.....

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

ok, ist eingespielt ^^

hoffentlich ist es nicht wieder ein ... HOT-FIX xDDDDD

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Code: Select all

2019-09-27 21:11:55: ACTION[Server]: anonym digs mcl_core:stone at (234,31,356)
2019-09-27 21:11:56: ACTION[Server]: anonym digs mcl_core:stone at (233,32,354)
2019-09-27 21:11:57: ACTION[Server]: anonym digs mcl_core:stone at (234,32,355)
2019-09-27 21:12:08: ACTION[Server]: anonym digs mcl_core:stone at (234,32,353)
2019-09-27 21:12:19: ACTION[Server]: anonym digs mcl_core:stone at (233,32,352)
2019-09-27 21:12:43: ACTION[Server]: anonym digs mcl_core:stone at (234,32,351)
2019-09-27 21:12:43: ACTION[Server]: anonym digs mcl_core:stone at (233,32,350)
2019-09-27 21:12:43: ACTION[Server]: anonym digs mcl_core:stone at (234,31,349)
2019-09-27 21:12:50: ACTION[Server]: anonym dies at (237,29,356). Inventory dropped
2019-09-27 21:13:07: ACTION[Server]: Server: NoCheat: anonym tried to interact while dead; ignoring.
2019-09-27 21:13:11: ACTION[Server]: Server: NoCheat: anonym tried to interact while dead; ignoring.
2019-09-27 21:13:11: ACTION[Server]: Server: NoCheat: anonym tried to interact while dead; ignoring.
2019-09-27 21:13:34: ACTION[Server]: Server: NoCheat: anonym tried to interact while dead; ignoring.
2019-09-27 21:13:34: ACTION[Server]: Server: NoCheat: anonym tried to interact while dead; ignoring.
[...]
2019-09-27 21:13:41: ACTION[Server]: Server: NoCheat: anonym tried to interact while dead; ignoring.
2019-09-27 21:13:56: ACTION[Server]: [doc] Wrote player data into /media/sda3/Minetest/server4/.minetest/worlds/MineClone2/doc.mt.
2019-09-27 21:13:56: ACTION[Server]: anonym leaves game. List of players: 
2019-09-27 21:26:02: ACTION[Server]: anonym [84.188.153.142] joins game. 
2019-09-27 21:26:02: ACTION[Server]: anonym joins game. List of players: anonym
2019-09-27 21:26:02: ACTION[Server]: [mcl_skins] Player skin for anonym set to skin #0
2019-09-27 21:26:05: ACTION[Server]: Moving anonym to static spawnpoint at (0,25,0)
2019-09-27 21:26:05: ACTION[Server]: anonym respawns at (0,25,0)
2019-09-27 21:26:39: ACTION[Server]: anonym takes stuff from chest at (224,27,345)
2019-09-27 21:26:44: ACTION[Server]: 'anonym' submitted formspec ('mcl_chests:chest_223_27_345') but server hasn't sent formspec to client, possible exploitation attempt
[...]
2019-09-27 21:27:27: ACTION[Server]: CHAT: <anonym> MANNNNNNNNNNNNNNNNNNNNNN
2019-09-27 21:27:44: ACTION[Server]: CHAT: <anonym> alle sache weggebuggt
2019-09-27 21:27:48: ACTION[Server]: CHAT: <anonym> mist
2019-09-27 21:27:55: ACTION[Server]: CHAT: <anonym> bin getsorben alles weg
2019-09-27 21:27:57: ACTION[Server]: CHAT: <anonym> hä
Todesursache: unbekannt !??!
was´n da los ?

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-09-30_05:49:43.tgz

Größe: 2.493.521.433 Bytes
Datum: 30.09.2019
Zeit: 05:49:43

User avatar
Walker
Member
Posts: 1811
Joined: Tue Oct 03, 2017 09:22
In-game: Walker
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by Walker » Post

Neues Backup wurde erfolgreich angelegt

File: Minetest_2019-10-04_04:04:12.tgz

Größe: 2.504.964.663 Bytes
Datum: 04.10.2019
Zeit: 04:04:12

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

Image
Hallo,
Ich und Testperson sind gerade dabei eine mittelalterliche Stadt zu bauen, die dann später auch mit Dorfbewohnern belebt werden soll.(Keine Sorge, sie ist extra weit weg vom Spawn, damit es nicht so lagt). Am Rand gibt es eine 3 Blöcke Dicke Wand, damit die Villager nicht ausbrechen können. Sie handeln aber bisher viel zu OP. für etwas Weizen bekommt man schon 1 Smaragd, was ja viel mehr ist, als ein Dia. Deshalb habe ich mir überlegt, den Villager Code etwas abzuändern. Jetzt handeln sie mit coins und auch zu viel faireren Preisen.

Code: Select all

--MCmobs v0.4
--maikerumine
--made for MC like Survival game
--License for code WTFPL and otherwise stated in readmes

--###################
--################### VILLAGER
--###################
-- Summary: Villagers are complex NPCs, their main feature allows players to trade with them.

-- TODO: Particles
-- TODO: 4s Regeneration I after trade unlock
-- TODO: Breeding
-- TODO: Baby villagers
-- TODO: Spawning in villages
-- TODO: Behaviour:
--     TODO: Walk around village, but do not leave it intentionally
--     TODO: Run into house on rain or danger, open doors
--     TODO: Internal inventory, pick up items, trade with other villagers
--     TODO: Farm stuff

local S = minetest.get_translator("mobs_mc")
local N = function(s) return s end

-- playername-indexed table containing the previously used tradenum
local player_tradenum = {}
-- playername-indexed table containing the objectref of trader, if trading formspec is open
local player_trading_with = {}

local DEFAULT_WALK_CHANCE = 33 -- chance to walk in percent, if no player nearby
local PLAYER_SCAN_INTERVAL = 5 -- every X seconds, villager looks for players nearby
local PLAYER_SCAN_RADIUS = 4 -- scan radius for looking for nearby players

--[=======[ TRADING ]=======]

-- LIST OF VILLAGER PROFESSIONS AND TRADES

-- TECHNICAL RESTRICTIONS (FIXME):
-- * You can't use a clock as requested item
-- * You can't use a compass as requested item if its stack size > 1
-- * You can't use a compass in the second requested slot
-- This is a problem in the mcl_compass and mcl_clock mods,
-- these items should be implemented as single items, then everything
-- will be much easier.

local COMPASS = "mcl_compass:compass"
if minetest.registered_aliases[COMPASS] then
	COMPASS = minetest.registered_aliases[COMPASS]
end


-- Special trades for v6 only
-- NOTE: These symbols MUST only be added at the end of a tier
local TRADE_V6_RED_SANDSTONE, TRADE_V6_DARK_OAK_SAPLING, TRADE_V6_ACACIA_SAPLING, TRADE_V6_BIRCH_SAPLING
if minetest.get_mapgen_setting("mg_name") == "v6" then
	TRADE_V6_RED_SANDSTONE = { {mcl_shops:coin, 1, 1}, { "mcl_core:redsandstone", 12, 16 } }
	TRADE_V6_DARK_OAK_SAPLING = { { "mcl_shops:coin", 6, 9 }, { "mcl_core:darksapling", 1, 1 } }
	TRADE_V6_ACACIA_SAPLING = { { "mcl_shops:coin", 14, 17 }, { "mcl_core:acaciasapling", 1, 1 } }
	TRADE_V6_BIRCH_SAPLING = { { "mcl_shops:coin", 8, 11 }, { "mcl_core:birchsapling", 1, 1 } }
end

local professions = {
	farmer = {
		name = N("Farmer"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_farming:wheat_item", 18, 22, }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_farming:potato_item", 15, 19, }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_farming:carrot_item", 15, 19, }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_farming:bread", 2, 4 } },
			},

			{
			{ { "mcl_farming:pumpkin_face", 8, 12 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_farming:pumpkin_pie", 2, 3} },
			},

			{
			{ { "mcl_farming:melon_item", 7, 12 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_core:apple", 5, 7 }, },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mcl_farming:cookie", 6, 10 } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_cake:cake", 1, 1 } },
			TRADE_V6_BIRCH_SAPLING,
			TRADE_V6_DARK_OAK_SAPLING,
			TRADE_V6_ACACIA_SAPLING,
			},
		}
	},
	fisherman = {
		name = N("Fisherman"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_fishing:fish_raw", 6, 6, "mcl_shops:coin", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } },
			{ { "mcl_mobitems:string", 5, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_core:coal_lump", 5, 7 }, {mcl_shops:coin, 1, 1} },
			},
			-- TODO: enchanted fishing rod
		},
	},
	fletcher = {
		name = N("Fletcher"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_mobitems:string", 5, 7 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_bows:arrow", 8, 12 } },
			},

			{
			{ { "mcl_core:gravel", 10, 10, "mcl_shops:coin", 1, 1 }, { "mcl_core:flint", 6, 10 } },
			{ { "mcl_shops:coin", 2, 3 }, { "mcl_bows:bow", 1, 1 } },
			},
		}
	},
	shepherd ={
		name = N("Shepherd"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_wool:white", 3, 5 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_tools:shears", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:white", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:grey", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:silver", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:black", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:yellow", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:orange", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:red", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:magenta", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:purple", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:blue", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:cyan", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:lime", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:green", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:pink", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:light_blue", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:brown", 3, 5 } },
			},
		},
	},
	librarian = {
		name = N("Librarian"),
		texture = "mobs_mc_villager_librarian.png",
		trades = {
			{
			{ { "mcl_core:paper", 24, 36 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchanted book
			{ { "mcl_books:book", 8, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 4, 5 }, { "mcl_compass:compass", 1 ,1 }},
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_books:bookshelf", 1 ,1 }},
			},

			{
			{ { "mcl_books:written_book", 2, 2 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 9, 11 }, { "mcl_clock:clock", 1, 1 } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_core:glass", 5, 7 } },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mcl_core:glass", 5, 7 } },
			},

			-- TODO: 2 enchanted book tiers

			{
			{ { "mcl_shops:coin", 20, 22 }, { "mcl_mobs:nametag", 1, 1 } },
			}
		},
	},
	cartographer = {
		name = N("Cartographer"),
		texture = "mobs_mc_villager_librarian.png",
		trades = {
			{
			{ { "mcl_core:paper", 20, 30 }, {mcl_shops:coin, 1, 1} },
			},

			{
			-- subject to special checks
			{ { "mcl_compass:compass", 1, 1 }, {"mcl_shops:coin", 4, 5} },
			},

			{
			-- TODO: replace with empty map
			{ { "mcl_shops:coin", 5, 10}, { "mcl_maps:filled_map", 1, 1 } },
			},

			-- TODO: special maps
		},
	},
	armorer = {
		name = N("Armorer"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 4, 6 }, { "3d_armor:helmet_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:iron_ingot", 1, 1 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 7, 9 }, { "3d_armor:chestplate_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 26, 28 }, { "3d_armor:chestplate_gold", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 2 }, { "3d_armor:boots_chain", 1, 1 } },
			{ { "mcl_shops:coin", 2, 3 }, { "3d_armor:leggings_chain", 1, 1 } },
			{ { "mcl_shops:coin", 1, 2 }, { "3d_armor:helmet_chain", 1, 1 } },
			{ { "mcl_shops:coin", 3, 4 }, { "3d_armor:chestplate_chain", 1, 1 } },
			},
		},
	},
	leatherworker = {
		name = N("Leatherworker"),
		texture = "mobs_mc_villager_butcher.png",
		trades = {
			{
			{ { "mcl_mobitems:leather", 5, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 3, 5 }, { "3d_armor:leggings_leather", 2, 4 } },
			},

			{
			-- TODO: enchant
			{ { "mcl_shops:coin", 4, 7 }, { "3d_armor:chestplate_leather", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 15, 20 }, { "mcl_mobitems:saddle", 1, 1 } },
			},
		},
	},
	butcher = {
		name = N("Butcher"),
		texture = "mobs_mc_villager_butcher.png",
		trades = {
			{
			{ { "mcl_mobitems:beef", 5, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_mobitems:chicken", 5, 10 }, {mcl_shops:coin, 1, 1} },
			},

			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_mobitems:cooked_beef", 5, 10 } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_mobitems:cooked_chicken", 5, 10 } },
			},
		},
	},
	weapon_smith = {
		name = N("Weapon Smith"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_tools:axe_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:iron_ingot", 1, 1 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 2, 3 }, { "mcl_tools:sword_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 17, 19 }, { "mcl_tools:sword_diamond", 1, 1 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 27, 28 }, { "mcl_tools:axe_diamond", 1, 1 } },
			},
		},
	},
	tool_smith = {
		name = N("Tool Smith"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 1, 1 }, { "mcl_tools:shovel_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:iron_ingot", 1, 1 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 2, 4 }, { "mcl_tools:pick_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 26, 28 }, { "mcl_tools:pick_diamond", 1, 1 } },
			},
		},
	},
	cleric = {
		name = N("Cleric"),
		texture = "mobs_mc_villager_priest.png",
		trades = {
			{
			{ { "mcl_mobitems:rotten_flesh", 15, 25 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_core:gold_ingot", 2, 2 }, {"mcl_shops:coin", 6, 6 } },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mesecons:redstone", 1, 4  } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_dye:blue", 1, 2 } },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mcl_nether:glowstone", 1, 3 } },
			{ { "mcl_shops:coin", 4, 7 }, { "mcl_throwing:ender_pearl", 1, 1 } },
			TRADE_V6_RED_SANDSTONE,
			},

			-- TODO: Bottle 'o enchanting
		},
	},
	nitwit = {
		name = N("Nitwit"),
		texture = "mobs_mc_villager.png",
		-- No trades for nitwit
		trades = nil,
	}
}

local profession_names = {}
for id, _ in pairs(professions) do
	table.insert(profession_names, id)
end

local stand_still = function(self)
	self.walk_chance = 0
	self.jump = false
end

local update_max_tradenum = function(self)
	if not self._trades then
		return
	end
	local trades = minetest.deserialize(self._trades)
	for t=1, #trades do
		local trade = trades[t]
		if trade.tier > self._max_trade_tier then
			self._max_tradenum = t - 1
			return
		end
	end
	self._max_tradenum = #trades
end

local init_trader_vars = function(self)
	if not self._profession then
		-- Select random profession from all professions with matching clothing
		local texture = self.base_texture[1]
		local matches = {}
		for prof_id, prof in pairs(professions) do
			if texture == prof.texture then
				table.insert(matches, prof_id)
			end
		end
		local p = math.random(1, #matches)
		self._profession = matches[p]
	end
	if not self._max_trade_tier then
		self._max_trade_tier = 1
	end
	if not self._locked_trades then
		self._locked_trades = 0
	end
	if not self._trading_players then
		self._trading_players = {}
	end
end

local init_trades = function(self, inv)
	local profession = professions[self._profession]
	local trade_tiers = profession.trades
	if trade_tiers == nil then
		-- Empty trades
		self._trades = false
		return
	end

	local max_tier = #trade_tiers
	local trades = {}
	for tiernum=1, max_tier do
		local tier = trade_tiers[tiernum]
		for tradenum=1, #tier do
			local trade = tier[tradenum]
			local wanted1_item = trade[1][1]
			local wanted1_count = math.random(trade[1][2], trade[1][3])
			local offered_item = trade[2][1]
			local offered_count = math.random(trade[2][2], trade[2][3])

			local wanted = { wanted1_item .. " " ..wanted1_count }
			if trade[1][4] then
				local wanted2_item = trade[1][4]
				local wanted2_count = math.random(trade[1][5], trade[1][6])
				table.insert(wanted, wanted2_item .. " " ..wanted2_count)
			end

			table.insert(trades, {
				wanted = wanted,
				offered = offered_item .. " " .. offered_count,
				tier = tiernum, -- tier of this trade
				traded_once = false, -- true if trade was traded at least once
				trade_counter = 0, -- how often the this trade was mate after the last time it got unlocked
				locked = false, -- if this trade is locked. Locked trades can't be used
			})
		end
	end
	self._trades = minetest.serialize(trades)
end

local set_trade = function(trader, player, inv, concrete_tradenum)
	local trades = minetest.deserialize(trader._trades)
	if not trades then
		init_trades(trader)
		trades = minetest.deserialize(trader._trades)
		if not trades then
			minetest.log("error", "[mobs_mc] Failed to select villager trade!")
			return
		end
	end
	local name = player:get_player_name()

	-- Stop tradenum from advancing into locked tiers or out-of-range areas
	if concrete_tradenum > trader._max_tradenum then
		concrete_tradenum = trader._max_tradenum
	elseif concrete_tradenum < 1 then
		concrete_tradenum = 1
	end
	player_tradenum[name] = concrete_tradenum
	local trade = trades[concrete_tradenum]
	inv:set_stack("wanted", 1, ItemStack(trade.wanted[1]))
	inv:set_stack("offered", 1, ItemStack(trade.offered))
	if trade.wanted[2] then
		local wanted2 = ItemStack(trade.wanted[2])
		inv:set_stack("wanted", 2, wanted2)
	else
		inv:set_stack("wanted", 2, "")
	end

end

local function show_trade_formspec(playername, trader, tradenum)
	if not trader._trades then
		return
	end
	if not tradenum then
		tradenum = 1
	end
	local trades = minetest.deserialize(trader._trades)
	local trade = trades[tradenum]
	local profession = professions[trader._profession].name
	local disabled_img = ""
	if trade.locked then
		disabled_img = "image[4.3,2.52;1,1;mobs_mc_trading_formspec_disabled.png]"..
			"image[4.3,1.1;1,1;mobs_mc_trading_formspec_disabled.png]"
	end
	local tradeinv_name = "mobs_mc:trade_"..playername
	local tradeinv = minetest.formspec_escape("detached:"..tradeinv_name)

	local b_prev, b_next = "", ""
	if #trades > 1 then
		if tradenum > 1 then
			b_prev = "button[1,1;0.5,1;prev_trade;<]"
		end
		if tradenum < trader._max_tradenum then
			b_next = "button[7.26,1;0.5,1;next_trade;>]"
		end
	end

	local formspec =
	"size[9,8.75]"
	.."background[-0.19,-0.25;9.41,9.49;mobs_mc_trading_formspec_bg.png]"
	..disabled_img
	..mcl_vars.inventory_header
	.."label[4,0;"..minetest.formspec_escape(minetest.colorize("#313131", S(profession))).."]"
	.."list[current_player;main;0,4.5;9,3;9]"
	.."list[current_player;main;0,7.74;9,1;]"
	..b_prev..b_next
	.."list["..tradeinv..";wanted;2,1;2,1;]"
	.."list["..tradeinv..";offered;5.76,1;1,1;]"
	.."list["..tradeinv..";input;2,2.5;2,1;]"
	.."list["..tradeinv..";output;5.76,2.55;1,1;]"
	.."listring["..tradeinv..";output]"
	.."listring[current_player;main]"
	.."listring["..tradeinv..";input]"
	.."listring[current_player;main]"
	minetest.sound_play("mobs_mc_villager_trade", {to_player = playername})
	minetest.show_formspec(playername, tradeinv_name, formspec)
end

local update_offer = function(inv, player, sound)
	local name = player:get_player_name()
	local trader = player_trading_with[name]
	local tradenum = player_tradenum[name]
	if not trader or not tradenum then
		return false
	end
	local trades = minetest.deserialize(trader._trades)
	if not trades then
		return false
	end
	local trade = trades[tradenum]
	if not trade then
		return false
	end
	local wanted1, wanted2 = inv:get_stack("wanted", 1), inv:get_stack("wanted", 2)
	local input1, input2 = inv:get_stack("input", 1), inv:get_stack("input", 2)

	-- BEGIN OF SPECIAL HANDLING OF COMPASS
	-- These 2 functions are a complicated check to check if the input contains a
	-- special item which we cannot check directly against their name, like
	-- compass.
	-- TODO: Remove these check functions when compass and clock are implemented
	-- as single items.
	local check_special = function(special_item, group, wanted1, wanted2, input1, input2)
		if minetest.registered_aliases[special_item] then
			special_item = minetest.registered_aliases[special_item]
		end
		if wanted1:get_name() == special_item then
			local check_input = function(input, wanted, group)
				return minetest.get_item_group(input:get_name(), group) ~= 0 and input:get_count() >= wanted:get_count()
			end
			if check_input(input1, wanted1, group) then
				return true
			elseif check_input(input2, wanted1, group) then
				return true
			else
				return false
			end
		end
		return false
	end
	-- Apply above function to all items which we consider special.
	-- This function succeeds if ANY item check succeeds.
	local check_specials = function(wanted1, wanted2, input1, input2)
		return check_special(COMPASS, "compass", wanted1, wanted2, input1, input2)
	end
	-- END OF SPECIAL HANDLING OF COMPASS

	if (
			((inv:contains_item("input", wanted1) and
			(wanted2:is_empty() or inv:contains_item("input", wanted2))) or
			-- BEGIN OF SPECIAL HANDLING OF COMPASS
			check_specials(wanted1, wanted2, input1, input2)) and
			-- END OF SPECIAL HANDLING OF COMPASS
			(trade.locked == false)) then
		inv:set_stack("output", 1, inv:get_stack("offered", 1))
		if sound then
			minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
		end
		return true
	else
		inv:set_stack("output", 1, ItemStack(""))
		if sound then
			minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
		end
		return false
	end
end

-- Returns a single itemstack in the given inventory to the player's main inventory, or drop it when there's no space left
local function return_item(itemstack, dropper, pos, inv_p)
	if dropper:is_player() then
		-- Return to main inventory
		if inv_p:room_for_item("main", itemstack) then
			inv_p:add_item("main", itemstack)
		else
			-- Drop item on the ground
			local v = dropper:get_look_dir()
			local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
			p.x = p.x+(math.random(1,3)*0.2)
			p.z = p.z+(math.random(1,3)*0.2)
			local obj = minetest.add_item(p, itemstack)
			if obj then
				v.x = v.x*4
				v.y = v.y*4 + 2
				v.z = v.z*4
				obj:set_velocity(v)
				obj:get_luaentity()._insta_collect = false
			end
		end
	else
		-- Fallback for unexpected cases
		minetest.add_item(pos, itemstack)
	end
	return itemstack
end

local return_fields = function(player)
	local name = player:get_player_name()
	local inv_t = minetest.get_inventory({type="detached", name = "mobs_mc:trade_"..name})
	local inv_p = player:get_inventory()
	for i=1, inv_t:get_size("input") do
		local stack = inv_t:get_stack("input", i)
		return_item(stack, player, player:get_pos(), inv_p)
		stack:clear()
		inv_t:set_stack("input", i, stack)
	end
	inv_t:set_stack("output", 1, "")
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if string.sub(formname, 1, 14) == "mobs_mc:trade_" then
		local name = player:get_player_name()
		if fields.quit then
			-- Get input items back
			return_fields(player)
			-- Reset internal "trading with" state
			local trader = player_trading_with[name]
			if trader then
				trader._trading_players[name] = nil
			end
			player_trading_with[name] = nil
		elseif fields.next_trade or fields.prev_trade then
			local trader = player_trading_with[name]
			if not trader or not trader.object:get_luaentity() then
				return
			end
			local trades = trader._trades
			if not trades then
				return
			end
			local dir = 1
			if fields.prev_trade then
				dir = -1
			end
			local tradenum = player_tradenum[name] + dir
			local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})
			set_trade(trader, player, inv, tradenum)
			update_offer(inv, player, false)
			show_trade_formspec(name, trader, player_tradenum[name])
		end
	end
end)

minetest.register_on_leaveplayer(function(player)
	local name = player:get_player_name()
	return_fields(player)
	player_tradenum[name] = nil
	local trader = player_trading_with[name]
	if trader then
		trader._trading_players[name] = nil
	end
	player_trading_with[name] = nil

end)

-- Return true if player is trading with villager, and the villager entity exists
local trader_exists = function(playername)
	local trader = player_trading_with[playername]
	return trader ~= nil and trader.object:get_luaentity() ~= nil
end

local trade_inventory = {
	allow_take = function(inv, listname, index, stack, player)
		if listname == "input" then
			return stack:get_count()
		elseif listname == "output" then
			if not trader_exists(player:get_player_name()) then
				return 0
			end
			-- Only allow taking full stack
			local count = stack:get_count()
			if count == inv:get_stack(listname, index):get_count() then
				-- Also update output stack again.
				-- If input has double the wanted items, the
				-- output will stay because there will be still
				-- enough items in input after the trade
				local wanted1 = inv:get_stack("wanted", 1)
				local wanted2 = inv:get_stack("wanted", 2)
				local input1 = inv:get_stack("input", 1)
				local input2 = inv:get_stack("input", 2)
				wanted1:set_count(wanted1:get_count()*2)
				wanted2:set_count(wanted2:get_count()*2)
				-- BEGIN OF SPECIAL HANDLING FOR COMPASS
				local special_checks = function(wanted1, input1, input2)
					if wanted1:get_name() == COMPASS then
						local compasses = 0
						if (minetest.get_item_group(input1:get_name(), "compass") ~= 0) then
							compasses = compasses + input1:get_count()
						end
						if (minetest.get_item_group(input2:get_name(), "compass") ~= 0) then
							compasses = compasses + input2:get_count()
						end
						return compasses >= wanted1:get_count()
					end
					return false
				end
				-- END OF SPECIAL HANDLING FOR COMPASS
				if (inv:contains_item("input", wanted1) and
					(wanted2:is_empty() or inv:contains_item("input", wanted2)))
					-- BEGIN OF SPECIAL HANDLING FOR COMPASS
					or special_checks(wanted1, input1, input2) then
					-- END OF SPECIAL HANDLING FOR COMPASS
					return -1
				else
					-- If less than double the wanted items,
					-- remove items from output (final trade,
					-- input runs empty)
					return count
				end
			else
				return 0
			end
		else
			return 0
		end
	end,
	allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
		if from_list == "input" and to_list == "input" then
			return count
		elseif from_list == "output" and to_list == "input" then
			if not trader_exists(player:get_player_name()) then
				return 0
			end
			local move_stack = inv:get_stack(from_list, from_index)
			if inv:get_stack(to_list, to_index):item_fits(move_stack) then
				return count
			end
		end
		return 0
	end,
	allow_put = function(inv, listname, index, stack, player)
		if listname == "input" then
			if not trader_exists(player:get_player_name()) then
				return 0
			else
				return stack:get_count()
			end
		else
			return 0
		end
	end,
	on_put = function(inv, listname, index, stack, player)
		update_offer(inv, player, true)
	end,
	on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
		if from_list == "output" and to_list == "input" then
			inv:remove_item("input", inv:get_stack("wanted", 1))
			local wanted2 = inv:get_stack("wanted", 2)
			if not wanted2:is_empty() then
				inv:remove_item("input", inv:get_stack("wanted", 2))
			end
			minetest.sound_play("mobs_mc_villager_accept", {to_player = player:get_player_name()})
		end
		update_offer(inv, player, true)
	end,
	on_take = function(inv, listname, index, stack, player)
		local accept
		local name = player:get_player_name()
		if listname == "output" then
			local wanted1 = inv:get_stack("wanted", 1)
			inv:remove_item("input", wanted1)
			local wanted2 = inv:get_stack("wanted", 2)
			if not wanted2:is_empty() then
				inv:remove_item("input", inv:get_stack("wanted", 2))
			end
			-- BEGIN OF SPECIAL HANDLING FOR COMPASS
			if wanted1:get_name() == COMPASS then
				for n=1, 2 do
					local input = inv:get_stack("input", n)
					if minetest.get_item_group(input:get_name(), "compass") ~= 0 then
						input:set_count(input:get_count() - wanted1:get_count())
						inv:set_stack("input", n, input)
						break
					end
				end
			end
			-- END OF SPECIAL HANDLING FOR COMPASS
			local trader = player_trading_with[name]
			local tradenum = player_tradenum[name]
			local trades
			if trader and trader._trades then
				trades = minetest.deserialize(trader._trades)
			end
			if trades then
				local trade = trades[tradenum]
				local unlock_stuff = false
				if not trade.traded_once then
					-- Unlock all the things if something was traded
					-- for the first time ever
					unlock_stuff = true
					trade.traded_once = true
				elseif trade.trade_counter == 0 and math.random(1,5) == 1 then
					-- Otherwise, 20% chance to unlock if used freshly reset trade
					unlock_stuff = true
				end
				local update_formspec = false
				if unlock_stuff then
					-- First-time trade unlock all trades and unlock next trade tier
					if trade.tier + 1 > trader._max_trade_tier then
						trader._max_trade_tier = trader._max_trade_tier + 1
						update_max_tradenum(trader)
						update_formspec = true
					end
					for t=1, #trades do
						trades[t].locked = false
						trades[t].trade_counter = 0
					end
					trader._locked_trades = 0
					-- Also heal trader for unlocking stuff
					-- TODO: Replace by Regeneration I
					trader.health = math.min(trader.hp_max, trader.health + 4)
				end
				trade.trade_counter = trade.trade_counter + 1
				-- Semi-randomly lock trade for repeated trade (not if there's only 1 trade)
				if trader._max_tradenum > 1 then
					if trade.trade_counter >= 12 then
						trade.locked = true
					elseif trade.trade_counter >= 2 then
						local r = math.random(1, math.random(4, 10))
						if r == 1 then
							trade.locked = true
						end
					end
				end

				if trade.locked then
					inv:set_stack("output", 1, "")
					update_formspec = true
					trader._locked_trades = trader._locked_trades + 1
					-- Check if we managed to lock ALL available trades. Rare but possible.
					if trader._locked_trades >= trader._max_tradenum then
						-- Emergency unlock! Unlock all other trades except the current one
						for t=1, #trades do
							if t ~= tradenum then
								trades[t].locked = false
								trades[t].trade_counter = 0
							end
						end
						trader._locked_trades = 1
						-- Also heal trader for unlocking stuff
						-- TODO: Replace by Regeneration I
						trader.health = math.min(trader.hp_max, trader.health + 4)
					end
				end
				trader._trades = minetest.serialize(trades)
				if update_formspec then
					show_trade_formspec(name, trader, tradenum)
				end
			else
				minetest.log("error", "[mobs_mc] Player took item from trader output but player_trading_with or player_tradenum is nil!")
			end

			accept = true
		elseif listname == "input" then
			update_offer(inv, player, false)
		end
		if accept then
			minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
		else
			minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
		end
	end,
}

minetest.register_on_joinplayer(function(player)
	local name = player:get_player_name()
	player_tradenum[name] = 1
	player_trading_with[name] = nil

	-- Create or get player-specific trading inventory
	local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})
	if not inv then
		inv = minetest.create_detached_inventory("mobs_mc:trade_"..name, trade_inventory, name)
	end
	inv:set_size("input", 2)
	inv:set_size("output", 1)
	inv:set_size("wanted", 2)
	inv:set_size("offered", 1)
end)

--[=======[ MOB REGISTRATION AND SPAWNING ]=======]

mobs:register_mob("mobs_mc:villager", {
	type = "npc",
	hp_min = 20,
	hp_max = 20,
	collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
	visual = "mesh",
	mesh = "mobs_mc_villager.b3d",
	textures = {
	{
		"mobs_mc_villager.png",
		"mobs_mc_villager.png", --hat
	},
	{
		"mobs_mc_villager_farmer.png",
		"mobs_mc_villager_farmer.png", --hat
	},
	{
		"mobs_mc_villager_priest.png",
		"mobs_mc_villager_priest.png", --hat
	},
	{
		"mobs_mc_villager_librarian.png",
		"mobs_mc_villager_librarian.png", --hat
	},
	{
		"mobs_mc_villager_butcher.png",
		"mobs_mc_villager_butcher.png", --hat
	},
	{
		"mobs_mc_villager_smith.png",
		"mobs_mc_villager_smith.png", --hat
	},
	},
	visual_size = {x=3, y=3},
	makes_footstep_sound = true,
	walk_velocity = 1.2,
	run_velocity = 2.4,
	drops = {},
	-- TODO: sounds
	animation = {
		stand_speed = 25,
		stand_start = 40,
		stand_end = 59,
		walk_speed = 25,
		walk_start = 0,
		walk_end = 40,
		run_speed = 25,
		run_start = 0,
		run_end = 40,
		die_speed = 15,
		die_start = 210,
		die_end = 220,
		die_loop = false,
	},
	water_damage = 0,
	lava_damage = 4,
	light_damage = 0,
	view_range = 16,
	fear_height = 4,
	jump = true,
	walk_chance = DEFAULT_WALK_CHANCE,
	on_rightclick = function(self, clicker)
		-- Initiate trading
		local name = clicker:get_player_name()
		self._trading_players[name] = true

		init_trader_vars(self)
		if self._trades == nil then
			init_trades(self)
		end
		update_max_tradenum(self)
		if self._trades == false then
			-- Villager has no trades, rightclick is a no-op
			return
		end

		player_trading_with[name] = self

		local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})

		set_trade(self, clicker, inv, 1)

		show_trade_formspec(name, self)

		-- Behaviour stuff:
		-- Make villager look at player and stand still
		local selfpos = self.object:get_pos()
		local clickerpos = clicker:get_pos()
		local dir = vector.direction(selfpos, clickerpos)
		self.object:set_yaw(minetest.dir_to_yaw(dir))
		stand_still(self)
	end,

	_player_scan_timer = 0,
	_trading_players = {}, -- list of playernames currently trading with villager (open formspec)
	do_custom = function(self, dtime)
		-- Stand still if player is nearby.
		if not self._player_scan_timer then
			self._player_scan_timer = 0
		end
		self._player_scan_timer = self._player_scan_timer + dtime
		-- Check infrequently to keep CPU load low
		if self._player_scan_timer > PLAYER_SCAN_INTERVAL then
			self._player_scan_timer = 0
			local selfpos = self.object:get_pos()
			local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS)
			local has_player = false
			for o, obj in pairs(objects) do
				if obj:is_player() then
					has_player = true
					break
				end
			end
			if has_player then
				minetest.log("verbose", "[mobs_mc] Player near villager found!")
				stand_still(self)
			else
				minetest.log("verbose", "[mobs_mc] No player near villager found!")
				self.walk_chance = DEFAULT_WALK_CHANCE
				self.jump = true
			end
		end
	end,

	on_spawn = function(self)
		init_trader_vars(self)
	end,
	on_die = function(self, pos)
		-- Close open trade formspecs and give input back to players
		local trading_players = self._trading_players
		for name, _ in pairs(trading_players) do
			minetest.close_formspec(name, "mobs_mc:trade_"..name)
			local player = minetest.get_player_by_name(name)
			if player then
				return_fields(player)
			end
		end
	end,
})



mobs:spawn_specific("mobs_mc:villager", mobs_mc.spawn.village, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 8000, 4, mobs_mc.spawn_height.water+1, mobs_mc.spawn_height.overworld_max)

-- compatibility
mobs:alias_mob("mobs:villager", "mobs_mc:villager")

-- spawn eggs
mobs:register_egg("mobs_mc:villager", S("Villager"), "mobs_mc_spawn_icon_villager.png", 0)

if minetest.settings:get_bool("log_mods") then
	minetest.log("action", "MC mobs loaded")
end
Könntest du vielleicht die Datei in Minecloneverzeichnis/mods/ENTITIES/mobs_mc/villager.lua
so abändern? Dann könnte man sie , auch wenn sie noch WIP sind schon benutzen. Die würden dann Leben in das Dorf bringen und andere Könnten auch mit ihnen handeln.
screenshot_20191005_173101.png
screenshot_20191005_173101.png (391.75 KiB) Viewed 557 times

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

Ups. es war noch ein kleiner Fehler im Code. Dieser Code hier funktioniert aber jetzt:

Code: Select all

--MCmobs v0.4
--maikerumine
--made for MC like Survival game
--License for code WTFPL and otherwise stated in readmes

--###################
--################### VILLAGER
--###################
-- Summary: Villagers are complex NPCs, their main feature allows players to trade with them.

-- TODO: Particles
-- TODO: 4s Regeneration I after trade unlock
-- TODO: Breeding
-- TODO: Baby villagers
-- TODO: Spawning in villages
-- TODO: Behaviour:
--     TODO: Walk around village, but do not leave it intentionally
--     TODO: Run into house on rain or danger, open doors
--     TODO: Internal inventory, pick up items, trade with other villagers
--     TODO: Farm stuff

local S = minetest.get_translator("mobs_mc")
local N = function(s) return s end

-- playername-indexed table containing the previously used tradenum
local player_tradenum = {}
-- playername-indexed table containing the objectref of trader, if trading formspec is open
local player_trading_with = {}

local DEFAULT_WALK_CHANCE = 33 -- chance to walk in percent, if no player nearby
local PLAYER_SCAN_INTERVAL = 5 -- every X seconds, villager looks for players nearby
local PLAYER_SCAN_RADIUS = 4 -- scan radius for looking for nearby players

--[=======[ TRADING ]=======]

-- LIST OF VILLAGER PROFESSIONS AND TRADES

-- TECHNICAL RESTRICTIONS (FIXME):
-- * You can't use a clock as requested item
-- * You can't use a compass as requested item if its stack size > 1
-- * You can't use a compass in the second requested slot
-- This is a problem in the mcl_compass and mcl_clock mods,
-- these items should be implemented as single items, then everything
-- will be much easier.

local COMPASS = "mcl_compass:compass"
if minetest.registered_aliases[COMPASS] then
	COMPASS = minetest.registered_aliases[COMPASS]
end

local E1 = { "mcl_core:dirt", 1, 1 } -- one emerald

-- Special trades for v6 only
-- NOTE: These symbols MUST only be added at the end of a tier
local TRADE_V6_RED_SANDSTONE, TRADE_V6_DARK_OAK_SAPLING, TRADE_V6_ACACIA_SAPLING, TRADE_V6_BIRCH_SAPLING
if minetest.get_mapgen_setting("mg_name") == "v6" then
	TRADE_V6_RED_SANDSTONE = { {mcl_shops:coin, 1, 1}, { "mcl_core:redsandstone", 12, 16 } }
	TRADE_V6_DARK_OAK_SAPLING = { { "mcl_shops:coin", 6, 9 }, { "mcl_core:darksapling", 1, 1 } }
	TRADE_V6_ACACIA_SAPLING = { { "mcl_shops:coin", 14, 17 }, { "mcl_core:acaciasapling", 1, 1 } }
	TRADE_V6_BIRCH_SAPLING = { { "mcl_shops:coin", 8, 11 }, { "mcl_core:birchsapling", 1, 1 } }
end

local professions = {
	farmer = {
		name = N("Farmer"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_farming:wheat_item", 18, 22, }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_farming:potato_item", 15, 19, }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_farming:carrot_item", 15, 19, }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_farming:bread", 2, 4 } },
			},

			{
			{ { "mcl_farming:pumpkin_face", 8, 12 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_farming:pumpkin_pie", 2, 3} },
			},

			{
			{ { "mcl_farming:melon_item", 7, 12 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_core:apple", 5, 7 }, },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mcl_farming:cookie", 6, 10 } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_cake:cake", 1, 1 } },
			TRADE_V6_BIRCH_SAPLING,
			TRADE_V6_DARK_OAK_SAPLING,
			TRADE_V6_ACACIA_SAPLING,
			},
		}
	},
	fisherman = {
		name = N("Fisherman"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_fishing:fish_raw", 6, 6, "mcl_shops:coin", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } },
			{ { "mcl_mobitems:string", 5, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_core:coal_lump", 5, 7 }, {mcl_shops:coin, 1, 1} },
			},
			-- TODO: enchanted fishing rod
		},
	},
	fletcher = {
		name = N("Fletcher"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_mobitems:string", 5, 7 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_bows:arrow", 8, 12 } },
			},

			{
			{ { "mcl_core:gravel", 10, 10, "mcl_shops:coin", 1, 1 }, { "mcl_core:flint", 6, 10 } },
			{ { "mcl_shops:coin", 2, 3 }, { "mcl_bows:bow", 1, 1 } },
			},
		}
	},
	shepherd ={
		name = N("Shepherd"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_wool:white", 3, 5 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_tools:shears", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:white", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:grey", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:silver", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:black", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:yellow", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:orange", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:red", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:magenta", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:purple", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:blue", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:cyan", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:lime", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:green", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:pink", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:light_blue", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:brown", 3, 5 } },
			},
		},
	},
	librarian = {
		name = N("Librarian"),
		texture = "mobs_mc_villager_librarian.png",
		trades = {
			{
			{ { "mcl_core:paper", 24, 36 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchanted book
			{ { "mcl_books:book", 8, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 4, 5 }, { "mcl_compass:compass", 1 ,1 }},
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_books:bookshelf", 1 ,1 }},
			},

			{
			{ { "mcl_books:written_book", 2, 2 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 9, 11 }, { "mcl_clock:clock", 1, 1 } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_core:glass", 5, 7 } },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mcl_core:glass", 5, 7 } },
			},

			-- TODO: 2 enchanted book tiers

			{
			{ { "mcl_shops:coin", 20, 22 }, { "mcl_mobs:nametag", 1, 1 } },
			}
		},
	},
	cartographer = {
		name = N("Cartographer"),
		texture = "mobs_mc_villager_librarian.png",
		trades = {
			{
			{ { "mcl_core:paper", 20, 30 }, {mcl_shops:coin, 1, 1} },
			},

			{
			-- subject to special checks
			{ { "mcl_compass:compass", 1, 1 }, {"mcl_shops:coin", 4, 5} },
			},

			{
			-- TODO: replace with empty map
			{ { "mcl_shops:coin", 5, 10}, { "mcl_maps:filled_map", 1, 1 } },
			},

			-- TODO: special maps
		},
	},
	armorer = {
		name = N("Armorer"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 4, 6 }, { "3d_armor:helmet_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:iron_ingot", 1, 1 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 7, 9 }, { "3d_armor:chestplate_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 26, 28 }, { "3d_armor:chestplate_gold", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 2 }, { "3d_armor:boots_chain", 1, 1 } },
			{ { "mcl_shops:coin", 2, 3 }, { "3d_armor:leggings_chain", 1, 1 } },
			{ { "mcl_shops:coin", 1, 2 }, { "3d_armor:helmet_chain", 1, 1 } },
			{ { "mcl_shops:coin", 3, 4 }, { "3d_armor:chestplate_chain", 1, 1 } },
			},
		},
	},
	leatherworker = {
		name = N("Leatherworker"),
		texture = "mobs_mc_villager_butcher.png",
		trades = {
			{
			{ { "mcl_mobitems:leather", 5, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 3, 5 }, { "3d_armor:leggings_leather", 2, 4 } },
			},

			{
			-- TODO: enchant
			{ { "mcl_shops:coin", 4, 7 }, { "3d_armor:chestplate_leather", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 15, 20 }, { "mcl_mobitems:saddle", 1, 1 } },
			},
		},
	},
	butcher = {
		name = N("Butcher"),
		texture = "mobs_mc_villager_butcher.png",
		trades = {
			{
			{ { "mcl_mobitems:beef", 5, 10 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_mobitems:chicken", 5, 10 }, {mcl_shops:coin, 1, 1} },
			},

			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			{ {mcl_shops:coin, 1, 1}, { "mcl_mobitems:cooked_beef", 5, 10 } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_mobitems:cooked_chicken", 5, 10 } },
			},
		},
	},
	weapon_smith = {
		name = N("Weapon Smith"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_tools:axe_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:iron_ingot", 1, 1 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 2, 3 }, { "mcl_tools:sword_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 17, 19 }, { "mcl_tools:sword_diamond", 1, 1 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 27, 28 }, { "mcl_tools:axe_diamond", 1, 1 } },
			},
		},
	},
	tool_smith = {
		name = N("Tool Smith"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 1, 1 }, { "mcl_tools:shovel_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:iron_ingot", 1, 1 }, {mcl_shops:coin, 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 2, 4 }, { "mcl_tools:pick_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 26, 28 }, { "mcl_tools:pick_diamond", 1, 1 } },
			},
		},
	},
	cleric = {
		name = N("Cleric"),
		texture = "mobs_mc_villager_priest.png",
		trades = {
			{
			{ { "mcl_mobitems:rotten_flesh", 15, 25 }, {mcl_shops:coin, 1, 1} },
			{ { "mcl_core:gold_ingot", 2, 2 }, {"mcl_shops:coin", 6, 6 } },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mesecons:redstone", 1, 4  } },
			{ {mcl_shops:coin, 1, 1}, { "mcl_dye:blue", 1, 2 } },
			},

			{
			{ {mcl_shops:coin, 1, 1}, { "mcl_nether:glowstone", 1, 3 } },
			{ { "mcl_shops:coin", 4, 7 }, { "mcl_throwing:ender_pearl", 1, 1 } },
			TRADE_V6_RED_SANDSTONE,
			},

			-- TODO: Bottle 'o enchanting
		},
	},
	nitwit = {
		name = N("Nitwit"),
		texture = "mobs_mc_villager.png",
		-- No trades for nitwit
		trades = nil,
	}
}

local profession_names = {}
for id, _ in pairs(professions) do
	table.insert(profession_names, id)
end

local stand_still = function(self)
	self.walk_chance = 0
	self.jump = false
end

local update_max_tradenum = function(self)
	if not self._trades then
		return
	end
	local trades = minetest.deserialize(self._trades)
	for t=1, #trades do
		local trade = trades[t]
		if trade.tier > self._max_trade_tier then
			self._max_tradenum = t - 1
			return
		end
	end
	self._max_tradenum = #trades
end

local init_trader_vars = function(self)
	if not self._profession then
		-- Select random profession from all professions with matching clothing
		local texture = self.base_texture[1]
		local matches = {}
		for prof_id, prof in pairs(professions) do
			if texture == prof.texture then
				table.insert(matches, prof_id)
			end
		end
		local p = math.random(1, #matches)
		self._profession = matches[p]
	end
	if not self._max_trade_tier then
		self._max_trade_tier = 1
	end
	if not self._locked_trades then
		self._locked_trades = 0
	end
	if not self._trading_players then
		self._trading_players = {}
	end
end

local init_trades = function(self, inv)
	local profession = professions[self._profession]
	local trade_tiers = profession.trades
	if trade_tiers == nil then
		-- Empty trades
		self._trades = false
		return
	end

	local max_tier = #trade_tiers
	local trades = {}
	for tiernum=1, max_tier do
		local tier = trade_tiers[tiernum]
		for tradenum=1, #tier do
			local trade = tier[tradenum]
			local wanted1_item = trade[1][1]
			local wanted1_count = math.random(trade[1][2], trade[1][3])
			local offered_item = trade[2][1]
			local offered_count = math.random(trade[2][2], trade[2][3])

			local wanted = { wanted1_item .. " " ..wanted1_count }
			if trade[1][4] then
				local wanted2_item = trade[1][4]
				local wanted2_count = math.random(trade[1][5], trade[1][6])
				table.insert(wanted, wanted2_item .. " " ..wanted2_count)
			end

			table.insert(trades, {
				wanted = wanted,
				offered = offered_item .. " " .. offered_count,
				tier = tiernum, -- tier of this trade
				traded_once = false, -- true if trade was traded at least once
				trade_counter = 0, -- how often the this trade was mate after the last time it got unlocked
				locked = false, -- if this trade is locked. Locked trades can't be used
			})
		end
	end
	self._trades = minetest.serialize(trades)
end

local set_trade = function(trader, player, inv, concrete_tradenum)
	local trades = minetest.deserialize(trader._trades)
	if not trades then
		init_trades(trader)
		trades = minetest.deserialize(trader._trades)
		if not trades then
			minetest.log("error", "[mobs_mc] Failed to select villager trade!")
			return
		end
	end
	local name = player:get_player_name()

	-- Stop tradenum from advancing into locked tiers or out-of-range areas
	if concrete_tradenum > trader._max_tradenum then
		concrete_tradenum = trader._max_tradenum
	elseif concrete_tradenum < 1 then
		concrete_tradenum = 1
	end
	player_tradenum[name] = concrete_tradenum
	local trade = trades[concrete_tradenum]
	inv:set_stack("wanted", 1, ItemStack(trade.wanted[1]))
	inv:set_stack("offered", 1, ItemStack(trade.offered))
	if trade.wanted[2] then
		local wanted2 = ItemStack(trade.wanted[2])
		inv:set_stack("wanted", 2, wanted2)
	else
		inv:set_stack("wanted", 2, "")
	end

end

local function show_trade_formspec(playername, trader, tradenum)
	if not trader._trades then
		return
	end
	if not tradenum then
		tradenum = 1
	end
	local trades = minetest.deserialize(trader._trades)
	local trade = trades[tradenum]
	local profession = professions[trader._profession].name
	local disabled_img = ""
	if trade.locked then
		disabled_img = "image[4.3,2.52;1,1;mobs_mc_trading_formspec_disabled.png]"..
			"image[4.3,1.1;1,1;mobs_mc_trading_formspec_disabled.png]"
	end
	local tradeinv_name = "mobs_mc:trade_"..playername
	local tradeinv = minetest.formspec_escape("detached:"..tradeinv_name)

	local b_prev, b_next = "", ""
	if #trades > 1 then
		if tradenum > 1 then
			b_prev = "button[1,1;0.5,1;prev_trade;<]"
		end
		if tradenum < trader._max_tradenum then
			b_next = "button[7.26,1;0.5,1;next_trade;>]"
		end
	end

	local formspec =
	"size[9,8.75]"
	.."background[-0.19,-0.25;9.41,9.49;mobs_mc_trading_formspec_bg.png]"
	..disabled_img
	..mcl_vars.inventory_header
	.."label[4,0;"..minetest.formspec_escape(minetest.colorize("#313131", S(profession))).."]"
	.."list[current_player;main;0,4.5;9,3;9]"
	.."list[current_player;main;0,7.74;9,1;]"
	..b_prev..b_next
	.."list["..tradeinv..";wanted;2,1;2,1;]"
	.."list["..tradeinv..";offered;5.76,1;1,1;]"
	.."list["..tradeinv..";input;2,2.5;2,1;]"
	.."list["..tradeinv..";output;5.76,2.55;1,1;]"
	.."listring["..tradeinv..";output]"
	.."listring[current_player;main]"
	.."listring["..tradeinv..";input]"
	.."listring[current_player;main]"
	minetest.sound_play("mobs_mc_villager_trade", {to_player = playername})
	minetest.show_formspec(playername, tradeinv_name, formspec)
end

local update_offer = function(inv, player, sound)
	local name = player:get_player_name()
	local trader = player_trading_with[name]
	local tradenum = player_tradenum[name]
	if not trader or not tradenum then
		return false
	end
	local trades = minetest.deserialize(trader._trades)
	if not trades then
		return false
	end
	local trade = trades[tradenum]
	if not trade then
		return false
	end
	local wanted1, wanted2 = inv:get_stack("wanted", 1), inv:get_stack("wanted", 2)
	local input1, input2 = inv:get_stack("input", 1), inv:get_stack("input", 2)

	-- BEGIN OF SPECIAL HANDLING OF COMPASS
	-- These 2 functions are a complicated check to check if the input contains a
	-- special item which we cannot check directly against their name, like
	-- compass.
	-- TODO: Remove these check functions when compass and clock are implemented
	-- as single items.
	local check_special = function(special_item, group, wanted1, wanted2, input1, input2)
		if minetest.registered_aliases[special_item] then
			special_item = minetest.registered_aliases[special_item]
		end
		if wanted1:get_name() == special_item then
			local check_input = function(input, wanted, group)
				return minetest.get_item_group(input:get_name(), group) ~= 0 and input:get_count() >= wanted:get_count()
			end
			if check_input(input1, wanted1, group) then
				return true
			elseif check_input(input2, wanted1, group) then
				return true
			else
				return false
			end
		end
		return false
	end
	-- Apply above function to all items which we consider special.
	-- This function succeeds if ANY item check succeeds.
	local check_specials = function(wanted1, wanted2, input1, input2)
		return check_special(COMPASS, "compass", wanted1, wanted2, input1, input2)
	end
	-- END OF SPECIAL HANDLING OF COMPASS

	if (
			((inv:contains_item("input", wanted1) and
			(wanted2:is_empty() or inv:contains_item("input", wanted2))) or
			-- BEGIN OF SPECIAL HANDLING OF COMPASS
			check_specials(wanted1, wanted2, input1, input2)) and
			-- END OF SPECIAL HANDLING OF COMPASS
			(trade.locked == false)) then
		inv:set_stack("output", 1, inv:get_stack("offered", 1))
		if sound then
			minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
		end
		return true
	else
		inv:set_stack("output", 1, ItemStack(""))
		if sound then
			minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
		end
		return false
	end
end

-- Returns a single itemstack in the given inventory to the player's main inventory, or drop it when there's no space left
local function return_item(itemstack, dropper, pos, inv_p)
	if dropper:is_player() then
		-- Return to main inventory
		if inv_p:room_for_item("main", itemstack) then
			inv_p:add_item("main", itemstack)
		else
			-- Drop item on the ground
			local v = dropper:get_look_dir()
			local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
			p.x = p.x+(math.random(1,3)*0.2)
			p.z = p.z+(math.random(1,3)*0.2)
			local obj = minetest.add_item(p, itemstack)
			if obj then
				v.x = v.x*4
				v.y = v.y*4 + 2
				v.z = v.z*4
				obj:set_velocity(v)
				obj:get_luaentity()._insta_collect = false
			end
		end
	else
		-- Fallback for unexpected cases
		minetest.add_item(pos, itemstack)
	end
	return itemstack
end

local return_fields = function(player)
	local name = player:get_player_name()
	local inv_t = minetest.get_inventory({type="detached", name = "mobs_mc:trade_"..name})
	local inv_p = player:get_inventory()
	for i=1, inv_t:get_size("input") do
		local stack = inv_t:get_stack("input", i)
		return_item(stack, player, player:get_pos(), inv_p)
		stack:clear()
		inv_t:set_stack("input", i, stack)
	end
	inv_t:set_stack("output", 1, "")
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if string.sub(formname, 1, 14) == "mobs_mc:trade_" then
		local name = player:get_player_name()
		if fields.quit then
			-- Get input items back
			return_fields(player)
			-- Reset internal "trading with" state
			local trader = player_trading_with[name]
			if trader then
				trader._trading_players[name] = nil
			end
			player_trading_with[name] = nil
		elseif fields.next_trade or fields.prev_trade then
			local trader = player_trading_with[name]
			if not trader or not trader.object:get_luaentity() then
				return
			end
			local trades = trader._trades
			if not trades then
				return
			end
			local dir = 1
			if fields.prev_trade then
				dir = -1
			end
			local tradenum = player_tradenum[name] + dir
			local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})
			set_trade(trader, player, inv, tradenum)
			update_offer(inv, player, false)
			show_trade_formspec(name, trader, player_tradenum[name])
		end
	end
end)

minetest.register_on_leaveplayer(function(player)
	local name = player:get_player_name()
	return_fields(player)
	player_tradenum[name] = nil
	local trader = player_trading_with[name]
	if trader then
		trader._trading_players[name] = nil
	end
	player_trading_with[name] = nil

end)

-- Return true if player is trading with villager, and the villager entity exists
local trader_exists = function(playername)
	local trader = player_trading_with[playername]
	return trader ~= nil and trader.object:get_luaentity() ~= nil
end

local trade_inventory = {
	allow_take = function(inv, listname, index, stack, player)
		if listname == "input" then
			return stack:get_count()
		elseif listname == "output" then
			if not trader_exists(player:get_player_name()) then
				return 0
			end
			-- Only allow taking full stack
			local count = stack:get_count()
			if count == inv:get_stack(listname, index):get_count() then
				-- Also update output stack again.
				-- If input has double the wanted items, the
				-- output will stay because there will be still
				-- enough items in input after the trade
				local wanted1 = inv:get_stack("wanted", 1)
				local wanted2 = inv:get_stack("wanted", 2)
				local input1 = inv:get_stack("input", 1)
				local input2 = inv:get_stack("input", 2)
				wanted1:set_count(wanted1:get_count()*2)
				wanted2:set_count(wanted2:get_count()*2)
				-- BEGIN OF SPECIAL HANDLING FOR COMPASS
				local special_checks = function(wanted1, input1, input2)
					if wanted1:get_name() == COMPASS then
						local compasses = 0
						if (minetest.get_item_group(input1:get_name(), "compass") ~= 0) then
							compasses = compasses + input1:get_count()
						end
						if (minetest.get_item_group(input2:get_name(), "compass") ~= 0) then
							compasses = compasses + input2:get_count()
						end
						return compasses >= wanted1:get_count()
					end
					return false
				end
				-- END OF SPECIAL HANDLING FOR COMPASS
				if (inv:contains_item("input", wanted1) and
					(wanted2:is_empty() or inv:contains_item("input", wanted2)))
					-- BEGIN OF SPECIAL HANDLING FOR COMPASS
					or special_checks(wanted1, input1, input2) then
					-- END OF SPECIAL HANDLING FOR COMPASS
					return -1
				else
					-- If less than double the wanted items,
					-- remove items from output (final trade,
					-- input runs empty)
					return count
				end
			else
				return 0
			end
		else
			return 0
		end
	end,
	allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
		if from_list == "input" and to_list == "input" then
			return count
		elseif from_list == "output" and to_list == "input" then
			if not trader_exists(player:get_player_name()) then
				return 0
			end
			local move_stack = inv:get_stack(from_list, from_index)
			if inv:get_stack(to_list, to_index):item_fits(move_stack) then
				return count
			end
		end
		return 0
	end,
	allow_put = function(inv, listname, index, stack, player)
		if listname == "input" then
			if not trader_exists(player:get_player_name()) then
				return 0
			else
				return stack:get_count()
			end
		else
			return 0
		end
	end,
	on_put = function(inv, listname, index, stack, player)
		update_offer(inv, player, true)
	end,
	on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
		if from_list == "output" and to_list == "input" then
			inv:remove_item("input", inv:get_stack("wanted", 1))
			local wanted2 = inv:get_stack("wanted", 2)
			if not wanted2:is_empty() then
				inv:remove_item("input", inv:get_stack("wanted", 2))
			end
			minetest.sound_play("mobs_mc_villager_accept", {to_player = player:get_player_name()})
		end
		update_offer(inv, player, true)
	end,
	on_take = function(inv, listname, index, stack, player)
		local accept
		local name = player:get_player_name()
		if listname == "output" then
			local wanted1 = inv:get_stack("wanted", 1)
			inv:remove_item("input", wanted1)
			local wanted2 = inv:get_stack("wanted", 2)
			if not wanted2:is_empty() then
				inv:remove_item("input", inv:get_stack("wanted", 2))
			end
			-- BEGIN OF SPECIAL HANDLING FOR COMPASS
			if wanted1:get_name() == COMPASS then
				for n=1, 2 do
					local input = inv:get_stack("input", n)
					if minetest.get_item_group(input:get_name(), "compass") ~= 0 then
						input:set_count(input:get_count() - wanted1:get_count())
						inv:set_stack("input", n, input)
						break
					end
				end
			end
			-- END OF SPECIAL HANDLING FOR COMPASS
			local trader = player_trading_with[name]
			local tradenum = player_tradenum[name]
			local trades
			if trader and trader._trades then
				trades = minetest.deserialize(trader._trades)
			end
			if trades then
				local trade = trades[tradenum]
				local unlock_stuff = false
				if not trade.traded_once then
					-- Unlock all the things if something was traded
					-- for the first time ever
					unlock_stuff = true
					trade.traded_once = true
				elseif trade.trade_counter == 0 and math.random(1,5) == 1 then
					-- Otherwise, 20% chance to unlock if used freshly reset trade
					unlock_stuff = true
				end
				local update_formspec = false
				if unlock_stuff then
					-- First-time trade unlock all trades and unlock next trade tier
					if trade.tier + 1 > trader._max_trade_tier then
						trader._max_trade_tier = trader._max_trade_tier + 1
						update_max_tradenum(trader)
						update_formspec = true
					end
					for t=1, #trades do
						trades[t].locked = false
						trades[t].trade_counter = 0
					end
					trader._locked_trades = 0
					-- Also heal trader for unlocking stuff
					-- TODO: Replace by Regeneration I
					trader.health = math.min(trader.hp_max, trader.health + 4)
				end
				trade.trade_counter = trade.trade_counter + 1
				-- Semi-randomly lock trade for repeated trade (not if there's only 1 trade)
				if trader._max_tradenum > 1 then
					if trade.trade_counter >= 12 then
						trade.locked = true
					elseif trade.trade_counter >= 2 then
						local r = math.random(1, math.random(4, 10))
						if r == 1 then
							trade.locked = true
						end
					end
				end

				if trade.locked then
					inv:set_stack("output", 1, "")
					update_formspec = true
					trader._locked_trades = trader._locked_trades + 1
					-- Check if we managed to lock ALL available trades. Rare but possible.
					if trader._locked_trades >= trader._max_tradenum then
						-- Emergency unlock! Unlock all other trades except the current one
						for t=1, #trades do
							if t ~= tradenum then
								trades[t].locked = false
								trades[t].trade_counter = 0
							end
						end
						trader._locked_trades = 1
						-- Also heal trader for unlocking stuff
						-- TODO: Replace by Regeneration I
						trader.health = math.min(trader.hp_max, trader.health + 4)
					end
				end
				trader._trades = minetest.serialize(trades)
				if update_formspec then
					show_trade_formspec(name, trader, tradenum)
				end
			else
				minetest.log("error", "[mobs_mc] Player took item from trader output but player_trading_with or player_tradenum is nil!")
			end

			accept = true
		elseif listname == "input" then
			update_offer(inv, player, false)
		end
		if accept then
			minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
		else
			minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
		end
	end,
}

minetest.register_on_joinplayer(function(player)
	local name = player:get_player_name()
	player_tradenum[name] = 1
	player_trading_with[name] = nil

	-- Create or get player-specific trading inventory
	local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})
	if not inv then
		inv = minetest.create_detached_inventory("mobs_mc:trade_"..name, trade_inventory, name)
	end
	inv:set_size("input", 2)
	inv:set_size("output", 1)
	inv:set_size("wanted", 2)
	inv:set_size("offered", 1)
end)

--[=======[ MOB REGISTRATION AND SPAWNING ]=======]

mobs:register_mob("mobs_mc:villager", {
	type = "npc",
	hp_min = 20,
	hp_max = 20,
	collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
	visual = "mesh",
	mesh = "mobs_mc_villager.b3d",
	textures = {
	{
		"mobs_mc_villager.png",
		"mobs_mc_villager.png", --hat
	},
	{
		"mobs_mc_villager_farmer.png",
		"mobs_mc_villager_farmer.png", --hat
	},
	{
		"mobs_mc_villager_priest.png",
		"mobs_mc_villager_priest.png", --hat
	},
	{
		"mobs_mc_villager_librarian.png",
		"mobs_mc_villager_librarian.png", --hat
	},
	{
		"mobs_mc_villager_butcher.png",
		"mobs_mc_villager_butcher.png", --hat
	},
	{
		"mobs_mc_villager_smith.png",
		"mobs_mc_villager_smith.png", --hat
	},
	},
	visual_size = {x=3, y=3},
	makes_footstep_sound = true,
	walk_velocity = 1.2,
	run_velocity = 2.4,
	drops = {},
	-- TODO: sounds
	animation = {
		stand_speed = 25,
		stand_start = 40,
		stand_end = 59,
		walk_speed = 25,
		walk_start = 0,
		walk_end = 40,
		run_speed = 25,
		run_start = 0,
		run_end = 40,
		die_speed = 15,
		die_start = 210,
		die_end = 220,
		die_loop = false,
	},
	water_damage = 0,
	lava_damage = 4,
	light_damage = 0,
	view_range = 16,
	fear_height = 4,
	jump = true,
	walk_chance = DEFAULT_WALK_CHANCE,
	on_rightclick = function(self, clicker)
		-- Initiate trading
		local name = clicker:get_player_name()
		self._trading_players[name] = true

		init_trader_vars(self)
		if self._trades == nil then
			init_trades(self)
		end
		update_max_tradenum(self)
		if self._trades == false then
			-- Villager has no trades, rightclick is a no-op
			return
		end

		player_trading_with[name] = self

		local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})

		set_trade(self, clicker, inv, 1)

		show_trade_formspec(name, self)

		-- Behaviour stuff:
		-- Make villager look at player and stand still
		local selfpos = self.object:get_pos()
		local clickerpos = clicker:get_pos()
		local dir = vector.direction(selfpos, clickerpos)
		self.object:set_yaw(minetest.dir_to_yaw(dir))
		stand_still(self)
	end,

	_player_scan_timer = 0,
	_trading_players = {}, -- list of playernames currently trading with villager (open formspec)
	do_custom = function(self, dtime)
		-- Stand still if player is nearby.
		if not self._player_scan_timer then
			self._player_scan_timer = 0
		end
		self._player_scan_timer = self._player_scan_timer + dtime
		-- Check infrequently to keep CPU load low
		if self._player_scan_timer > PLAYER_SCAN_INTERVAL then
			self._player_scan_timer = 0
			local selfpos = self.object:get_pos()
			local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS)
			local has_player = false
			for o, obj in pairs(objects) do
				if obj:is_player() then
					has_player = true
					break
				end
			end
			if has_player then
				minetest.log("verbose", "[mobs_mc] Player near villager found!")
				stand_still(self)
			else
				minetest.log("verbose", "[mobs_mc] No player near villager found!")
				self.walk_chance = DEFAULT_WALK_CHANCE
				self.jump = true
			end
		end
	end,

	on_spawn = function(self)
		init_trader_vars(self)
	end,
	on_die = function(self, pos)
		-- Close open trade formspecs and give input back to players
		local trading_players = self._trading_players
		for name, _ in pairs(trading_players) do
			minetest.close_formspec(name, "mobs_mc:trade_"..name)
			local player = minetest.get_player_by_name(name)
			if player then
				return_fields(player)
			end
		end
	end,
})



mobs:spawn_specific("mobs_mc:villager", mobs_mc.spawn.village, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 8000, 4, mobs_mc.spawn_height.water+1, mobs_mc.spawn_height.overworld_max)

-- compatibility
mobs:alias_mob("mobs:villager", "mobs_mc:villager")

-- spawn eggs
mobs:register_egg("mobs_mc:villager", S("Villager"), "mobs_mc_spawn_icon_villager.png", 0)

if minetest.settings:get_bool("log_mods") then
	minetest.log("action", "MC mobs loaded")
end
EDIT: Dieser Code funktioniert ja auch nicht. Ich setz mich nochmal ran...

User avatar
jjk1
Member
Posts: 385
Joined: Sun Mar 10, 2019 17:00
GitHub: jjk4
IRC: jjk1
In-game: jjk1
Contact:

Re: [Server] [v5.0.0] German-MineClone2-server

by jjk1 » Post

So jetzt hab ich's. Dieser Code funktioniert jetzt wirklich zu 100%:

Code: Select all

--MCmobs v0.4
--maikerumine
--made for MC like Survival game
--License for code WTFPL and otherwise stated in readmes

--###################
--################### VILLAGER
--###################
-- Summary: Villagers are complex NPCs, their main feature allows players to trade with them.

-- TODO: Particles
-- TODO: 4s Regeneration I after trade unlock
-- TODO: Breeding
-- TODO: Baby villagers
-- TODO: Spawning in villages
-- TODO: Behaviour:
--     TODO: Walk around village, but do not leave it intentionally
--     TODO: Run into house on rain or danger, open doors
--     TODO: Internal inventory, pick up items, trade with other villagers
--     TODO: Farm stuff

local S = minetest.get_translator("mobs_mc")
local N = function(s) return s end

-- playername-indexed table containing the previously used tradenum
local player_tradenum = {}
-- playername-indexed table containing the objectref of trader, if trading formspec is open
local player_trading_with = {}

local DEFAULT_WALK_CHANCE = 10 -- chance to walk in percent, if no player nearby
local PLAYER_SCAN_INTERVAL = 5 -- every X seconds, villager looks for players nearby
local PLAYER_SCAN_RADIUS = 4 -- scan radius for looking for nearby players

--[=======[ TRADING ]=======]

-- LIST OF VILLAGER PROFESSIONS AND TRADES

-- TECHNICAL RESTRICTIONS (FIXME):
-- * You can't use a clock as requested item
-- * You can't use a compass as requested item if its stack size > 1
-- * You can't use a compass in the second requested slot
-- This is a problem in the mcl_compass and mcl_clock mods,
-- these items should be implemented as single items, then everything
-- will be much easier.

local COMPASS = "mcl_compass:compass"
if minetest.registered_aliases[COMPASS] then
	COMPASS = minetest.registered_aliases[COMPASS]
end

local E1 = { "mcl_shops:coin", 1, 1 } -- one emerald

local professions = {
	farmer = {
		name = N("Farmer"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_farming:wheat_item", 18, 22, }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_farming:potato_item", 15, 19, }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_farming:carrot_item", 15, 19, }, {"mcl_shops:coin", 1, 1} },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_farming:bread", 2, 4 } },
			},

			{
			{ { "mcl_farming:pumpkin_face", 8, 12 }, {"mcl_shops:coin", 1, 1} },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_farming:pumpkin_pie", 2, 3} },
			},

			{
			{ { "mcl_farming:melon_item", 7, 12 }, {"mcl_shops:coin", 1, 1} },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_core:apple", 5, 7 }, },
			},

			{
			{ {"mcl_shops:coin", 1, 1}, { "mcl_farming:cookie", 6, 10 } },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_cake:cake", 1, 1 } }

			},
		}
	},
	fisherman = {
		name = N("Fisherman"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_fishing:fish_raw", 6, 6, "mcl_shops:coin", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } },
			{ { "mcl_mobitems:string", 5, 10 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_core:coal_lump", 5, 7 }, {"mcl_shops:coin", 1, 1} },
			},
			-- TODO: enchanted fishing rod
		},
	},
	fletcher = {
		name = N("Fletcher"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_mobitems:string", 5, 7 }, {"mcl_shops:coin", 1, 1} },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_bows:arrow", 8, 12 } },
			},

			{
			{ { "mcl_core:gravel", 10, 10, "mcl_shops:coin", 1, 1 }, { "mcl_core:flint", 6, 10 } },
			{ { "mcl_shops:coin", 2, 3 }, { "mcl_bows:bow", 1, 1 } },
			},
		}
	},
	shepherd ={
		name = N("Shepherd"),
		texture = "mobs_mc_villager_farmer.png",
		trades = {
			{
			{ { "mcl_wool:white", 3, 5 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_tools:shears", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:white", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:grey", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:silver", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:black", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:yellow", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:orange", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:red", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:magenta", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:purple", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:blue", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:cyan", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:lime", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:green", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:pink", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:light_blue", 3, 5 } },
			{ { "mcl_shops:coin", 1, 2 }, { "mcl_wool:brown", 3, 5 } },
			},
		},
	},
	librarian = {
		name = N("Librarian"),
		texture = "mobs_mc_villager_librarian.png",
		trades = {
			{
			{ { "mcl_core:paper", 24, 36 }, {"mcl_shops:coin", 1, 1} },
			-- TODO: enchanted book
			{ { "mcl_books:book", 8, 10 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 4, 5 }, { "mcl_compass:compass", 1 ,1 }},
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_books:bookshelf", 1 ,1 }},
			},

			{
			{ { "mcl_books:written_book", 2, 2 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 9, 11 }, { "mcl_clock:clock", 1, 1 } },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_core:glass", 5, 7 } },
			},

			{
			{ {"mcl_shops:coin", 1, 1}, { "mcl_core:glass", 5, 7 } },
			},

			-- TODO: 2 enchanted book tiers

			{
			{ { "mcl_shops:coin", 20, 22 }, { "mcl_mobs:nametag", 1, 1 } },
			}
		},
	},
	cartographer = {
		name = N("Cartographer"),
		texture = "mobs_mc_villager_librarian.png",
		trades = {
			{
			{ { "mcl_core:paper", 20, 30 }, {"mcl_shops:coin", 1, 1} },
			},

			{
			-- subject to special checks
			{ { "mcl_compass:compass", 1, 1 }, {"mcl_shops:coin", 4, 5} },
			},

			{
			-- TODO: replace with empty map
			{ { "mcl_shops:coin", 5, 10}, { "mcl_maps:filled_map", 1, 1 } },
			},

			-- TODO: special maps
		},
	},
	armorer = {
		name = N("Armorer"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 4, 6 }, { "3d_armor:helmet_iron", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 1 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 7, 9 }, { "3d_armor:chestplate_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 26, 28 }, { "3d_armor:chestplate_gold", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 2 }, { "3d_armor:boots_chain", 1, 1 } },
			{ { "mcl_shops:coin", 2, 3 }, { "3d_armor:leggings_chain", 1, 1 } },
			{ { "mcl_shops:coin", 1, 2 }, { "3d_armor:helmet_chain", 1, 1 } },
			{ { "mcl_shops:coin", 3, 4 }, { "3d_armor:chestplate_chain", 1, 1 } },
			},
		},
	},
	leatherworker = {
		name = N("Leatherworker"),
		texture = "mobs_mc_villager_butcher.png",
		trades = {
			{
			{ { "mcl_mobitems:leather", 5, 10 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 3, 5 }, { "3d_armor:leggings_leather", 2, 4 } },
			},

			{
			-- TODO: enchant
			{ { "mcl_shops:coin", 4, 7 }, { "3d_armor:chestplate_leather", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 15, 20 }, { "mcl_mobitems:saddle", 1, 1 } },
			},
		},
	},
	butcher = {
		name = N("Butcher"),
		texture = "mobs_mc_villager_butcher.png",
		trades = {
			{
			{ { "mcl_mobitems:beef", 5, 10 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_mobitems:chicken", 5, 10 }, {"mcl_shops:coin", 1, 1} },
			},

			{
			{ { "mcl_core:coal_lump", 2, 3 }, {"mcl_shops:coin", 1, 1} },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_mobitems:cooked_beef", 5, 10 } },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_mobitems:cooked_chicken", 5, 10 } },
			},
		},
	},
	weapon_smith = {
		name = N("Weapon Smith"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_shops:coin", 3, 4 }, { "mcl_tools:axe_iron", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 1 }, {"mcl_shops:coin", 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 2, 3 }, { "mcl_tools:sword_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 17, 19 }, { "mcl_tools:sword_diamond", 1, 1 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 27, 28 }, { "mcl_tools:axe_diamond", 1, 1 } },
			},
		},
	},
	tool_smith = {
		name = N("Tool Smith"),
		texture = "mobs_mc_villager_smith.png",
		trades = {
			{
			{ { "mcl_core:coal_lump", 2, 3 }, {"mcl_shops:coin", 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 1, 1 }, { "mcl_tools:shovel_iron", 1, 1 } },
			},

			{
			{ { "mcl_shops:coin", 1, 1 }, {"mcl_shops:coin", 1, 1} },
			-- TODO: enchant
			{ { "mcl_shops:coin", 2, 4 }, { "mcl_tools:pick_iron", 1, 1 } },
			},

			{
			{ { "mcl_core:diamond", 1, 1 }, {"mcl_shops:coin", 9, 9 } },
			-- TODO: enchant
			{ { "mcl_shops:coin", 26, 28 }, { "mcl_tools:pick_diamond", 1, 1 } },
			},
		},
	},
	cleric = {
		name = N("Cleric"),
		texture = "mobs_mc_villager_priest.png",
		trades = {
			{
			{ { "mcl_mobitems:rotten_flesh", 15, 25 }, {"mcl_shops:coin", 1, 1} },
			{ { "mcl_core:gold_ingot", 2, 2 }, {"mcl_shops:coin", 6, 6 } },
			},

			{
			{ {"mcl_shops:coin", 1, 1}, { "mesecons:redstone", 1, 4  } },
			{ {"mcl_shops:coin", 1, 1}, { "mcl_dye:blue", 1, 2 } },
			},

			{
			{ {"mcl_shops:coin", 1, 1}, { "mcl_nether:glowstone", 1, 3 } },
			{ { "mcl_shops:coin", 4, 7 }, { "mcl_throwing:ender_pearl", 1, 1 } },
			TRADE_V6_RED_SANDSTONE,
			},

			-- TODO: Bottle 'o enchanting
		},
	},
	nitwit = {
		name = N("Nitwit"),
		texture = "mobs_mc_villager.png",
		-- No trades for nitwit
		trades = nil,
	}
}

local profession_names = {}
for id, _ in pairs(professions) do
	table.insert(profession_names, id)
end

local stand_still = function(self)
	self.walk_chance = 0
	self.jump = false
end

local update_max_tradenum = function(self)
	if not self._trades then
		return
	end
	local trades = minetest.deserialize(self._trades)
	for t=1, #trades do
		local trade = trades[t]
		if trade.tier > self._max_trade_tier then
			self._max_tradenum = t - 1
			return
		end
	end
	self._max_tradenum = #trades
end

local init_trader_vars = function(self)
	if not self._profession then
		-- Select random profession from all professions with matching clothing
		local texture = self.base_texture[1]
		local matches = {}
		for prof_id, prof in pairs(professions) do
			if texture == prof.texture then
				table.insert(matches, prof_id)
			end
		end
		local p = math.random(1, #matches)
		self._profession = matches[p]
	end
	if not self._max_trade_tier then
		self._max_trade_tier = 1
	end
	if not self._locked_trades then
		self._locked_trades = 0
	end
	if not self._trading_players then
		self._trading_players = {}
	end
end

local init_trades = function(self, inv)
	local profession = professions[self._profession]
	local trade_tiers = profession.trades
	if trade_tiers == nil then
		-- Empty trades
		self._trades = false
		return
	end

	local max_tier = #trade_tiers
	local trades = {}
	for tiernum=1, max_tier do
		local tier = trade_tiers[tiernum]
		for tradenum=1, #tier do
			local trade = tier[tradenum]
			local wanted1_item = trade[1][1]
			local wanted1_count = math.random(trade[1][2], trade[1][3])
			local offered_item = trade[2][1]
			local offered_count = math.random(trade[2][2], trade[2][3])

			local wanted = { wanted1_item .. " " ..wanted1_count }
			if trade[1][4] then
				local wanted2_item = trade[1][4]
				local wanted2_count = math.random(trade[1][5], trade[1][6])
				table.insert(wanted, wanted2_item .. " " ..wanted2_count)
			end

			table.insert(trades, {
				wanted = wanted,
				offered = offered_item .. " " .. offered_count,
				tier = tiernum, -- tier of this trade
				traded_once = false, -- true if trade was traded at least once
				trade_counter = 0, -- how often the this trade was mate after the last time it got unlocked
				locked = false, -- if this trade is locked. Locked trades can't be used
			})
		end
	end
	self._trades = minetest.serialize(trades)
end

local set_trade = function(trader, player, inv, concrete_tradenum)
	local trades = minetest.deserialize(trader._trades)
	if not trades then
		init_trades(trader)
		trades = minetest.deserialize(trader._trades)
		if not trades then
			minetest.log("error", "[mobs_mc] Failed to select villager trade!")
			return
		end
	end
	local name = player:get_player_name()

	-- Stop tradenum from advancing into locked tiers or out-of-range areas
	if concrete_tradenum > trader._max_tradenum then
		concrete_tradenum = trader._max_tradenum
	elseif concrete_tradenum < 1 then
		concrete_tradenum = 1
	end
	player_tradenum[name] = concrete_tradenum
	local trade = trades[concrete_tradenum]
	inv:set_stack("wanted", 1, ItemStack(trade.wanted[1]))
	inv:set_stack("offered", 1, ItemStack(trade.offered))
	if trade.wanted[2] then
		local wanted2 = ItemStack(trade.wanted[2])
		inv:set_stack("wanted", 2, wanted2)
	else
		inv:set_stack("wanted", 2, "")
	end

end

local function show_trade_formspec(playername, trader, tradenum)
	if not trader._trades then
		return
	end
	if not tradenum then
		tradenum = 1
	end
	local trades = minetest.deserialize(trader._trades)
	local trade = trades[tradenum]
	local profession = professions[trader._profession].name
	local disabled_img = ""
	if trade.locked then
		disabled_img = "image[4.3,2.52;1,1;mobs_mc_trading_formspec_disabled.png]"..
			"image[4.3,1.1;1,1;mobs_mc_trading_formspec_disabled.png]"
	end
	local tradeinv_name = "mobs_mc:trade_"..playername
	local tradeinv = minetest.formspec_escape("detached:"..tradeinv_name)

	local b_prev, b_next = "", ""
	if #trades > 1 then
		if tradenum > 1 then
			b_prev = "button[1,1;0.5,1;prev_trade;<]"
		end
		if tradenum < trader._max_tradenum then
			b_next = "button[7.26,1;0.5,1;next_trade;>]"
		end
	end

	local formspec =
	"size[9,8.75]"
	.."background[-0.19,-0.25;9.41,9.49;mobs_mc_trading_formspec_bg.png]"
	..disabled_img
	..mcl_vars.inventory_header
	.."label[4,0;"..minetest.formspec_escape(minetest.colorize("#313131", S(profession))).."]"
	.."list[current_player;main;0,4.5;9,3;9]"
	.."list[current_player;main;0,7.74;9,1;]"
	..b_prev..b_next
	.."list["..tradeinv..";wanted;2,1;2,1;]"
	.."list["..tradeinv..";offered;5.76,1;1,1;]"
	.."list["..tradeinv..";input;2,2.5;2,1;]"
	.."list["..tradeinv..";output;5.76,2.55;1,1;]"
	.."listring["..tradeinv..";output]"
	.."listring[current_player;main]"
	.."listring["..tradeinv..";input]"
	.."listring[current_player;main]"
	minetest.sound_play("mobs_mc_villager_trade", {to_player = playername})
	minetest.show_formspec(playername, tradeinv_name, formspec)
end

local update_offer = function(inv, player, sound)
	local name = player:get_player_name()
	local trader = player_trading_with[name]
	local tradenum = player_tradenum[name]
	if not trader or not tradenum then
		return false
	end
	local trades = minetest.deserialize(trader._trades)
	if not trades then
		return false
	end
	local trade = trades[tradenum]
	if not trade then
		return false
	end
	local wanted1, wanted2 = inv:get_stack("wanted", 1), inv:get_stack("wanted", 2)
	local input1, input2 = inv:get_stack("input", 1), inv:get_stack("input", 2)

	-- BEGIN OF SPECIAL HANDLING OF COMPASS
	-- These 2 functions are a complicated check to check if the input contains a
	-- special item which we cannot check directly against their name, like
	-- compass.
	-- TODO: Remove these check functions when compass and clock are implemented
	-- as single items.
	local check_special = function(special_item, group, wanted1, wanted2, input1, input2)
		if minetest.registered_aliases[special_item] then
			special_item = minetest.registered_aliases[special_item]
		end
		if wanted1:get_name() == special_item then
			local check_input = function(input, wanted, group)
				return minetest.get_item_group(input:get_name(), group) ~= 0 and input:get_count() >= wanted:get_count()
			end
			if check_input(input1, wanted1, group) then
				return true
			elseif check_input(input2, wanted1, group) then
				return true
			else
				return false
			end
		end
		return false
	end
	-- Apply above function to all items which we consider special.
	-- This function succeeds if ANY item check succeeds.
	local check_specials = function(wanted1, wanted2, input1, input2)
		return check_special(COMPASS, "compass", wanted1, wanted2, input1, input2)
	end
	-- END OF SPECIAL HANDLING OF COMPASS

	if (
			((inv:contains_item("input", wanted1) and
			(wanted2:is_empty() or inv:contains_item("input", wanted2))) or
			-- BEGIN OF SPECIAL HANDLING OF COMPASS
			check_specials(wanted1, wanted2, input1, input2)) and
			-- END OF SPECIAL HANDLING OF COMPASS
			(trade.locked == false)) then
		inv:set_stack("output", 1, inv:get_stack("offered", 1))
		if sound then
			minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
		end
		return true
	else
		inv:set_stack("output", 1, ItemStack(""))
		if sound then
			minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
		end
		return false
	end
end

-- Returns a single itemstack in the given inventory to the player's main inventory, or drop it when there's no space left
local function return_item(itemstack, dropper, pos, inv_p)
	if dropper:is_player() then
		-- Return to main inventory
		if inv_p:room_for_item("main", itemstack) then
			inv_p:add_item("main", itemstack)
		else
			-- Drop item on the ground
			local v = dropper:get_look_dir()
			local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
			p.x = p.x+(math.random(1,3)*0.2)
			p.z = p.z+(math.random(1,3)*0.2)
			local obj = minetest.add_item(p, itemstack)
			if obj then
				v.x = v.x*4
				v.y = v.y*4 + 2
				v.z = v.z*4
				obj:set_velocity(v)
				obj:get_luaentity()._insta_collect = false
			end
		end
	else
		-- Fallback for unexpected cases
		minetest.add_item(pos, itemstack)
	end
	return itemstack
end

local return_fields = function(player)
	local name = player:get_player_name()
	local inv_t = minetest.get_inventory({type="detached", name = "mobs_mc:trade_"..name})
	local inv_p = player:get_inventory()
	for i=1, inv_t:get_size("input") do
		local stack = inv_t:get_stack("input", i)
		return_item(stack, player, player:get_pos(), inv_p)
		stack:clear()
		inv_t:set_stack("input", i, stack)
	end
	inv_t:set_stack("output", 1, "")
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if string.sub(formname, 1, 14) == "mobs_mc:trade_" then
		local name = player:get_player_name()
		if fields.quit then
			-- Get input items back
			return_fields(player)
			-- Reset internal "trading with" state
			local trader = player_trading_with[name]
			if trader then
				trader._trading_players[name] = nil
			end
			player_trading_with[name] = nil
		elseif fields.next_trade or fields.prev_trade then
			local trader = player_trading_with[name]
			if not trader or not trader.object:get_luaentity() then
				return
			end
			local trades = trader._trades
			if not trades then
				return
			end
			local dir = 1
			if fields.prev_trade then
				dir = -1
			end
			local tradenum = player_tradenum[name] + dir
			local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})
			set_trade(trader, player, inv, tradenum)
			update_offer(inv, player, false)
			show_trade_formspec(name, trader, player_tradenum[name])
		end
	end
end)

minetest.register_on_leaveplayer(function(player)
	local name = player:get_player_name()
	return_fields(player)
	player_tradenum[name] = nil
	local trader = player_trading_with[name]
	if trader then
		trader._trading_players[name] = nil
	end
	player_trading_with[name] = nil

end)

-- Return true if player is trading with villager, and the villager entity exists
local trader_exists = function(playername)
	local trader = player_trading_with[playername]
	return trader ~= nil and trader.object:get_luaentity() ~= nil
end

local trade_inventory = {
	allow_take = function(inv, listname, index, stack, player)
		if listname == "input" then
			return stack:get_count()
		elseif listname == "output" then
			if not trader_exists(player:get_player_name()) then
				return 0
			end
			-- Only allow taking full stack
			local count = stack:get_count()
			if count == inv:get_stack(listname, index):get_count() then
				-- Also update output stack again.
				-- If input has double the wanted items, the
				-- output will stay because there will be still
				-- enough items in input after the trade
				local wanted1 = inv:get_stack("wanted", 1)
				local wanted2 = inv:get_stack("wanted", 2)
				local input1 = inv:get_stack("input", 1)
				local input2 = inv:get_stack("input", 2)
				wanted1:set_count(wanted1:get_count()*2)
				wanted2:set_count(wanted2:get_count()*2)
				-- BEGIN OF SPECIAL HANDLING FOR COMPASS
				local special_checks = function(wanted1, input1, input2)
					if wanted1:get_name() == COMPASS then
						local compasses = 0
						if (minetest.get_item_group(input1:get_name(), "compass") ~= 0) then
							compasses = compasses + input1:get_count()
						end
						if (minetest.get_item_group(input2:get_name(), "compass") ~= 0) then
							compasses = compasses + input2:get_count()
						end
						return compasses >= wanted1:get_count()
					end
					return false
				end
				-- END OF SPECIAL HANDLING FOR COMPASS
				if (inv:contains_item("input", wanted1) and
					(wanted2:is_empty() or inv:contains_item("input", wanted2)))
					-- BEGIN OF SPECIAL HANDLING FOR COMPASS
					or special_checks(wanted1, input1, input2) then
					-- END OF SPECIAL HANDLING FOR COMPASS
					return -1
				else
					-- If less than double the wanted items,
					-- remove items from output (final trade,
					-- input runs empty)
					return count
				end
			else
				return 0
			end
		else
			return 0
		end
	end,
	allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
		if from_list == "input" and to_list == "input" then
			return count
		elseif from_list == "output" and to_list == "input" then
			if not trader_exists(player:get_player_name()) then
				return 0
			end
			local move_stack = inv:get_stack(from_list, from_index)
			if inv:get_stack(to_list, to_index):item_fits(move_stack) then
				return count
			end
		end
		return 0
	end,
	allow_put = function(inv, listname, index, stack, player)
		if listname == "input" then
			if not trader_exists(player:get_player_name()) then
				return 0
			else
				return stack:get_count()
			end
		else
			return 0
		end
	end,
	on_put = function(inv, listname, index, stack, player)
		update_offer(inv, player, true)
	end,
	on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
		if from_list == "output" and to_list == "input" then
			inv:remove_item("input", inv:get_stack("wanted", 1))
			local wanted2 = inv:get_stack("wanted", 2)
			if not wanted2:is_empty() then
				inv:remove_item("input", inv:get_stack("wanted", 2))
			end
			minetest.sound_play("mobs_mc_villager_accept", {to_player = player:get_player_name()})
		end
		update_offer(inv, player, true)
	end,
	on_take = function(inv, listname, index, stack, player)
		local accept
		local name = player:get_player_name()
		if listname == "output" then
			local wanted1 = inv:get_stack("wanted", 1)
			inv:remove_item("input", wanted1)
			local wanted2 = inv:get_stack("wanted", 2)
			if not wanted2:is_empty() then
				inv:remove_item("input", inv:get_stack("wanted", 2))
			end
			-- BEGIN OF SPECIAL HANDLING FOR COMPASS
			if wanted1:get_name() == COMPASS then
				for n=1, 2 do
					local input = inv:get_stack("input", n)
					if minetest.get_item_group(input:get_name(), "compass") ~= 0 then
						input:set_count(input:get_count() - wanted1:get_count())
						inv:set_stack("input", n, input)
						break
					end
				end
			end
			-- END OF SPECIAL HANDLING FOR COMPASS
			local trader = player_trading_with[name]
			local tradenum = player_tradenum[name]
			local trades
			if trader and trader._trades then
				trades = minetest.deserialize(trader._trades)
			end
			if trades then
				local trade = trades[tradenum]
				local unlock_stuff = false
				if not trade.traded_once then
					-- Unlock all the things if something was traded
					-- for the first time ever
					unlock_stuff = true
					trade.traded_once = true
				elseif trade.trade_counter == 0 and math.random(1,5) == 1 then
					-- Otherwise, 20% chance to unlock if used freshly reset trade
					unlock_stuff = true
				end
				local update_formspec = false
				if unlock_stuff then
					-- First-time trade unlock all trades and unlock next trade tier
					if trade.tier + 1 > trader._max_trade_tier then
						trader._max_trade_tier = trader._max_trade_tier + 1
						update_max_tradenum(trader)
						update_formspec = true
					end
					for t=1, #trades do
						trades[t].locked = false
						trades[t].trade_counter = 0
					end
					trader._locked_trades = 0
					-- Also heal trader for unlocking stuff
					-- TODO: Replace by Regeneration I
					trader.health = math.min(trader.hp_max, trader.health + 4)
				end
				trade.trade_counter = trade.trade_counter + 1
				-- Semi-randomly lock trade for repeated trade (not if there's only 1 trade)
				if trader._max_tradenum > 1 then
					if trade.trade_counter >= 12 then
						trade.locked = true
					elseif trade.trade_counter >= 2 then
						local r = math.random(1, math.random(4, 10))
						if r == 1 then
							trade.locked = true
						end
					end
				end

				if trade.locked then
					inv:set_stack("output", 1, "")
					update_formspec = true
					trader._locked_trades = trader._locked_trades + 1
					-- Check if we managed to lock ALL available trades. Rare but possible.
					if trader._locked_trades >= trader._max_tradenum then
						-- Emergency unlock! Unlock all other trades except the current one
						for t=1, #trades do
							if t ~= tradenum then
								trades[t].locked = false
								trades[t].trade_counter = 0
							end
						end
						trader._locked_trades = 1
						-- Also heal trader for unlocking stuff
						-- TODO: Replace by Regeneration I
						trader.health = math.min(trader.hp_max, trader.health + 4)
					end
				end
				trader._trades = minetest.serialize(trades)
				if update_formspec then
					show_trade_formspec(name, trader, tradenum)
				end
			else
				minetest.log("error", "[mobs_mc] Player took item from trader output but player_trading_with or player_tradenum is nil!")
			end

			accept = true
		elseif listname == "input" then
			update_offer(inv, player, false)
		end
		if accept then
			minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
		else
			minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
		end
	end,
}

minetest.register_on_joinplayer(function(player)
	local name = player:get_player_name()
	player_tradenum[name] = 1
	player_trading_with[name] = nil

	-- Create or get player-specific trading inventory
	local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})
	if not inv then
		inv = minetest.create_detached_inventory("mobs_mc:trade_"..name, trade_inventory, name)
	end
	inv:set_size("input", 2)
	inv:set_size("output", 1)
	inv:set_size("wanted", 2)
	inv:set_size("offered", 1)
end)

--[=======[ MOB REGISTRATION AND SPAWNING ]=======]

mobs:register_mob("mobs_mc:villager", {
	type = "npc",
	hp_min = 20,
	hp_max = 20,
	collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
	visual = "mesh",
	mesh = "mobs_mc_villager.b3d",
	textures = {
	{
		"mobs_mc_villager.png",
		"mobs_mc_villager.png", --hat
	},
	{
		"mobs_mc_villager_farmer.png",
		"mobs_mc_villager_farmer.png", --hat
	},
	{
		"mobs_mc_villager_priest.png",
		"mobs_mc_villager_priest.png", --hat
	},
	{
		"mobs_mc_villager_librarian.png",
		"mobs_mc_villager_librarian.png", --hat
	},
	{
		"mobs_mc_villager_butcher.png",
		"mobs_mc_villager_butcher.png", --hat
	},
	{
		"mobs_mc_villager_smith.png",
		"mobs_mc_villager_smith.png", --hat
	},
	},
	visual_size = {x=3, y=3},
	makes_footstep_sound = true,
	walk_velocity = 1.2,
	run_velocity = 2.4,
	drops = {},
	-- TODO: sounds
	animation = {
		stand_speed = 25,
		stand_start = 40,
		stand_end = 59,
		walk_speed = 25,
		walk_start = 0,
		walk_end = 40,
		run_speed = 25,
		run_start = 0,
		run_end = 40,
		die_speed = 15,
		die_start = 210,
		die_end = 220,
		die_loop = false,
	},
	water_damage = 0,
	lava_damage = 4,
	light_damage = 0,
	view_range = 16,
	fear_height = 4,
	jump = true,
	walk_chance = DEFAULT_WALK_CHANCE,
	on_rightclick = function(self, clicker)
		-- Initiate trading
		local name = clicker:get_player_name()
		self._trading_players[name] = true

		init_trader_vars(self)
		if self._trades == nil then
			init_trades(self)
		end
		update_max_tradenum(self)
		if self._trades == false then
			-- Villager has no trades, rightclick is a no-op
			return
		end

		player_trading_with[name] = self

		local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade_"..name})

		set_trade(self, clicker, inv, 1)

		show_trade_formspec(name, self)

		-- Behaviour stuff:
		-- Make villager look at player and stand still
		local selfpos = self.object:get_pos()
		local clickerpos = clicker:get_pos()
		local dir = vector.direction(selfpos, clickerpos)
		self.object:set_yaw(minetest.dir_to_yaw(dir))
		stand_still(self)
	end,

	_player_scan_timer = 0,
	_trading_players = {}, -- list of playernames currently trading with villager (open formspec)
	do_custom = function(self, dtime)
		-- Stand still if player is nearby.
		if not self._player_scan_timer then
			self._player_scan_timer = 0
		end
		self._player_scan_timer = self._player_scan_timer + dtime
		-- Check infrequently to keep CPU load low
		if self._player_scan_timer > PLAYER_SCAN_INTERVAL then
			self._player_scan_timer = 0
			local selfpos = self.object:get_pos()
			local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS)
			local has_player = false
			for o, obj in pairs(objects) do
				if obj:is_player() then
					has_player = true
					break
				end
			end
			if has_player then
				minetest.log("verbose", "[mobs_mc] Player near villager found!")
				stand_still(self)
			else
				minetest.log("verbose", "[mobs_mc] No player near villager found!")
				self.walk_chance = DEFAULT_WALK_CHANCE
				self.jump = true
			end
		end
	end,

	on_spawn = function(self)
		init_trader_vars(self)
	end,
	on_die = function(self, pos)
		-- Close open trade formspecs and give input back to players
		local trading_players = self._trading_players
		for name, _ in pairs(trading_players) do
			minetest.close_formspec(name, "mobs_mc:trade_"..name)
			local player = minetest.get_player_by_name(name)
			if player then
				return_fields(player)
			end
		end
	end,
})



mobs:spawn_specific("mobs_mc:villager", mobs_mc.spawn.village, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 8000, 4, mobs_mc.spawn_height.water+1, mobs_mc.spawn_height.overworld_max)

-- compatibility
mobs:alias_mob("mobs:villager", "mobs_mc:villager")

-- spawn eggs
mobs:register_egg("mobs_mc:villager", S("Villager"), "mobs_mc_spawn_icon_villager.png", 0)

if minetest.settings:get_bool("log_mods") then
	minetest.log("action", "MC mobs loaded")
end

Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests