Merge pull request 'Villager employment system and mob-api enhancements' (#2209) from villagers_jobsites into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2209
This commit is contained in:
cora 2022-05-22 13:22:41 +00:00
commit fc428da6fd
34 changed files with 903 additions and 592 deletions

@ -1438,6 +1438,7 @@ end
-- should mob follow what I'm holding ?
local follow_holding = function(self, clicker)
if self.nofollow then return false end
if mobs.invis[clicker:get_player_name()] then
return false
@ -2317,17 +2318,50 @@ local dogswitch = function(self, dtime)
return self.dogshoot_switch
end
local function go_to_pos(entity,b)
if not entity then return end
local s=entity.object:get_pos()
if vector.distance(b,s) < 1 then
--set_velocity(entity,0)
return true
end
local v = { x = b.x - s.x, z = b.z - s.z }
local yaw = (math.atan(v.z / v.x) + math.pi / 2) - entity.rotate
if b.x > s.x then yaw = yaw + math.pi end
entity.object:set_yaw(yaw)
set_velocity(entity,entity.follow_velocity)
mobs:set_animation(entity, "walk")
end
local function check_doors(self)
local p = self.object:get_pos()
local t = minetest.get_timeofday()
local dd = minetest.find_nodes_in_area(vector.offset(p,-1,-1,-1),vector.offset(p,1,1,1),{"group:door"})
for _,d in pairs(dd) do
local n = minetest.get_node(d)
if n.name:find("_b_") then
local def = minetest.registered_nodes[n.name]
local closed = n.name:find("_b_1")
if t < 0.3 or t > 0.8 then
if not closed then def.on_rightclick(d,n,self) end
else
if closed then def.on_rightclick(d,n,self) end
end
end
end
end
-- execute current state (stand, walk, run, attacks)
-- returns true if mob has died
local do_states = function(self, dtime)
if self.can_open_doors then check_doors(self) end
local yaw = self.object:get_yaw() or 0
if self.state == "stand" then
if random(1, 4) == 1 then
local lp = nil
local s = self.object:get_pos()
local objs = minetest.get_objects_inside_radius(s, 3)
@ -2340,7 +2374,7 @@ local do_states = function(self, dtime)
end
-- look at any players nearby, otherwise turn randomly
if lp then
if self.look_at_players then
local vec = {
x = lp.x - s.x,
@ -2375,8 +2409,35 @@ local do_states = function(self, dtime)
end
end
elseif self.state == "walk" then
elseif self.state == "gowp" then
local p = self.object:get_pos()
if not p or not self._target then return end
if vector.distance(p,self._target) < 2 or ( self.waypoints and #self.waypoints == 0 ) then
self.waypoints = nil
self._target = nil
self.current_target = nil
self.state = "walk"
if self.callback_arrived then return self.callback_arrived(self) end
return true
end
if self.waypoints and ( not self.current_target or vector.distance(p,self.current_target) < 1.5 ) then
self.current_target = table.remove(self.waypoints, 1)
--minetest.log("nextwp:".. tostring(self.current_target) )
elseif self.current_target then
go_to_pos(self,self.current_target)
end
if self.current_target and not minetest.line_of_sight(self.object:get_pos(),self.current_target) then
self.waypoints=minetest.find_path(p,self._target,150,1,4)
self.current_target = nil
return
end
if not self.current_target then
--minetest.log("no path")
self.state = "walk"
end
elseif self.state == "walk" then
local s = self.object:get_pos()
local lp = nil
@ -2880,6 +2941,62 @@ local do_states = function(self, dtime)
end
end
local plane_adjacents = {
vector.new(1,0,0),
vector.new(-1,0,0),
vector.new(0,0,1),
vector.new(0,0,-1),
}
function mobs:gopath(self,target,callback_arrived)
local p = self.object:get_pos()
local t = vector.offset(target,0,1,0)
local wp = minetest.find_path(p,t,150,1,4)
if not wp then
local d = minetest.find_node_near(target,16,{"group:door"})
if d then
for _,v in pairs(plane_adjacents) do
local pos = vector.add(d,v)
local n = minetest.get_node(pos)
if n.name == "air" then
wp = minetest.find_path(p,pos,150,1,4)
if wp then break end
end
end
end
end
if wp and #wp > 0 then
self._target = t
self.callback_arrived = callback_arrived
self.waypoints = wp
self.state = "gowp"
return true
else
--minetest.log("no path found")
end
end
local function player_near(pos)
for _,o in pairs(minetest.get_objects_inside_radius(pos,2)) do
if o:is_player() then return true end
end
end
local function check_item_pickup(self)
if self.pick_up and #self.pick_up > 0 then
local p = self.object:get_pos()
for _,o in pairs(minetest.get_objects_inside_radius(p,2)) do
local l=o:get_luaentity()
if l and l.name == "__builtin:item" then
for k,v in pairs(self.pick_up) do
if not player_near(p) and self.on_pick_up and l.itemstring:find(v) then
if self.on_pick_up(self,l) == nil then o:remove() end
end
end
end
end
end
end
-- falling and fall damage
-- returns true if mob died
@ -3481,7 +3598,7 @@ end
-- main mob function
local mob_step = function(self, dtime)
check_item_pickup(self)
if not self.fire_resistant then
mcl_burning.tick(self.object, dtime, self)
end
@ -3576,8 +3693,7 @@ local mob_step = function(self, dtime)
-- attack timer
self.timer = self.timer + dtime
if self.state ~= "attack" then
if self.state ~= "attack" and self.state ~= "gowp" then
if self.timer < 1 then
return
end
@ -3845,6 +3961,8 @@ minetest.register_entity(name, {
sounds = def.sounds or {},
animation = def.animation,
follow = def.follow,
nofollow = def.nofollow,
can_open_doors = def.can_open_doors,
jump = def.jump ~= false,
walk_chance = def.walk_chance or 50,
attacks_monsters = def.attacks_monsters or false,
@ -3911,6 +4029,7 @@ minetest.register_entity(name, {
texture_mods = {},
shoot_arrow = def.shoot_arrow,
sounds_child = def.sounds_child,
pick_up = def.pick_up,
explosion_strength = def.explosion_strength,
suffocation_timer = 0,
follow_velocity = def.follow_velocity or 2.4,
@ -3934,6 +4053,8 @@ minetest.register_entity(name, {
on_grown = def.on_grown,
on_pick_up = def.on_pick_up,
on_detach_child = mob_detach_child,
on_activate = function(self, staticdata, dtime)
@ -4250,7 +4371,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
end
-- can eat/tame with item in hand
if follow_holding(self, clicker) then
if self.nofollow or follow_holding(self, clicker) then
-- if not in creative then take item
if not mobs.is_creative(clicker:get_player_name()) then

@ -194,6 +194,7 @@ functions needed for the mob to work properly which contains the following:
'punch2' animations.
'animation' holds a table containing animation names and settings for use with mesh models:
{
'stand_start'start frame for when mob stands still.
'stand_end' end frame of stand animation.
'stand_speed'speed of animation in frames per second.
@ -219,6 +220,7 @@ functions needed for the mob to work properly which contains the following:
'die_end'
'die_speed'
'die_loop' when set to false stops the animation looping.
}
Using '_loop = false' setting will stop any of the above animations from
looping.
@ -237,7 +239,7 @@ functions needed for the mob to work properly which contains the following:
'rain_damage' damage per second if mob is standing in rain (default: 0)
'sunlight_damage' holds the damage per second inflicted to mobs when they
are in direct sunlight
'spawn_small_alternative': name of a smaller mob to use as replacement if
'spawn_small_alternative' name of a smaller mob to use as replacement if
spawning fails due to space requirements
'glow' same as in entity definition
'child' if true, spawn mob as child
@ -253,6 +255,12 @@ functions needed for the mob to work properly which contains the following:
'fire_resistant' If true, the mob can't burn
'fire_damage_resistant' If true the mob will not take damage when burning
'ignited_by_sunlight' If true the mod will burn at daytime. (Takes sunlight_damage per second)
'nofollow' Do not follow players when they wield the "follow" item. For mobs (like villagers)
that are bred in a different way.
'pick_up' table of itemstrings the mob will pick up (e.g. for breeding)
'on_pick_up' function that will be called on item pickup - return true to not pickup the item
mobs:gopath(self,target,callback_arrived) pathfind a way to target and run callback on arrival

@ -169,7 +169,7 @@ mobs_mc.follow = {
dog = { mobs_mc.items.rabbit_raw, mobs_mc.items.rabbit_cooked, mobs_mc.items.mutton_raw, mobs_mc.items.mutton_cooked, mobs_mc.items.beef_raw, mobs_mc.items.beef_cooked, mobs_mc.items.chicken_raw, mobs_mc.items.chicken_cooked, mobs_mc.items.rotten_flesh,
-- Mobs Redo items
"mobs:meat", "mobs:meat_raw" },
villager = { "mcl_farming:bread" },
villager = { "mcl_farming:bread", "mcl_farming:carrot_item", "mcl_farming:beetroot_item" , "mcl_farming:potato_item" },
}
-- Contents for replace_what

@ -9,7 +9,7 @@ local S = minetest.get_translator("mobs_mc")
--################### IRON GOLEM
--###################
local etime = 0
mobs:register_mob("mobs_mc:iron_golem", {
description = S("Iron Golem"),
@ -41,6 +41,26 @@ mobs:register_mob("mobs_mc:iron_golem", {
group_attack = true,
attacks_monsters = true,
attack_type = "dogfight",
_got_poppy = false,
pick_up = {"mcl_flowers:poppy"},
on_pick_up = function(self,n)
if n.itemstring:find("mcl_flowers:poppy") then
if not self._got_poppy then
self._got_poppy=true
return
end
return true
end
end,
replace_what = {"mcl_flowers:poppy"},
replace_with = {"air"},
on_replace = function(self, pos, oldnode, newnode)
if not self.got_poppy and oldnode.name == "mcl_flowers:poppy" then
self._got_poppy=true
return
end
return false
end,
drops = {
{name = mobs_mc.items.iron_ingot,
chance = 1,
@ -60,6 +80,14 @@ mobs:register_mob("mobs_mc:iron_golem", {
punch_start = 40, punch_end = 50,
},
jump = true,
on_step = function(self,dtime)
etime = etime + dtime
if etime > 10 then
if self._home and vector.distance(self._home,self.object:get_pos()) > 50 then
mobs:gopath(self,self._home)
end
end
end,
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

@ -10,14 +10,10 @@
-- 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
-- TODO: Internal inventory, trade with other villagers
-- TODO: Schedule stuff (work,sleep,father)
local S = minetest.get_translator("mobs_mc")
local N = function(s) return s end
@ -61,15 +57,37 @@ if minetest.get_mapgen_setting("mg_name") == "v6" then
TRADE_V6_BIRCH_SAPLING = { { "mcl_core:emerald", 8, 11 }, { "mcl_core:birchsapling", 1, 1 } }
end
local tiernames = {
"Novice",
"Apprentice",
"Journeyman",
"Expert",
"Master",
}
local badges = {
"default_wood.png",
"default_steel_block.png",
"default_gold_block.png",
"mcl_core_emerald_block.png",
"default_diamond_block.png",
}
local professions = {
unemployed = {
name = N("Unemployed"),
texture = "mobs_mc_villager.png",
textures = {
"mobs_mc_villager.png",
"mobs_mc_villager.png",
},
trades = nil,
},
farmer = {
name = N("Farmer"),
texture = "mobs_mc_villager_farmer.png",
textures = {
"mobs_mc_villager_farmer.png",
"mobs_mc_villager_farmer.png",
},
jobsite = "mcl_composters:composter",
trades = {
{
@ -103,7 +121,10 @@ local professions = {
},
fisherman = {
name = N("Fisherman"),
texture = "mobs_mc_villager_farmer.png",
textures = {
"mobs_mc_villager_fisherman.png",
"mobs_mc_villager_fisherman.png",
},
jobsite = "mcl_barrels:barrel_closed",
trades = {
{
@ -138,7 +159,10 @@ local professions = {
},
fletcher = {
name = N("Fletcher"),
texture = "mobs_mc_villager_farmer.png",
textures = {
"mobs_mc_villager_fletcher.png",
"mobs_mc_villager_fletcher.png",
},
jobsite = "mcl_fletching_table:fletching_table",
trades = {
{
@ -177,7 +201,10 @@ local professions = {
},
shepherd ={
name = N("Shepherd"),
texture = "mobs_mc_villager_farmer.png",
textures = {
"mobs_mc_villager_sheperd.png",
"mobs_mc_villager_sheperd.png",
},
jobsite = "mcl_loom:loom",
trades = {
{
@ -207,8 +234,11 @@ local professions = {
},
librarian = {
name = N("Librarian"),
texture = "mobs_mc_villager_librarian.png",
jobsite = "mcl_villages:stonebrickcarved", --FIXME: lectern
textures = {
"mobs_mc_villager_librarian.png",
"mobs_mc_villager_librarian.png",
},
jobsite = "mcl_books:bookshelf", --FIXME: lectern
trades = {
{
{ { "mcl_core:paper", 24, 36 }, E1 },
@ -242,7 +272,10 @@ local professions = {
},
cartographer = {
name = N("Cartographer"),
texture = "mobs_mc_villager_librarian.png",
textures = {
"mobs_mc_villager_cartographer.png",
"mobs_mc_villager_cartographer.png",
},
jobsite = "mcl_cartography_table:cartography_table",
trades = {
{
@ -285,7 +318,10 @@ local professions = {
},
armorer = {
name = N("Armorer"),
texture = "mobs_mc_villager_smith.png",
textures = {
"mobs_mc_villager_armorer.png",
"mobs_mc_villager_armorer.png",
},
jobsite = "mcl_blast_furnace:blast_furnace",
trades = {
{
@ -298,7 +334,7 @@ local professions = {
{
{ { "mcl_core:iron_ingot", 4, 4 }, E1 },
--{ { "mcl_core:emerald", 36, 36 }, { "FIXME: Bell", 1, 1 } },
{ { "mcl_core:emerald", 36, 36 }, { "mcl_bells:bell", 1, 1 } },
{ { "mcl_core:emerald", 3, 3 }, { "mcl_armor:leggings_chain", 1, 1 } },
{ { "mcl_core:emerald", 1, 1 }, { "mcl_armor:boots_chain", 1, 1 } },
},
@ -322,7 +358,10 @@ local professions = {
},
leatherworker = {
name = N("Leatherworker"),
texture = "mobs_mc_villager_butcher.png",
textures = {
"mobs_mc_villager_leatherworker.png",
"mobs_mc_villager_leatherworker.png",
},
jobsite = "mcl_cauldrons:cauldron",
trades = {
{
@ -351,7 +390,10 @@ local professions = {
},
butcher = {
name = N("Butcher"),
texture = "mobs_mc_villager_butcher.png",
textures = {
"mobs_mc_villager_butcher.png",
"mobs_mc_villager_butcher.png",
},
jobsite = "mcl_smoker:smoker",
trades = {
{
@ -381,8 +423,11 @@ local professions = {
},
weapon_smith = {
name = N("Weapon Smith"),
texture = "mobs_mc_villager_smith.png",
jobsite = "mcl_villages:stonebrickcarved", --FIXME: grindstone
textures = {
"mobs_mc_villager_weaponsmith.png",
"mobs_mc_villager_weaponsmith.png",
},
jobsite = "mcl_furnaces:furnace", --FIXME: grindstone
trades = {
{
{ { "mcl_core:coal_lump", 15, 15 }, E1 },
@ -392,7 +437,7 @@ local professions = {
{
{ { "mcl_core:iron_ingot", 4, 4 }, E1 },
--{ { "mcl_core:emerald", 36, 36 }, { "FIXME: Bell", 1, 1 } },
{ { "mcl_core:emerald", 36, 36 }, { "mcl_bells:bell", 1, 1 } },
},
{
{ { "mcl_core:flint", 7, 9 }, E1 },
@ -409,8 +454,11 @@ local professions = {
},
tool_smith = {
name = N("Tool Smith"),
texture = "mobs_mc_villager_smith.png",
jobsite = "mcl_villages:stonebrickcarved", --FIXME: smithing table
textures = {
"mobs_mc_villager_toolsmith.png",
"mobs_mc_villager_toolsmith.png",
},
jobsite = "mcl_anvils:anvil", --FIXME: smithing table
trades = {
{
{ { "mcl_core:coal_lump", 15, 15 }, E1 },
@ -422,7 +470,7 @@ local professions = {
{
{ { "mcl_core:iron_ingot", 4, 4 }, E1 },
--{ { "mcl_core:emerald", 36, 36 }, { "FIXME: Bell", 1, 1 } },
{ { "mcl_core:emerald", 36, 36 }, { "mcl_bells:bell", 1, 1 } },
},
{
{ { "mcl_core:flint", 30, 30 }, E1 },
@ -443,8 +491,11 @@ local professions = {
},
cleric = {
name = N("Cleric"),
texture = "mobs_mc_villager_priest.png",
jobsite = "mcl_brewing:stand",
textures = {
"mobs_mc_villager_priest.png",
"mobs_mc_villager_priest.png",
},
jobsite = "mcl_brewing:stand_000",
trades = {
{
{ { "mcl_mobitems:rotten_flesh", 32, 32 }, E1 },
@ -472,7 +523,10 @@ local professions = {
},
nitwit = {
name = N("Nitwit"),
texture = "mobs_mc_villager.png",
textures = {
"mobs_mc_villager_nitwit.png",
"mobs_mc_villager_nitwit.png",
},
-- No trades for nitwit
trades = nil,
}
@ -483,48 +537,106 @@ for id, _ in pairs(professions) do
table.insert(profession_names, id)
end
local stand_still = function(self)
local jobsites={}
for _,n in pairs(profession_names) do
table.insert(jobsites,professions[n].jobsite)
end
local function stand_still(self)
self.walk_chance = 0
self.jump = false
end
local function set_velocity(self, v)
local yaw = (self.object:get_yaw() or 0) + self.rotate
self.object:set_velocity({
x = (math.sin(yaw) * -v),
y = self.object:get_velocity().y,
z = (math.cos(yaw) * v),
})
local function init_trader_vars(self)
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 function go_to_pos(entity,b)
local s=entity.object:get_pos()
local v = { x = b.x - s.x, z = b.z - s.z }
local yaw = (math.atan(v.z / v.x) + math.pi / 2) - entity.rotate
if b.x > s.x then yaw = yaw + math.pi end
entity.object:set_yaw(yaw)
set_velocity(entity,entity.follow_velocity)
if vector.distance(b,s) < 5 then
return true
local function get_badge_textures(self)
local t = professions[self._profession].textures
if self._profession == "unemployed" or self._profession == "nitwit" then return t end
local tier = self._max_trade_tier or 1
return {
"[combine:64x64:0,0="..t[1]..":11,55=".. badges[tier].."\\^[resize\\:2x2",
t[2]
}
end
local function set_textures(self)
self.object:set_properties({textures=get_badge_textures(self)})
end
local function go_home(entity)
entity.state = "go_home"
local b=entity.bed
local b=entity._bed
if not b then return end
if go_to_pos(entity,b) then
mobs:gopath(entity,b,function(entity,b)
if vector.distance(entity.object:get_pos(),b) < 2 then
entity.state = "stand"
set_velocity(entity,0)
entity.object:set_pos(b)
local n=minetest.get_node(b)
if n and n.name ~= "mcl_beds:bed_red_bottom" then
entity.bed=nil --the stormtroopers have killed uncle owen
entity._bed=nil --the stormtroopers have killed uncle owen
return false
end
return true
end
end)
end
----- JOBSITE LOGIC
local function get_profession_by_jobsite(js)
for k,v in pairs(professions) do
if v.jobsite == js then return k end
end
end
local function employ(self,jobsite_pos)
local n = minetest.get_node(jobsite_pos)
local m = minetest.get_meta(jobsite_pos)
local p = get_profession_by_jobsite(n.name)
if p and m:get_string("villager") == "" then
self._profession=p
m:set_string("villager",self._id)
self._jobsite = jobsite_pos
set_textures(self)
return true
end
end
local function look_for_job(self)
local p = self.object:get_pos()
local nn = minetest.find_nodes_in_area(vector.offset(p,-48,-48,-48),vector.offset(p,48,48,48),jobsites)
for _,n in pairs(nn) do
local m=minetest.get_meta(n)
if m:get_string("villager") == "" then
--minetest.log("goingt to jobsite "..minetest.pos_to_string(n) )
local gp = mobs:gopath(self,n,function()
--minetest.log("arrived jobsite "..minetest.pos_to_string(n) )
end)
if gp then return end
end
end
end
local update_max_tradenum = function(self)
local function get_a_job(self)
local p = self.object:get_pos()
local nn = minetest.find_nodes_in_area(vector.offset(p,-8,-8,-8),vector.offset(p,8,8,8),jobsites)
for _,n in pairs(nn) do
if n and employ(self,n) then return true end
end
if self.state ~= "gowp" then look_for_job(self) end
end
local function update_max_tradenum(self)
if not self._trades then
return
end
@ -539,31 +651,7 @@ local update_max_tradenum = function(self)
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 function init_trades(self, inv)
local profession = professions[self._profession]
local trade_tiers = profession.trades
if trade_tiers == nil then
@ -614,7 +702,7 @@ local init_trades = function(self, inv)
minetest.deserialize(self._trades)
end
local set_trade = function(trader, player, inv, concrete_tradenum)
local function set_trade(trader, player, inv, concrete_tradenum)
local trades = minetest.deserialize(trader._trades)
if not trades then
init_trades(trader)
@ -688,12 +776,17 @@ local function show_trade_formspec(playername, trader, tradenum)
w2_formspec = "item_image[3,1;1,1;"..wanted2:to_string().."]"
.."tooltip[3,1;0.8,0.8;"..F(wanted2:get_description()).."]"
end
local tiername = tiernames[trader._max_trade_tier]
if tiername then
tiername = S(tiername)
else
tiername = S("Master")
end
local formspec =
"size[9,8.75]"
.."background[-0.19,-0.25;9.41,9.49;mobs_mc_trading_formspec_bg.png]"
..disabled_img
.."label[4,0;"..F(minetest.colorize("#313131", S(profession))).."]"
.."label[3,0;"..F(minetest.colorize("#313131", S(profession).." - "..tiername)) .."]"
.."list[current_player;main;0,4.5;9,3;9]"
.."list[current_player;main;0,7.74;9,1;]"
..b_prev..b_next
@ -713,7 +806,7 @@ local function show_trade_formspec(playername, trader, tradenum)
minetest.show_formspec(playername, tradeinv_name, formspec)
end
local update_offer = function(inv, player, sound)
local function update_offer(inv, player, sound)
local name = player:get_player_name()
local trader = player_trading_with[name]
local tradenum = player_tradenum[name]
@ -737,12 +830,12 @@ local update_offer = function(inv, player, sound)
-- 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)
local function check_special(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)
local function check_input(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
@ -757,7 +850,7 @@ local update_offer = function(inv, player, sound)
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)
local function check_specials(wanted1, wanted2, input1, input2)
return check_special(COMPASS, "compass", wanted1, wanted2, input1, input2)
end
-- END OF SPECIAL HANDLING OF COMPASS
@ -811,7 +904,7 @@ local function return_item(itemstack, dropper, pos, inv_p)
return itemstack
end
local return_fields = function(player)
local function return_fields(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()
@ -877,7 +970,7 @@ minetest.register_on_leaveplayer(function(player)
end)
-- Return true if player is trading with villager, and the villager entity exists
local trader_exists = function(playername)
local function trader_exists(playername)
local trader = player_trading_with[playername]
return trader ~= nil and trader.object:get_luaentity() ~= nil
end
@ -904,7 +997,7 @@ local trade_inventory = {
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)
local function special_checks(wanted1, input1, input2)
if wanted1:get_name() == COMPASS then
local compasses = 0
if (minetest.get_item_group(input1:get_name(), "compass") ~= 0) then
@ -1021,6 +1114,10 @@ local trade_inventory = {
-- 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
if trader._max_trade_tier > 5 then
trader._max_trade_tier = 5
end
set_textures(trader)
update_max_tradenum(trader)
update_formspec = true
end
@ -1113,31 +1210,9 @@ mobs:register_mob("mobs_mc:villager", {
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=2.75, y=2.75},
makes_footstep_sound = true,
walk_velocity = 1.2,
@ -1165,23 +1240,45 @@ mobs:register_mob("mobs_mc:villager", {
die_loop = false,
},
follow = mobs_mc.follow.villager,
nofollow = true,
view_range = 16,
fear_height = 4,
jump = true,
walk_chance = DEFAULT_WALK_CHANCE,
on_rightclick = function(self, clicker)
if clicker:get_wielded_item():get_name() == "mcl_farming:bread" then
if mobs:feed_tame(self, clicker, 1, true, true) then return end
if mobs:protect(self, clicker) then return end
_bed = nil,
_id = nil,
_profession = "unemployed",
look_at_player = true,
pick_up = mobs_mc.follow.villager,
can_open_doors = true,
on_pick_up = function(self,itementity)
local clicker
for _,p in pairs(minetest.get_connected_players()) do
if vector.distance(p:get_pos(),self.object:get_pos()) < 10 then
clicker = p
end
if self.child then
end
if clicker then
mobs:feed_tame(self, clicker, 1, true, false)
return
end
return true --do not pick up
end,
on_rightclick = function(self, clicker)
local trg=vector.new(0,9,0)
if self._jobsite then
mobs:gopath(self,self._jobsite,function()
--minetest.log("arrived at jobsite")
end)
end
if self.child or self._profession == "unemployed" then
return
end
-- Initiate trading
init_trader_vars(self)
local name = clicker:get_player_name()
self._trading_players[name] = true
init_trader_vars(self)
if self._trades == nil then
init_trades(self)
end
@ -1219,10 +1316,6 @@ mobs:register_mob("mobs_mc:villager", {
self._player_scan_timer = 0
end
if self.bed and ( self.state == "go_home" or vector.distance(self.object:get_pos(),self.bed) > 50 ) then
go_home(self)
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
@ -1244,15 +1337,31 @@ mobs:register_mob("mobs_mc:villager", {
self.walk_chance = DEFAULT_WALK_CHANCE
self.jump = true
end
if self._bed and ( self.state ~= "go_home" and vector.distance(self.object:get_pos(),self._bed) > 50 ) then
go_home(self)
end
if self._profession == "unemployed" then
get_a_job(self)
end
end
end,
on_spawn = function(self)
init_trader_vars(self)
if self._id then
set_textures(self)
return
end
self._id=minetest.sha1(minetest.get_gametime()..minetest.pos_to_string(self.object:get_pos())..tostring(math.random()))
self._profession = "unemployed"
if math.random(100) == 1 then
self._profession = "nitwit"
end
set_textures(self)
end,
on_die = function(self, pos)
-- Close open trade formspecs and give input back to players
local trading_players = self._trading_players
if trading_players then
for name, _ in pairs(trading_players) do
minetest.close_formspec(name, "mobs_mc:trade_"..name)
local player = minetest.get_player_by_name(name)
@ -1260,6 +1369,7 @@ mobs:register_mob("mobs_mc:villager", {
return_fields(player)
end
end
end
end,
})

@ -5,7 +5,13 @@ mcl_bells = {}
local has_mcl_wip = minetest.get_modpath("mcl_wip")
function mcl_bells.ring_once(pos)
minetest.sound_play( "mcl_bells_bell_stroke", { pos = pos, gain = 1.5, max_hear_distance = 300,});
minetest.sound_play( "mcl_bells_bell_stroke", { pos = pos, gain = 1.5, max_hear_distance = 150,})
local vv=minetest.get_objects_inside_radius(pos,150)
for _,o in pairs(vv) do
if o.type == "npc" then
mobs:gopath(o:get_luaentity(),pos,function() end)
end
end
end
minetest.register_node("mcl_bells:bell", {

@ -188,11 +188,41 @@ local function construct_node(p1, p2, name)
end
minetest.log("warning", "[mcl_villages] Attempt to 'construct' inexistant nodes: " .. name)
end
local function spawn_iron_golem(pos)
local p = minetest.find_node_near(pos,50,"mcl_core:grass_path")
if p then
local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity()
if l then
l._home = p
end
end
end
local function spawn_villagers(minp,maxp)
local beds=minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20),{"mcl_beds:bed_red_bottom"})
for _,bed in pairs(beds) do
local m = minetest.get_meta(bed)
if m:get_string("villager") == "" then
local v=minetest.add_entity(bed,"mobs_mc:villager")
if v then
local l=v:get_luaentity()
l._bed = bed
m:set_string("villager",l._id)
end
end
end
end
local function init_nodes(p1, p2, size, rotation, pr)
construct_node(p1, p2, "mcl_itemframes:item_frame")
construct_node(p1, p2, "mcl_furnaces:furnace")
construct_node(p1, p2, "mcl_anvils:anvil")
construct_node(p1, p2, "mcl_smoker:smoker")
construct_node(p1, p2, "mcl_barrels:barrel_closed")
construct_node(p1, p2, "mcl_blast_furnace:blast_furnace")
construct_node(p1, p2, "mcl_brewing:stand_000")
local nodes = construct_node(p1, p2, "mcl_chests:chest")
if nodes and #nodes > 0 then
for p=1, #nodes do
@ -201,9 +231,30 @@ local function init_nodes(p1, p2, size, rotation, pr)
end
end
end
function settlements.place_schematics(settlement_info, pr)
local building_all_info
--attempt to place one belltower in the center of the village - this doesn't always work out great but it's a lot better than doing it first or last.
local belltower = table.remove(settlement_info,math.floor(#settlement_info/2))
if belltower then
mcl_structures.place_schematic(
vector.offset(belltower["pos"],0,0,0),
settlements.modpath.."/schematics/belltower.mts",
belltower["rotation"],
nil,
true,
nil,
function(p1, p2, size, rotation, pr)
spawn_iron_golem(p1)
end,
pr
)
end
for i, built_house in ipairs(settlement_info) do
local is_last = i == #settlement_info
for j, schem in ipairs(settlements.schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
building_all_info = schem
@ -271,7 +322,10 @@ function settlements.place_schematics(settlement_info, pr)
nil,
true,
nil,
init_nodes,
function(p1, p2, size, rotation, pr)
init_nodes(p1, p2, size, rotation, pr)
spawn_villagers(p1,p2)
end,
pr
)
end

@ -21,7 +21,6 @@ minetest.register_node("mcl_villages:stonebrickcarved", {
description = ("Chiseled Stone Village Bricks"),
_doc_items_longdesc = doc.sub.items.temp.build,
tiles = {"mcl_core_stonebrick_carved.png"},
stack_max = 64,
drop = "mcl_core:stonebrickcarved",
groups = {pickaxey=1, stone=1, stonebrick=1, building_block=1, material_stone=1},
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -50,17 +49,6 @@ if minetest.get_modpath("mobs_mc") then
end
--]]
local function spawn_villagers(minp,maxp)
local beds=minetest.find_nodes_in_area(minp,maxp,{"mcl_beds:bed_red_bottom"})
for _,bed in pairs(beds) do
minetest.get_meta(bed):set_string("villagebed","true")
local v=minetest.add_entity(bed,"mobs_mc:villager")
if v then
v:get_luaentity().bed = bed
end
end
end
--
-- on map generation, try to build a settlement
--
@ -79,10 +67,6 @@ local function build_a_settlement(minp, maxp, blockseed)
-- evaluate settlement_info and place schematics
settlements.place_schematics(settlement_info, pr)
minetest.after(60,function()
spawn_villagers(minp,maxp)
end) --give the village some time to fully generate
end
local function ecb_village(blockpos, action, calls_remaining, param)

Binary file not shown.