[Server] [v5.1.0] German-MineClone2-server

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

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

by Walker » Fri Sep 06, 2019 14:48

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 !
 

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Fri Sep 06, 2019 15:15

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

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Fri Sep 06, 2019 15:42

Walker wrote:geht ihr also bitte nicht auf den Sack !

Darf ich dich mal wieder korrigieren?: IHM ;)
 

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

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

by Walker » Mon Sep 09, 2019 02:37

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Fri Sep 13, 2019 04:12

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Fri Sep 13, 2019 04:26

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Fri Sep 13, 2019 16:29

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

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Fri Sep 13, 2019 17:20

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Sat Sep 14, 2019 02:38

nichts ?

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

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Sat Sep 14, 2019 07:04

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Sat Sep 14, 2019 10:15

-.- 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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Mon Sep 16, 2019 02:52

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Fri Sep 20, 2019 04:23

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Mon Sep 23, 2019 03:14

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
Member
 
Posts: 28
Joined: Fri Sep 13, 2019 17:40
Location: Austria
GitHub: cHyper-0815OL
In-game: cHyper cHyper-0815OL

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

by cHyper-0815OL » Thu Sep 26, 2019 12:18

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

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

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

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

by Walker » Fri Sep 27, 2019 04:10

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
 

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Fri Sep 27, 2019 15:44

Wuzzy wrote:Version 0.54.1 released!

Changelog:
[list]
[*]Fix crash when fishing rod breaks

Du solltest drigend updaten.....
 

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

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

by Walker » Sat Sep 28, 2019 02:13

ok, ist eingespielt ^^

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

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

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

by Walker » Sat Sep 28, 2019 02:30

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Mon Sep 30, 2019 04:02

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: 418
Joined: Tue Oct 03, 2017 09:22
In-game: Walker

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

by Walker » Fri Oct 04, 2019 07:59

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
 

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Sat Oct 05, 2019 15:45

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
(391.75 KiB) Not downloaded yet
 

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Sat Oct 05, 2019 15:51

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...
 

jjk1
Member
 
Posts: 72
Joined: Sun Mar 10, 2019 17:00
In-game: jjk1

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

by jjk1 » Sat Oct 05, 2019 16:12

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
 

PreviousNext

Return to Servers



Who is online

Users browsing this forum: No registered users and 6 guests