Implement trading tier unlocking
[MineClone/MineClone2.git] / mods / ENTITIES / mobs_mc / villager.lua
blobf541260c7b9afd420992696a723f0e0a599146f9
1 --MCmobs v0.4
2 --maikerumine
3 --made for MC like Survival game
4 --License for code WTFPL and otherwise stated in readmes
6 -- TODO: Per-player trading inventories
7 -- TODO: Trade locking
9 -- intllib
10 local MP = minetest.get_modpath(minetest.get_current_modname())
11 local S, NS = dofile(MP.."/intllib.lua")
13 -- playername-indexed table containing the previously used tradenum
14 local player_tradenum = {}
15 -- playername-indexed table containing the objectref of trader, if trading formspec is open
16 local player_trading_with = {}
18 --###################
19 --################### VILLAGER
20 --###################
22 -- LIST OF VILLAGER PROFESSIONS AND TRADES
23 local E1 = { "mcl_core:emerald", 1, 1 } -- one emerald
24 local professions = {
25 farmer = {
26 name = "Farmer",
27 texture = "mobs_mc_villager_farmer.png",
28 trades = {
30 { { "mcl_farming:wheat_item", 18, 22, }, E1 },
31 { { "mcl_farming:potato_item", 15, 15, }, E1 },
32 { { "mcl_farming:carrot_item", 15, 19, }, E1 },
33 { E1, { "mcl_farming:bread", 2, 4 } },
37 { { "mcl_farming:pumpkin_face", 8, 13 }, E1 },
38 { E1, { "mcl_farming:pumpkin_pie", 2, 3} },
42 { { "mcl_farming:melon", 7, 12 }, E1 },
43 { E1, { "mcl_core:apple", 5, 7 }, },
47 { E1, { "mcl_farming:cookie", 6, 10 } },
48 { E1, { "mcl_cake:cake", 1, 1 } },
52 fisherman = {
53 name = "Fisherman",
54 texture = "mobs_mc_villager_farmer.png",
55 trades = {
57 { { "mcl_fishing:fish_raw", 6, 6, "mcl_core:emerald", 1, 1 }, { "mcl_fishing:fish_cooked", 6, 6 } },
58 { { "mcl_mobitems:string", 15, 20 }, E1 },
59 { { "mcl_core:coal_lump", 16, 24 }, E1 },
61 -- TODO: enchanted fishing rod
64 fletcher = {
65 name = "Fletcher",
66 texture = "mobs_mc_villager_farmer.png",
67 trades = {
69 { { "mcl_mobitems:string", 15, 20 }, E1 },
70 { E1, { "mcl_bows:arrow", 8, 12 } },
74 { { "mcl_core:gravel", 10, 10, "mcl_core:emerald", 1, 1 }, { "mcl_core:flint", 6, 10 } },
75 { { "mcl_core:emerald", 2, 3 }, { "mcl_bows:bow", 1, 1 } },
79 shepherd ={
80 name = "Shepherd",
81 texture = "mobs_mc_villager_farmer.png",
82 trades = {
84 { { "mcl_wool:white", 16, 22 }, E1 },
85 { { "mcl_core:emerald", 3, 4 }, { "mcl_tools:shears", 1, 1 } },
89 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:white", 1, 1 } },
90 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:grey", 1, 1 } },
91 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:silver", 1, 1 } },
92 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:yellow", 1, 1 } },
93 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:red", 1, 1 } },
94 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:purple", 1, 1 } },
95 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:blue", 1, 1 } },
96 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:light_blue", 1, 1 } },
97 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:brown", 1, 1 } },
98 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:lime", 1, 1 } },
99 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:green", 1, 1 } },
100 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:magenta", 1, 1 } },
101 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:black", 1, 1 } },
102 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:cyan", 1, 1 } },
103 { { "mcl_core:emerald", 1, 2 }, { "mcl_wool:pink", 1, 1 } },
107 librarian = {
108 name = "Librarian",
109 texture = "mobs_mc_villager_librarian.png",
110 trades = {
112 { { "mcl_core:paper", 24, 36 }, E1 },
113 -- TODO: enchanted book
114 { { "mcl_books:book", 8, 10 }, E1 },
115 { { "mcl_core:emerald", 10, 12 }, { "mcl_compass:compass", 1 ,1 }},
116 { { "mcl_core:emerald", 3, 4 }, { "mcl_books:bookshelf", 1 ,1 }},
120 { { "mcl_books:written_book", 2, 2 }, E1 },
121 { { "mcl_core:emerald", 10, 12 }, { "mcl_clock:clock", 1, 1 } },
122 { E1, { "mcl_core:glass", 3, 5 } },
126 { E1, { "mcl_core:glass", 3, 5 } },
129 -- TODO: 2 enchanted book tiers
132 { { "mcl_core:emerald", 20, 22 }, { "mcl_mobs:nametag", 1, 1 } },
136 cartographer = {
137 name = "Cartographer",
138 texture = "mobs_mc_villager_librarian.png",
139 trades = {
141 { { "mcl_core:paper", 24, 36 }, E1 },
144 -- {
145 -- TODO: compass
146 -- the difficulty lies in supporting the compass group, not the concrete item
147 -- { { "mcl_compass:compass", 1, 1 }, E1 },
148 -- },
151 -- TODO: replace with empty map
152 { { "mcl_core:emerald", 7, 11}, { "mcl_maps:filled_map", 1, 1 } },
155 -- TODO: special maps
158 armorer = {
159 name = "Armorer",
160 texture = "mobs_mc_villager_smith.png",
161 trades = {
163 { { "mcl_core:coal_lump", 16, 24 }, E1 },
164 { { "mcl_core:emerald", 6, 8 }, { "3d_armor:helmet_iron", 1, 1 } },
168 { { "mcl_core:iron_ingot", 7, 9 }, E1 },
169 { { "mcl_core:emerald", 10, 14 }, { "3d_armor:chestplate_iron", 1, 1 } },
173 { { "mcl_core:diamond", 3, 4 }, E1 },
174 -- TODO: enchant
175 { { "mcl_core:emerald", 16, 19 }, { "3d_armor:chestplate_diamond", 1, 1 } },
179 { { "mcl_core:emerald", 5, 7 }, { "3d_armor:boots_chain", 1, 1 } },
180 { { "mcl_core:emerald", 9, 11 }, { "3d_armor:leggings_chain", 1, 1 } },
181 { { "mcl_core:emerald", 5, 7 }, { "3d_armor:helmet_chain", 1, 1 } },
182 { { "mcl_core:emerald", 11, 15 }, { "3d_armor:chestplate_chain", 1, 1 } },
186 leatherworker = {
187 name = "Leatherworker",
188 texture = "mobs_mc_villager_butcher.png",
189 trades = {
191 { { "mcl_mobitems:leather", 9, 12 }, E1 },
192 { { "mcl_core:emerald", 2, 4 }, { "3d_armor:leggings_leather", 2, 4 } },
196 -- TODO: enchant
197 { { "mcl_core:emerald", 7, 12 }, { "3d_armor:chestplate_leather", 1, 1 } },
201 { { "mcl_core:emerald", 8, 10 }, { "mcl_mobitems:saddle", 1, 1 } },
205 butcher = {
206 name = "Butcher",
207 texture = "mobs_mc_villager_butcher.png",
208 trades = {
210 { { "mcl_mobitems:beef", 14, 18 }, E1 },
211 { { "mcl_mobitems:chicken", 14, 18 }, E1 },
215 { { "mcl_core:coal_lump", 16, 24 }, E1 },
216 { E1, { "mcl_mobitems:cooked_beef", 5, 7 } },
217 { E1, { "mcl_mobitems:cooked_chicken", 6, 8 } },
221 weapon_smith = {
222 name = "Weapon Smith",
223 texture = "mobs_mc_villager_smith.png",
224 trades = {
226 { { "mcl_core:coal_lump", 16, 24 }, E1 },
227 { { "mcl_core:emerald", 6, 8 }, { "mcl_tools:axe_iron", 1, 1 } },
231 { { "mcl_core:iron_ingot", 7, 9 }, E1 },
232 -- TODO: enchant
233 { { "mcl_core:emerald", 9, 10 }, { "mcl_tools:sword_iron", 1, 1 } },
237 { { "mcl_core:diamond", 3, 4 }, E1 },
238 -- TODO: enchant
239 { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:sword_diamond", 1, 1 } },
240 -- TODO: enchant
241 { { "mcl_core:emerald", 9, 12 }, { "mcl_tools:axe_diamond", 1, 1 } },
245 tool_smith = {
246 name = "Tool Smith",
247 texture = "mobs_mc_villager_smith.png",
248 trades = {
250 { { "mcl_core:coal_lump", 16, 24 }, E1 },
251 -- TODO: enchant
252 { { "mcl_core:emerald", 5, 7 }, { "mcl_tools:shovel_iron", 1, 1 } },
256 { { "mcl_core:iron_ingot", 7, 9 }, E1 },
257 -- TODO: enchant
258 { { "mcl_core:emerald", 9, 11 }, { "mcl_tools:pick_iron", 1, 1 } },
262 { { "mcl_core:diamond", 3, 4 }, E1 },
263 -- TODO: enchant
264 { { "mcl_core:emerald", 12, 15 }, { "mcl_tools:pick_diamond", 1, 1 } },
268 cleric = {
269 name = "Cleric",
270 texture = "mobs_mc_villager_priest.png",
271 trades = {
273 { { "mcl_mobitems:rotten_flesh", 36, 40 }, E1 },
274 { { "mcl_core:gold_ingot", 8, 10 }, E1 },
278 { E1, { "mesecons:redstone", 1, 4 } },
279 { E1, { "mcl_dye:blue", 1, 2 } },
283 { E1, { "mcl_nether:glowstone", 1, 3 } },
284 { { "mcl_core:emerald", 4, 7 }, { "mcl_throwing:ender_pearl", 1, 1 } },
287 -- TODO: Bottle 'o enchanting
290 nitwit = {
291 name = "Nitwit",
292 texture = "mobs_mc_villager.png",
293 -- No trades for nitwit
294 trades = nil,
298 local profession_names = {}
299 for id, _ in pairs(professions) do
300 table.insert(profession_names, id)
303 local init_profession = function(self)
304 if not self._profession then
305 -- Select random profession from all professions with matching clothing
306 local texture = self.base_texture[1]
307 local matches = {}
308 for prof_id, prof in pairs(professions) do
309 if texture == prof.texture then
310 table.insert(matches, prof_id)
313 local p = math.random(1, #matches)
314 self._profession = matches[p]
316 if not self._max_trade_tier then
317 self._max_trade_tier = 1
321 local init_trades = function(self, inv)
322 local profession = professions[self._profession]
323 local trade_tiers = profession.trades
324 if trade_tiers == nil then
325 -- Empty trades
326 self._trades = false
327 return
330 local max_tier = #trade_tiers
331 local trades = {}
332 for tiernum=1, max_tier do
333 local tier = trade_tiers[tiernum]
334 for tradenum=1, #tier do
335 local trade = tier[tradenum]
336 local wanted1_item = trade[1][1]
337 local wanted1_count = math.random(trade[1][2], trade[1][3])
338 local offered_item = trade[2][1]
339 local offered_count = math.random(trade[2][2], trade[2][3])
341 local wanted = { wanted1_item .. " " ..wanted1_count }
342 if trade[1][4] then
343 local wanted2_item = trade[1][4]
344 local wanted2_count = math.random(trade[1][5], trade[1][6])
345 table.insert(wanted, wanted2_item .. " " ..wanted2_count)
348 table.insert(trades, {
349 wanted = wanted,
350 offered = offered_item .. " " .. offered_count,
351 tier = tiernum, -- tier of this trade
352 traded_once = false, -- true if trade was traded at least once
353 trade_counter = 0, -- how often the this trade was mate after the last time it got unlocked
354 locked = false, -- if this trade is locked. Locked trades can't be used
358 self._trades = minetest.serialize(trades)
361 local set_trade = function(trader, player, inv, concrete_tradenum)
362 local trades = minetest.deserialize(trader._trades)
363 if not trades then
364 init_trades(trader)
365 trades = minetest.deserialize(trader._trades)
366 if not trades then
367 minetest.log("error", "[mobs_mc] Failed to select villager trade!")
368 return
372 if concrete_tradenum > #trades then
373 concrete_tradenum = 1
374 player_tradenum[player:get_player_name()] = concrete_tradenum
375 elseif concrete_tradenum < 1 then
376 concrete_tradenum = #trades
377 player_tradenum[player:get_player_name()] = concrete_tradenum
379 local trade = trades[concrete_tradenum]
380 if trader._max_trade_tier < trade.tier then
381 concrete_tradenum = 1
382 player_tradenum[player:get_player_name()] = concrete_tradenum
383 trade = trades[concrete_tradenum]
385 inv:set_stack("wanted", 1, ItemStack(trade.wanted[1]))
386 inv:set_stack("offered", 1, ItemStack(trade.offered))
387 if trade.wanted[2] then
388 local wanted2 = ItemStack(trade.wanted[2])
389 inv:set_stack("wanted", 2, wanted2)
390 else
391 inv:set_stack("wanted", 2, "")
396 local function show_trade_formspec(playername, trader, is_disabled)
397 local profession = professions[trader._profession].name
398 local disabled = ""
399 if is_disabled then
400 disabled = "image[4.3,2.52;1,1;mobs_mc_trading_formspec_disabled.png]"
402 local formspec =
403 "size[9,8.75]"..
404 "background[-0.19,-0.25;9.41,9.49;mobs_mc_trading_formspec_bg.png]"..
405 disabled..
406 mcl_vars.inventory_header..
407 "label[4,0;"..minetest.formspec_escape(profession).."]"
408 .."list[current_player;main;0,4.5;9,3;9]"
409 .."list[current_player;main;0,7.74;9,1;]"
410 .."button[1,1;0.5,1;prev_trade;<]"
411 .."button[7.26,1;0.5,1;next_trade;>]"
412 .."list[detached:mobs_mc:trade;wanted;2,1;2,1;]"
413 .."list[detached:mobs_mc:trade;offered;5.76,1;1,1;]"
414 .."list[detached:mobs_mc:trade;input;2,2.5;2,1;]"
415 .."list[detached:mobs_mc:trade;output;5.76,2.55;1,1;]"
416 .."listring[detached:mobs_mc:trade;output]"
417 .."listring[current_player;main]"
418 .."listring[detached:mobs_mc:trade;input]"
419 .."listring[current_player;main]"
420 minetest.sound_play("mobs_mc_villager_trade", {to_player = playername})
421 minetest.show_formspec(playername, "mobs_mc:trade", formspec)
424 local update_offer = function(inv, player, sound)
425 if inv:contains_item("input", inv:get_stack("wanted", 1)) and
426 (inv:get_stack("wanted", 2):is_empty() or inv:contains_item("input", inv:get_stack("wanted", 2))) then
427 inv:set_stack("output", 1, inv:get_stack("offered", 1))
428 if sound then
429 minetest.sound_play("mobs_mc_villager_accept", {to_player = player:get_player_name()})
431 return true
432 else
433 inv:set_stack("output", 1, ItemStack(""))
434 if sound then
435 minetest.sound_play("mobs_mc_villager_deny", {to_player = player:get_player_name()})
437 return false
441 mobs:register_mob("mobs_mc:villager", {
442 type = "npc",
443 hp_min = 20,
444 hp_max = 20,
445 collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
446 visual = "mesh",
447 mesh = "mobs_mc_villager.b3d",
448 textures = {
450 "mobs_mc_villager.png",
451 "mobs_mc_villager.png", --hat
454 "mobs_mc_villager_farmer.png",
455 "mobs_mc_villager_farmer.png", --hat
458 "mobs_mc_villager_priest.png",
459 "mobs_mc_villager_priest.png", --hat
462 "mobs_mc_villager_librarian.png",
463 "mobs_mc_villager_librarian.png", --hat
466 "mobs_mc_villager_butcher.png",
467 "mobs_mc_villager_butcher.png", --hat
470 "mobs_mc_villager_smith.png",
471 "mobs_mc_villager_smith.png", --hat
474 visual_size = {x=3, y=3},
475 makes_footstep_sound = true,
476 walk_velocity = 1.2,
477 run_velocity = 2.4,
478 drops = {},
479 sounds = {
480 random = "mobs_mc_villager_noise",
481 death = "mobs_mc_villager_death",
482 damage = "mobs_mc_villager_damage",
483 distance = 16,
485 animation = {
486 stand_speed = 25,
487 stand_start = 40,
488 stand_end = 59,
489 walk_speed = 25,
490 walk_start = 0,
491 walk_end = 40,
492 run_speed = 25,
493 run_start = 0,
494 run_end = 40,
495 die_speed = 15,
496 die_start = 210,
497 die_end = 220,
498 die_loop = false,
500 water_damage = 0,
501 lava_damage = 4,
502 light_damage = 0,
503 view_range = 16,
504 fear_height = 4,
505 on_rightclick = function(self, clicker)
506 local name = clicker:get_player_name()
508 init_profession(self)
509 if self._trades == nil then
510 init_trades(self)
512 if self._trades == false then
513 -- Villager has no trades, rightclick is a no-op
514 return
517 player_trading_with[name] = self
519 -- TODO: Create per-player trading inventories
520 local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade"})
521 if not inv then
522 inv = minetest.create_detached_inventory("mobs_mc:trade", {
523 allow_take = function(inv, listname, index, stack, player)
524 if listname == "input" then
525 return stack:get_count()
526 elseif listname == "output" then
527 -- Only allow taking full stack
528 local count = stack:get_count()
529 if count == inv:get_stack(listname, index):get_count() then
530 -- Also update output stack again.
531 -- If input has double the wanted items, the
532 -- output will stay because there will be still
533 -- enough items in input after the trade
534 local wanted1 = inv:get_stack("wanted", 1)
535 local wanted2 = inv:get_stack("wanted", 2)
536 wanted1:set_count(wanted1:get_count()*2)
537 wanted2:set_count(wanted2:get_count()*2)
538 if inv:contains_item("input", wanted1) and
539 (wanted2:is_empty() or inv:contains_item("input", wanted2)) then
540 return -1
541 else
542 -- If less than double the wanted items,
543 -- remove items from output (final trade,
544 -- input runs empty)
545 return count
547 else
548 return 0
550 else
551 return 0
553 end,
554 allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
555 if from_list == "input" and to_list == "input" then
556 return count
557 elseif from_list == "output" and to_list == "input" then
558 local move_stack = inv:get_stack(from_list, from_index)
559 if inv:get_stack(to_list, to_index):item_fits(move_stack) then
560 return count
563 return 0
564 end,
565 allow_put = function(inv, listname, index, stack, player)
566 if listname == "input" then
567 return stack:get_count()
568 else
569 return 0
571 end,
572 on_put = function(inv, listname, index, stack, player)
573 update_offer(inv, player, true)
574 end,
575 on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
576 if from_list == "output" and to_list == "input" then
577 inv:remove_item("input", inv:get_stack("wanted", 1))
578 local wanted2 = inv:get_stack("wanted", 2)
579 if not wanted2:is_empty() then
580 inv:remove_item("input", inv:get_stack("wanted", 2))
582 minetest.sound_play("mobs_mc_villager_accept", {to_player = player:get_player_name()})
584 update_offer(inv, player, true)
585 end,
586 on_take = function(inv, listname, index, stack, player)
587 local accept
588 local name = player:get_player_name()
589 if listname == "output" then
590 inv:remove_item("input", inv:get_stack("wanted", 1))
591 local wanted2 = inv:get_stack("wanted", 2)
592 if not wanted2:is_empty() then
593 inv:remove_item("input", inv:get_stack("wanted", 2))
595 local trader = player_trading_with[name]
596 local tradenum = player_tradenum[name]
597 local trades
598 if trader and trader._trades then
599 trades = minetest.deserialize(trader._trades)
601 if trades then
602 local trade = trades[tradenum]
603 local unlock_stuff = false
604 if not trade.traded_once then
605 unlock_stuff = true
606 trade.traded_once = true
607 elseif math.random(1,5) then
608 -- Otherwise, 20% chance to unlock all trades
609 unlock_stuff = true
611 if unlock_stuff then
612 -- First-time trade unlock all trades and unlock next trade tier
613 if trade.tier + 1 > trader._max_trade_tier then
614 trader._max_trade_tier = trader._max_trade_tier + 1
616 for t=1, #trades do
617 trades[t].locked = false
618 trades[t].trade_counter = 0
621 trade.trade_counter = trade.trade_counter + 1
622 if trade.trade_counter >= 12 then
623 trade.locked = true
624 elseif trade.trade_counter >= 2 then
625 math.random(1, math.random(1, 20))
626 trade.locked = true
628 else
629 minetest.log("error", "[mobs_mc] Player took item from trader output but player_trading_with or player_tradenum is nil!")
631 accept = true
632 elseif listname == "input" then
633 update_offer(inv, player, false)
635 if accept then
636 minetest.sound_play("mobs_mc_villager_accept", {to_player = name})
637 else
638 minetest.sound_play("mobs_mc_villager_deny", {to_player = name})
640 end,
643 inv:set_size("input", 2)
644 inv:set_size("output", 1)
645 inv:set_size("wanted", 2)
646 inv:set_size("offered", 1)
648 player_tradenum[name] = 1
649 set_trade(self, clicker, inv, player_tradenum[name])
651 show_trade_formspec(name, self)
652 end,
654 on_spawn = function(self)
655 init_profession(self)
656 end,
659 -- Returns a single itemstack in the given inventory to the player's main inventory, or drop it when there's no space left
660 local function return_item(itemstack, dropper, pos, inv_p)
661 if dropper:is_player() then
662 -- Return to main inventory
663 if inv_p:room_for_item("main", itemstack) then
664 inv_p:add_item("main", itemstack)
665 else
666 -- Drop item on the ground
667 local v = dropper:get_look_dir()
668 local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
669 p.x = p.x+(math.random(1,3)*0.2)
670 p.z = p.z+(math.random(1,3)*0.2)
671 local obj = minetest.add_item(p, itemstack)
672 if obj then
673 v.x = v.x*4
674 v.y = v.y*4 + 2
675 v.z = v.z*4
676 obj:setvelocity(v)
677 obj:get_luaentity()._insta_collect = false
680 else
681 -- Fallback for unexpected cases
682 minetest.add_item(pos, itemstack)
684 return itemstack
687 local return_fields = function(player)
688 local inv_t = minetest.get_inventory({type="detached", name = "mobs_mc:trade"})
689 local inv_p = player:get_inventory()
690 for i=1, inv_t:get_size("input") do
691 local stack = inv_t:get_stack("input", i)
692 return_item(stack, player, player:get_pos(), inv_p)
693 stack:clear()
694 inv_t:set_stack("input", i, stack)
696 inv_t:set_stack("output", 1, "")
699 minetest.register_on_player_receive_fields(function(player, formname, fields)
700 if formname == "mobs_mc:trade" then
701 local name = player:get_player_name()
702 if fields.quit then
703 return_fields(player)
704 player_trading_with[name] = nil
705 elseif fields.next_trade then
706 local trader = player_trading_with[name]
707 if not trader or not trader.object:get_luaentity() then
708 return
710 local trades = trader._trades
711 if not trades then
712 return
714 player_tradenum[name] = player_tradenum[name] + 1
715 local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade"})
716 set_trade(trader, player, inv, player_tradenum[name])
717 update_offer(inv, player, false)
718 show_trade_formspec(name, trader)
719 elseif fields.prev_trade then
720 local trader = player_trading_with[name]
721 if not trader or not trader.object:get_luaentity() then
722 return
724 local trades = trader._trades
725 if not trades then
726 return
728 player_tradenum[name] = player_tradenum[name] - 1
729 local inv = minetest.get_inventory({type="detached", name="mobs_mc:trade"})
730 set_trade(trader, player, inv, player_tradenum[name])
731 update_offer(inv, player, false)
732 show_trade_formspec(name, trader)
735 end)
737 minetest.register_on_leaveplayer(function(player)
738 return_fields(player)
739 player_tradenum[player:get_player_name()] = nil
740 player_trading_with[player:get_player_name()] = nil
741 end)
743 minetest.register_on_joinplayer(function(player)
744 player_tradenum[player:get_player_name()] = 1
745 player_trading_with[player:get_player_name()] = nil
746 end)
748 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)
750 -- compatibility
751 mobs:alias_mob("mobs:villager", "mobs_mc:villager")
753 -- spawn eggs
754 mobs:register_egg("mobs_mc:villager", S("Villager"), "mobs_mc_spawn_icon_villager.png", 0)
756 if minetest.settings:get_bool("log_mods") then
757 minetest.log("action", "MC mobs loaded")