[Server v5.8.0] German-MineClone2-server ( german )
Re: [Server] [v5.0.0] German-MineClone2-server
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 !
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 !
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
oh. interessant. Aber bevor ich das neue Recht benutze, übe ich lieber noch im singleplayer ;)
Re: [Server] [v5.0.0] German-MineClone2-server
Darf ich dich mal wieder korrigieren?: IHM ;)Walker wrote: geht ihr also bitte nicht auf den Sack !
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-09_04:01:07.tgz
Größe: 2.270.399.032 Bytes
Datum: 09.09.2019
Zeit: 04:01:07
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-13_06:03:36.tgz
Größe: 2.274.563.635 Bytes
Datum: 13.09.2019
Zeit: 06:03:36
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
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
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
WICHTIG:
jjk1 ist nun auf ALLEN German-*-servern von mir Moderator !
behandelt sie ... ... ... IHN also mit respekt ;)
jjk1 ist nun auf ALLEN German-*-servern von mir Moderator !
behandelt sie ... ... ... IHN also mit respekt ;)
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
Da hat mal wieder jemand den creativ server vermüllt. Was sagt der log zu dem Block -11 16 -148?
Re: [Server] [v5.0.0] German-MineClone2-server
nichts ?
ansonsten gehört der Bereich Exo und Rox, da dürfte also keiner, außer die beide, bauen können
ansonsten gehört der Bereich Exo und Rox, da dürfte also keiner, außer die beide, bauen können
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
Ich meinte den Kreativ Server ;)jjk1 wrote:Da hat mal wieder jemand den creativ server vermüllt. Was sagt der log zu dem Block -11 16 -148?
Re: [Server] [v5.0.0] German-MineClone2-server
-.- 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 ^^:
zolder ^^
^^
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)
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-16_04:01:31.tgz
Größe: 2.307.856.360 Bytes
Datum: 16.09.2019
Zeit: 04:01:31
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-20_06:06:43.tgz
Größe: 2.262.190.405 Bytes
Datum: 20.09.2019
Zeit: 06:06:43
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-23_04:01:22.tgz
Größe: 2.393.563.252 Bytes
Datum: 23.09.2019
Zeit: 04:01:22
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
Gut, dass du regelmaessig Updates machst, da kann nix schiefgehen.
Re: [Server] [v5.0.0] German-MineClone2-server
^^
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-27_06:00:18.tgz
Größe: 2.461.961.433 Bytes
Datum: 27.09.2019
Zeit: 06:00:18
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
Du solltest drigend updaten.....Wuzzy wrote:Version 0.54.1 released!
Changelog:
- Fix crash when fishing rod breaks
Re: [Server] [v5.0.0] German-MineClone2-server
ok, ist eingespielt ^^
hoffentlich ist es nicht wieder ein ... HOT-FIX xDDDDD
hoffentlich ist es nicht wieder ein ... HOT-FIX xDDDDD
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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ä
was´n da los ?
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-09-30_05:49:43.tgz
Größe: 2.493.521.433 Bytes
Datum: 30.09.2019
Zeit: 05:49:43
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
File: Minetest_2019-10-04_04:04:12.tgz
Größe: 2.504.964.663 Bytes
Datum: 04.10.2019
Zeit: 04:04:12
My YouTube-Site with Minetest-LetsPlays and some of my Servers: Carpathian Ethereal Vanilla Creative MineClone2 --- Discord
Re: [Server] [v5.0.0] German-MineClone2-server
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
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.
Re: [Server] [v5.0.0] German-MineClone2-server
Ups. es war noch ein kleiner Fehler im Code. Dieser Code hier funktioniert aber jetzt:
EDIT: Dieser Code funktioniert ja auch nicht. Ich setz mich nochmal ran...
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
Re: [Server] [v5.0.0] German-MineClone2-server
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
Who is online
Users browsing this forum: No registered users and 0 guests