1
0
Fork 0

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

Reviewed-on: MineClone2/MineClone2#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

View File

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

View File

@ -44,7 +44,7 @@ functions needed for the mob to work properly which contains the following:
'passive' when true allows animals to defend themselves when hit,
otherwise they amble onwards.
'walk_velocity' is the speed that your mob can walk around.
'run_velocity' is the speed your mob can run with, usually when attacking.
'run_velocity'is the speed your mob can run with, usually when attacking.
'walk_chance' has a 0-100 chance value your mob will walk from standing,
set to 0 for jumping mobs only.
'jump' when true allows your mob to jump updwards.
@ -66,13 +66,13 @@ functions needed for the mob to work properly which contains the following:
walking, 0 to turn off height fear.
'fall_speed' has the maximum speed the mob can fall at, default is -10.
'fall_damage' when true causes falling to inflict damage.
'water_damage' holds the damage per second infliced to mobs when standing in
'water_damage'holds the damage per second infliced to mobs when standing in
water (default: 0).
'lava_damage' holds the damage per second inflicted to mobs when standing
in lava (default: 8).
'fire_damage' holds the damage per second inflicted to mobs when standing
in fire (default: 1).
'light_damage' holds the damage per second inflicted to mobs when it's too
'light_damage'holds the damage per second inflicted to mobs when it's too
bright (above 13 light).
'suffocation' when true causes mobs to suffocate inside solid blocks (2 damage per second).
'floats' when set to 1 mob will float in water, 0 has them sink.
@ -119,7 +119,7 @@ functions needed for the mob to work properly which contains the following:
attacking.
'dogshoot_switch' allows switching between attack types by using timers
(1 for shoot, 2 for dogfight)
'dogshoot_count_max' contains how many seconds before switching from
'dogshoot_count_max'contains how many seconds before switching from
dogfight to shoot.
'dogshoot_count2_max' contains how many seconds before switching from shoot
to dogfight.
@ -136,7 +136,7 @@ functions needed for the mob to work properly which contains the following:
in minetest.conf is not false).
'immune_to' is a table that holds specific damage when being hit by
certain items e.g.
{"default:sword_wood", 0} -- causes no damage.
{"default:sword_wood",0} -- causes no damage.
{"default:gold_lump", -10} -- heals by 10 health points.
{"default:coal_block", 20} -- 20 damage when hit on head with coal blocks.
@ -194,9 +194,10 @@ 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_start'start frame for when mob stands still.
'stand_end' end frame of stand animation.
'stand_speed' speed of animation in frames per second.
'stand_speed'speed of animation in frames per second.
'walk_start' when mob is walking around.
'walk_end'
'walk_speed'
@ -206,19 +207,20 @@ functions needed for the mob to work properly which contains the following:
'fly_start' when a mob is flying.
'fly_end'
'fly_speed'
'punch_start' when a mob melee attacks.
'punch_start'when a mob melee attacks.
'punch_end'
'punch_speed'
'punch2_start' alternative melee attack animation.
'punch2_end'
'punch2_speed'
'shoot_start' shooting animation.
'shoot_start'shooting animation.
'shoot_end'
'shoot_speed'
'die_start' death animation
'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
@ -297,7 +305,7 @@ enhance mob functionality and have them do many interesting things:
'on_die' a function that is called when the mob is killed; the
parameters are (self, pos). Return true to skip the builtin
death animation and death effects
'on_rightclick' its same as in minetest.register_entity()
'on_rightclick'its same as in minetest.register_entity()
'on_blast' is called when an explosion happens near mob when using TNT
functions, parameters are (object, damage) and returns
(do_damage, do_knockback, drops)
@ -308,13 +316,13 @@ enhance mob functionality and have them do many interesting things:
'on_breed' called when two similar mobs breed, paramaters are
(parent1, parent2) objects, return false to stop child from
being resized and owner/tamed flags and child textures being
applied. Function itself must spawn new child mob.
applied.Function itself must spawn new child mob.
'on_grown' is called when a child mob has grown up, only paramater is
(self).
'do_punch' called when mob is punched with paramaters (self, hitter,
time_from_last_punch, tool_capabilities, direction), return
false to stop punch damage and knockback from taking place.
'custom_attack' when set this function is called instead of the normal mob
'custom_attack'when set this function is called instead of the normal mob
melee attack, parameters are (self, to_attack).
'on_die' a function that is called when mob is killed (self, pos)
'do_custom' a custom function that is called every tick while mob is
@ -335,9 +343,9 @@ for each mob.
damage at all (cannot exceed self.breath_max). Breath
decreases by 1 each second while in a node with drowning
damage and increases by 1 each second otherwise.
'self.texture_list' contains list of all mob textures
'self.texture_list'contains list of all mob textures
'self.child_texture' contains mob child texture when growing up
'self.base_texture' contains current skin texture which was randomly
'self.base_texture'contains current skin texture which was randomly
selected from textures list
'self.gotten' this is used to track whether some special item has been
gotten from the mob, for example, wool from sheep.
@ -406,7 +414,7 @@ command which uses above names to make settings clearer:
For each mob that spawns with this function is a field in mobs.spawning_mobs.
It tells if the mob should spawn or not. Default is true. So other mods can
It tells if the mob should spawn or not.Default is true.So other mods can
only use the API of this mod by disabling the spawning of the default mobs in
this mod.
@ -414,7 +422,7 @@ this mod.
mobs:spawn_abm_check(pos, node, name)
This global function can be changed to contain additional checks for mobs to
spawn e.g. mobs that spawn only in specific areas and the like. By returning
spawn e.g. mobs that spawn only in specific areas and the like.By returning
true the mob will not spawn.
'pos' holds the position of the spawning mob
@ -446,7 +454,7 @@ This function registers a arrow for mobs with the attack type shoot.
'name' is the name of the arrow
'definition' is a table with the following values:
'visual' same is in minetest.register_entity()
'visual_size' same is in minetest.register_entity()
'visual_size'same is in minetest.register_entity()
'textures' same is in minetest.register_entity()
'velocity' the velocity of the arrow
'drop' if set to true any arrows hitting a node will drop as item
@ -482,7 +490,7 @@ This function registers a spawn egg which can be used by admin to properly spawn
'name' this is the name of your new mob to spawn e.g. "mob:sheep"
'description' the name of the new egg you are creating e.g. "Spawn Sheep"
'background' the texture displayed for the egg in inventory
'background'the texture displayed for the egg in inventory
'addegg' would you like an egg image in front of your texture (1 = yes,
0 = no)
'no_creative' when set to true this stops spawn egg appearing in creative
@ -498,7 +506,7 @@ mobs:boom(self, pos, radius)
'radius' radius of explosion (typically set to 3)
This function generates an explosion which removes nodes in a specific radius
and damages any entity caught inside the blast radius. Protection will limit
and damages any entity caught inside the blast radius.Protection will limit
node destruction but not entity damage.
@ -542,7 +550,7 @@ mobs:protect(self, clicker)
This function can be used to right-click any tamed mob with mobs:protector item,
this will protect the mob from harm inside of a protected area from other
players. Will return true when mob right-clicked with mobs:protector item.
players.Will return true when mob right-clicked with mobs:protector item.
'self' mob information
'clicker' player information
@ -578,7 +586,7 @@ This function allows an attached player to move the mob around and animate it at
same time.
'self' mob information
'move_animation' string containing movement animation e.g. "walk"
'move_animation'string containing movement animation e.g. "walk"
'stand_animation' string containing standing animation e.g. "stand"
'can_fly' if true then jump and sneak controls will allow mob to fly
up and down
@ -596,7 +604,7 @@ controls.
'can_shoot' true if mob can fire arrow (sneak and left mouse button
fires)
'arrow_entity' name of arrow entity used for firing
'move_animation' string containing name of pre-defined animation e.g. "walk"
'move_animation'string containing name of pre-defined animation e.g. "walk"
or "fly" etc.
'stand_animation' string containing name of pre-defined animation e.g.
"stand" or "blink" etc.
@ -623,7 +631,7 @@ Certain variables need to be set before using the above functions:
'self.accel' acceleration speed
'self.terrain_type' integer containing terrain mob can walk on
(1 = water, 2 or 3 = land)
'self.driver_attach_at' position offset for attaching player to mob
'self.driver_attach_at'position offset for attaching player to mob
'self.driver_eye_offset' position offset for attached player view
'self.driver_scale' sets driver scale for mobs larger than {x=1, y=1}
@ -642,7 +650,7 @@ External Settings for "minetest.conf"
multiplied by this number), defaults to 1.0.
'mob_spawn_chance' multiplies chance of all mobs spawning and can be set
to 0.5 to have mobs spawn more or 2.0 to spawn less.
e.g. 1 in 7000 * 0.5 = 1 in 3500 so better odds of
e.g.1 in 7000 * 0.5 = 1 in 3500 so better odds of
spawning.
'mobs_spawn' if false then mobs no longer spawn without spawner or
spawn egg.

View File

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

View File

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

View File

@ -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
end
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,
})

View File

@ -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", {

View File

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

View File

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