From d148e6d4ba6583026f8fe825143029db735c64a3 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Tue, 18 Oct 2022 23:59:53 +0100 Subject: [PATCH 1/5] Villagers - Work, sleep and wander. Check and claim beds etc. --- mods/ENTITIES/mcl_mobs/api.lua | 6 +- mods/ENTITIES/mobs_mc/villager.lua | 289 ++++++++++++++++++++++++++--- 2 files changed, 266 insertions(+), 29 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 58cb72f9a..3577783c5 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -2486,6 +2486,8 @@ local function check_gowp(self,dtime) self._target = nil self.current_target = nil self.state = "stand" + self.object:set_velocity({x = 0, y = 0, z = 0}) + self.object:set_acceleration({x = 0, y = 0, z = 0}) if self.callback_arrived then return self.callback_arrived(self) end return true end @@ -2556,8 +2558,8 @@ local do_states = function(self, dtime) end -- npc's ordered to stand stay standing - if self.type ~= "npc" - or self.order ~= "stand" then + if self.type ~= "npc" or + (self.order ~= "stand" and self.order ~= "sleep" and self.order ~= "work") then if self.walk_chance ~= 0 and self.facing_fence ~= true diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 376e9fd89..074977d14 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -492,6 +492,9 @@ local professions = { } } +local WORK = "work" +local SLEEP = "sleep" + local profession_names = {} for id, _ in pairs(professions) do table.insert(profession_names, id) @@ -499,9 +502,16 @@ end local jobsites={} for _,n in pairs(profession_names) do - table.insert(jobsites,professions[n].jobsite) + if n then + if professions[n].jobsite then + table.insert(jobsites,professions[n].jobsite) + end + end end +local spawnable_bed={} +table.insert(spawnable_bed, "mcl_beds:bed_red_bottom") + local function stand_still(self) self.walk_chance = 0 self.jump = false @@ -532,23 +542,124 @@ 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 - if not b then return end - mcl_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 + +function get_activity(tod) + -- night hours = tod > 18541 or tod < 5458 + if not tod then + tod = minetest.get_timeofday() + end + tod = ( tod * 24000 ) % 24000 + + local lunch_start = 12000 + local lunch_end = 13300 + local work_start = 8300 + local work_end = 16300 + + local activity = nil + if (tod > work_start and tod < lunch_start) or (tod > lunch_end and tod < work_end) then + activity = WORK + elseif mcl_beds.is_night() then + activity = SLEEP + elseif tod > lunch_start and tod < lunch_end then + activity = "chill" + else + activity = "undefined" + end + --minetest.log("Time is " .. tod ..". Activity is: ".. activity) + return activity + +end + + +local function go_home(entity, sleep) + local b = entity._bed + if not b then + return + end + + local bed_node = minetest.get_node(b) + if not bed_node then + entity._bed = nil + --minetest.log("Cannot find bed. Unset it") + return + end + + + if vector.distance(entity.object:get_pos(),b) < 2 then + if sleep then + entity.order = SLEEP + --minetest.log("Sleep time!") + end + else + if sleep and entity.order == SLEEP then + entity.order = nil + return + end + + mcl_mobs:gopath(entity,b,function(entity,b) + local b = entity._bed + + if not b then + --minetest.log("NO BED, problem") return false end - return true + + if not minetest.get_node(b) then + --minetest.log("NO BED NODE, problem") + return false + end + + if vector.distance(entity.object:get_pos(),b) < 2 then + --minetest.log("Managed to walk home callback!") + return true + else + --minetest.log("Need to walk to home") + end + end) + end +end + +local function check_bed (entity) + local b = entity._bed + if not b then + --minetest.log("No bed set on villager") + return false + end + + local n = minetest.get_node(b) + if n and n.name ~= "mcl_beds:bed_red_bottom" then + --minetest.log("Where did my bed go?!") + entity._bed = nil --the stormtroopers have killed uncle owen + return false + else + return true + end +end + +local function take_bed (entity) + if not entity then return end + + local p = entity.object:get_pos() + local nn = minetest.find_nodes_in_area(vector.offset(p,-48,-48,-48), vector.offset(p,48,48,48), spawnable_bed) + + for _,n in pairs(nn) do + local m=minetest.get_meta(n) + --minetest.log("Bed owner: ".. m:get_string("villager")) + if m:get_string("villager") == "" then + --minetest.log("Can we path to bed: "..minetest.pos_to_string(n) ) + local gp = mcl_mobs:gopath(entity,n,function() + --minetest.log("We did path to bed. This is great: "..minetest.pos_to_string(n) ) + end) + if gp then + --minetest.log("Nice bed. I'll defintely take it as I can path") + m:set_string("villager", entity._id) + entity._bed = n + break + else + --minetest.log("Awww. I can't find my bed.") + end end - end) + end end local function has_golem(pos) @@ -608,11 +719,14 @@ local function employ(self,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 + minetest.log("Taking this job") self._profession=p m:set_string("villager",self._id) self._jobsite = jobsite_pos set_textures(self) return true + else + --minetest.log("I can not steal someone's job!") end end @@ -621,10 +735,12 @@ local function look_for_job(self) self.last_jobhunt = os.time() + math.random(0,60) 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) + --minetest.log("Looking for jobs") for _,n in pairs(nn) do local m=minetest.get_meta(n) + --minetest.log("Job owner: ".. m:get_string("villager")) if m:get_string("villager") == "" then - --minetest.log("goingt to jobsite "..minetest.pos_to_string(n) ) + --minetest.log("It's a free job for me (".. minetest.pos_to_string(p) .. ")! I'll take: "..minetest.pos_to_string(n) ) local gp = mcl_mobs:gopath(self,n,function() --minetest.log("arrived jobsite "..minetest.pos_to_string(n) ) end) @@ -634,24 +750,99 @@ local function look_for_job(self) end local function get_a_job(self) + --minetest.log("In get a job") if self.child then return end + local p = self.object:get_pos() local n = minetest.find_node_near(p,1,jobsites) + + if not n then + --minetest.log("Job hunt failed. No job block near.") + else + --minetest.log("Found job block near. Is it free though?") + end + if n and employ(self,n) then return true end + if self.state ~= "gowp" then look_for_job(self) end end -local function check_jobsite(self) - if self._traded or not self._jobsite then return end +local function find_jobsite (self) + if not self or not self._jobsite then + --minetest.log("find_jobsite. Invalid params") + return + end + local n = mcl_vars.get_node(self._jobsite) local m = minetest.get_meta(self._jobsite) - if m:get_string("villager") ~= self._id then + if m:get_string("villager") == self._id then + --minetest.log("find_jobsite. is my job.") + return n + else + --minetest.log("find_jobsite. Not my job") + end + return +end + +local function check_jobsite(self) + if self._traded then + return false + end + + if not find_jobsite (self) then self._profession = "unemployed" self._trades = nil set_textures(self) + return false + else + return true end end + + +local function do_work (self) + local jobsite2 = find_jobsite (self) + local jobsite = self._jobsite + + if self and jobsite2 and self._jobsite then + if self.order == WORK then + --minetest.log("I'm already working, boss!") + + self.object:set_velocity({x = 0, y = 0, z = 0}) + --minetest.log("[mobs_mc] Villager velocity is: ".. minetest.pos_to_string(self.object:get_velocity())) + return + end + + if vector.distance(self.object:get_pos(),self._jobsite) < 2 then + --minetest.log("Made it to work ok!") + self.order = WORK + else + --self.state = "go_to_work" + mcl_mobs:gopath(self, jobsite, function(self,jobsite) + if not self then + --minetest.log("missing self. not good") + return false + end + if not self._jobsite then + --minetest.log("Jobsite not valid") + return false + end + if vector.distance(self.object:get_pos(),self._jobsite) < 2 then + --minetest.log("Made it to work ok callback!") + return true + else + --minetest.log("Need to walk to work. Not sure we can get here.") + end + + end) + end + else + --minetest.log("Cannot find the jobsite in do_work. Do nothing.") + end + +end + local function update_max_tradenum(self) if not self._trades then return @@ -724,7 +915,7 @@ local function set_trade(trader, player, inv, concrete_tradenum) init_trades(trader) trades = minetest.deserialize(trader._trades) if not trades then - minetest.log("error", "[mobs_mc] Failed to select villager trade!") + --minetest.log("error", "[mobs_mc] Failed to select villager trade!") return end end @@ -1345,18 +1536,21 @@ mcl_mobs:register_mob("mobs_mc:villager", { _trading_players = {}, -- list of playernames currently trading with villager (open formspec) do_custom = function(self, dtime) check_summon(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 @@ -1367,18 +1561,59 @@ mcl_mobs:register_mob("mobs_mc:villager", { minetest.log("verbose", "[mobs_mc] Player near villager found!") stand_still(self) else - minetest.log("verbose", "[mobs_mc] No player near villager found!") + --minetest.log("verbose", "[mobs_mc] No player near villager found!") 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) + + if not self._bed then + --minetest.log("[mobs_mc] Villager has no bed. Currently at location: "..minetest.pos_to_string(self.object:get_pos())) + take_bed (self) end - if self._profession == "unemployed" then - get_a_job(self) + if check_bed (self) then + --self.state ~= "go_home" + local wandered_too_far = ( self.state ~= "gowp" ) and (vector.distance(self.object:get_pos(),self._bed) > 50 ) + + if wandered_too_far then minetest.log("[mobs_mc] Wandered too far! Return home ") end + if wandered_too_far then + go_home(self, false) + return + elseif mcl_beds.is_night() then + --minetest.log("[mobs_mc] It's night. Better get to bed ") + go_home(self, true) + return + else + -- work + -- or gossip at town bell + end else - check_jobsite(self) + --minetest.log("[mobs_mc] check bed failed ") end + + -- Daytime is work and play time + if not mcl_beds.is_night() then + if self.order == SLEEP then self.order = nil end + if self._profession == "unemployed" then + --minetest.log("[mobs_mc] I'm unemployed. Can I get a job?") + get_a_job(self) + else + if get_activity() == WORK then + --minetest.log("[mobs_mc] Time for work") + if check_jobsite(self) then + --minetest.log("[mobs_mc] My jobsite is valid. Let's do work ") + do_work(self) + -- at night or thunder, go to safety (bed) + else + --minetest.log("[mobs_mc] My job site is invalid or i'm already working. I cannot work.") + end + else + self.order = nil + end + end + else + if self.order == WORK then self.order = nil end + end + end end, From a8b336381d7828f7e4d1bc6a4eeec9247c01e3a6 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 22 Oct 2022 01:28:45 +0100 Subject: [PATCH 2/5] Villagers - On die, clear job and bed. Get same job if already traded. Thunderstorms go to bed --- mods/ENTITIES/mcl_mobs/api.lua | 61 ++++- mods/ENTITIES/mobs_mc/villager.lua | 377 ++++++++++++++++++++--------- 2 files changed, 317 insertions(+), 121 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 3577783c5..41a23a9de 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -16,6 +16,14 @@ local CRAMMING_DAMAGE = 3 -- Localize local S = minetest.get_translator("mcl_mobs") +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false) +local LOG_MODULE = "[Mobs]" +local function mcl_log (message) + if LOGGING_ON and message then + minetest.log(LOG_MODULE .. " " .. message) + end +end + local function shortest_term_of_yaw_rotatoin(self, rot_origin, rot_target, nums) if not rot_origin or not rot_target then @@ -1562,6 +1570,7 @@ end -- find two animals of same type and breed if nearby and horny local breed = function(self) + --mcl_log("In breed function") -- child takes a long time before growing into adult if self.child == true then @@ -1619,6 +1628,8 @@ local breed = function(self) if self.horny == true and self.hornytimer <= HORNY_TIME then + mcl_log("In breed function. All good. Do the magic.") + local pos = self.object:get_pos() effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1) @@ -1653,6 +1664,8 @@ local breed = function(self) end end + if canmate then mcl_log("In breed function. Can mate.") end + if ent and canmate == true and ent.horny == true @@ -1667,6 +1680,8 @@ local breed = function(self) ent.hornytimer = HORNY_TIME + 1 -- spawn baby + + minetest.after(5, function(parent1, parent2, pos) if not parent1.object:get_luaentity() then return @@ -2480,20 +2495,42 @@ local function check_gowp(self,dtime) if gowp_etime < 0.2 then return end gowp_etime = 0 local p = self.object:get_pos() - if not p or not self._target then return end - if vector.distance(p,self._target) < 1 then + + -- no destination + if not p or not self._target then + mcl_log("p: ".. tostring(p)) + mcl_log("self._target: ".. tostring(self._target)) + return + end + + -- arrived at location + local distance_to_targ = vector.distance(p,self._target) + mcl_log("Distance to targ: ".. tostring(distance_to_targ)) + if distance_to_targ < 2 then + mcl_log("Arrived at _target") self.waypoints = nil self._target = nil self.current_target = nil self.state = "stand" + self.order = "stand" self.object:set_velocity({x = 0, y = 0, z = 0}) self.object:set_acceleration({x = 0, y = 0, z = 0}) 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) < 2 ) then + if not self.current_target then + for i, j in pairs (self.waypoints) do + mcl_log("Way: ".. tostring(i)) + mcl_log("Val: ".. tostring(j)) + end + --mcl_log("nextwp:".. tostring(self.waypoints) ) + end + self.current_target = table.remove(self.waypoints, 1) - --minetest.log("nextwp:".. tostring(self.current_target) ) + mcl_log("current target:".. tostring(self.current_target) ) + --mcl_log("type:".. type(self.current_target) ) go_to_pos(self,self.current_target) return elseif self.current_target then @@ -2507,7 +2544,7 @@ local function check_gowp(self,dtime) return end if not self.current_target then - --minetest.log("no path") + --mcl_log("no path") self.state = "walk" end end @@ -2558,9 +2595,9 @@ local do_states = function(self, dtime) end -- npc's ordered to stand stay standing - if self.type ~= "npc" or - (self.order ~= "stand" and self.order ~= "sleep" and self.order ~= "work") then + if self.type == "npc" or (self.order == "stand" or self.order == "sleep" or self.order == "work") then + else if self.walk_chance ~= 0 and self.facing_fence ~= true and random(1, 100) <= self.walk_chance @@ -3073,6 +3110,8 @@ local do_states = function(self, dtime) end end end + else + end end end @@ -3087,13 +3126,21 @@ local plane_adjacents = { local gopath_last = os.time() function mcl_mobs:gopath(self,target,callback_arrived) + if self.state == "gowp" then mcl_log("Already set as gowp, don't set another path until done.") return end + if os.time() - gopath_last < 15 then return end gopath_last = os.time() - --minetest.log("gowp") + + self.order = nil + + mcl_log("gowp target: " .. minetest.pos_to_string(target)) local p = self.object:get_pos() local t = vector.offset(target,0,1,0) local wp = minetest.find_path(p,t,150,1,4) + + --Path to door first if not wp then + --mcl_log("gowp. no wp. Look for door") local d = minetest.find_node_near(target,16,{"group:door"}) if d then for _,v in pairs(plane_adjacents) do diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 074977d14..bd5173d23 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -15,6 +15,8 @@ -- TODO: Internal inventory, trade with other villagers -- TODO: Schedule stuff (work,sleep,father) +local weather_mod = minetest.get_modpath("mcl_weather") + local S = minetest.get_translator("mobs_mc") local N = function(s) return s end local F = minetest.formspec_escape @@ -40,6 +42,14 @@ local PLAYER_SCAN_RADIUS = 4 -- scan radius for looking for nearby players -- these items should be implemented as single items, then everything -- will be much easier. +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false) +local LOG_MODULE = "[Mobs - Villager]" +local function mcl_log (message) + if LOGGING_ON and message then + minetest.log(LOG_MODULE .. " " .. message) + end +end + local COMPASS = "mcl_compass:compass" if minetest.registered_aliases[COMPASS] then COMPASS = minetest.registered_aliases[COMPASS] @@ -500,15 +510,24 @@ for id, _ in pairs(professions) do table.insert(profession_names, id) end -local jobsites={} -for _,n in pairs(profession_names) do - if n then - if professions[n].jobsite then - table.insert(jobsites,professions[n].jobsite) +local function populate_jobsites (profession) + if profession then + mcl_log("populate_jobsites: ".. tostring(profession)) + end + local jobsites_requested={} + for _,n in pairs(profession_names) do + if n and professions[n].jobsite then + if not profession or (profession and profession == n) then + --minetest.log("populate_jobsites. Adding: ".. tostring(n)) + table.insert(jobsites_requested,professions[n].jobsite) + end end end + return jobsites_requested end +jobsites = populate_jobsites() + local spawnable_bed={} table.insert(spawnable_bed, "mcl_beds:bed_red_bottom") @@ -539,10 +558,11 @@ local function get_badge_textures(self) end local function set_textures(self) - self.object:set_properties({textures=get_badge_textures(self)}) + local badge_textures = get_badge_textures(self) + mcl_log("Setting textures: " .. tostring(badge_textures)) + self.object:set_properties({textures=badge_textures}) end - function get_activity(tod) -- night hours = tod > 18541 or tod < 5458 if not tod then @@ -551,9 +571,15 @@ function get_activity(tod) tod = ( tod * 24000 ) % 24000 local lunch_start = 12000 - local lunch_end = 13300 - local work_start = 8300 - local work_end = 16300 + local lunch_end = 13000 + local work_start = 6500 + local work_end = 18000 + + --local lunch_start = 12000 + --local lunch_end = 13300 + --local work_start = 8300 + --local work_end = 16300 + local activity = nil if (tod > work_start and tod < lunch_start) or (tod > lunch_end and tod < work_end) then @@ -561,11 +587,11 @@ function get_activity(tod) elseif mcl_beds.is_night() then activity = SLEEP elseif tod > lunch_start and tod < lunch_end then - activity = "chill" + activity = "lunch" else - activity = "undefined" + activity = "chill" end - --minetest.log("Time is " .. tod ..". Activity is: ".. activity) + mcl_log("Time is " .. tod ..". Activity is: ".. activity) return activity end @@ -580,7 +606,7 @@ local function go_home(entity, sleep) local bed_node = minetest.get_node(b) if not bed_node then entity._bed = nil - --minetest.log("Cannot find bed. Unset it") + mcl_log("Cannot find bed. Unset it") return end @@ -588,7 +614,7 @@ local function go_home(entity, sleep) if vector.distance(entity.object:get_pos(),b) < 2 then if sleep then entity.order = SLEEP - --minetest.log("Sleep time!") + mcl_log("Sleep time!") end else if sleep and entity.order == SLEEP then @@ -628,7 +654,7 @@ local function check_bed (entity) local n = minetest.get_node(b) if n and n.name ~= "mcl_beds:bed_red_bottom" then - --minetest.log("Where did my bed go?!") + mcl_log("Where did my bed go?!") entity._bed = nil --the stormtroopers have killed uncle owen return false else @@ -644,20 +670,27 @@ local function take_bed (entity) for _,n in pairs(nn) do local m=minetest.get_meta(n) - --minetest.log("Bed owner: ".. m:get_string("villager")) - if m:get_string("villager") == "" then - --minetest.log("Can we path to bed: "..minetest.pos_to_string(n) ) - local gp = mcl_mobs:gopath(entity,n,function() - --minetest.log("We did path to bed. This is great: "..minetest.pos_to_string(n) ) + mcl_log("Bed owner: ".. m:get_string("villager")) + if m:get_string("villager") == "" and not entity.state == "gowp" then + mcl_log("Can we path to bed: "..minetest.pos_to_string(n) ) + local gp = mcl_mobs:gopath(entity,n,function(self) + if self then + self.order = "sleep" + mcl_log("Sleepy time" ) + else + mcl_log("Can't sleep, no self in the callback" ) + end end) if gp then - --minetest.log("Nice bed. I'll defintely take it as I can path") + mcl_log("Nice bed. I'll defintely take it as I can path") m:set_string("villager", entity._id) entity._bed = n break else - --minetest.log("Awww. I can't find my bed.") + mcl_log("Awww. I can't find my bed.") end + else + mcl_log("Currently gowp, or it's taken.") end end end @@ -707,6 +740,37 @@ local function check_summon(self,dtime) self._summon_timer = self._summon_timer + dtime end +local function has_traded (self) + --mcl_log("Checking name: " .. self._trades) + local cur_trades_tab = minetest.deserialize(self._trades) + + if cur_trades_tab and type(cur_trades_tab) == "table" then + for trader, trades in pairs(cur_trades_tab) do + --mcl_log("Current record: ".. tostring(trader)) + --for tr3, tr4 in pairs (tab_val) do + --mcl_log("Key: ".. tostring(tr3)) + --mcl_log("Value: ".. tostring(tr4)) + --end + --mcl_log("traded once: ".. tostring(trades.traded_once)) + + if trades.traded_once then + mcl_log("Villager has traded before. Returning true") + return true + end + end + end + mcl_log("Villager has not traded before") + return false +end + +local function unlock_trades (self) + if self then + mcl_log("We should now try to unlock trades") + else + mcl_log("Missing self") + end +end + ----- JOBSITE LOGIC local function get_profession_by_jobsite(js) for k,v in pairs(professions) do @@ -719,80 +783,133 @@ local function employ(self,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 - minetest.log("Taking this job") - self._profession=p + mcl_log("Taking this jobsite") + m:set_string("villager",self._id) self._jobsite = jobsite_pos - set_textures(self) + + if not self.traded then + self._profession=p + set_textures(self) + end + return true else - --minetest.log("I can not steal someone's job!") + mcl_log("I can not steal someone's job!") end end -local function look_for_job(self) - if self.last_jobhunt and os.time() - self.last_jobhunt < 360 then return end - self.last_jobhunt = os.time() + math.random(0,60) + +local function look_for_job(self, requested_jobsites) + if self.last_jobhunt and os.time() - self.last_jobhunt < 40 then return end + self.last_jobhunt = os.time() + math.random(0,30) + + mcl_log("Looking for jobs") + + local looking_for_type = jobsites + if requested_jobsites then + mcl_log("Looking for jobs of my type") + local looking_for_type = requested_jobsites + else + mcl_log("Looking for any job type") + end + 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) - --minetest.log("Looking for jobs") + local nn = minetest.find_nodes_in_area(vector.offset(p,-48,-48,-48),vector.offset(p,48,48,48), looking_for_type) + for _,n in pairs(nn) do - local m=minetest.get_meta(n) - --minetest.log("Job owner: ".. m:get_string("villager")) + local m = minetest.get_meta(n) + --mcl_log("Job owner: ".. m:get_string("villager")) + if m:get_string("villager") == "" then - --minetest.log("It's a free job for me (".. minetest.pos_to_string(p) .. ")! I'll take: "..minetest.pos_to_string(n) ) - local gp = mcl_mobs:gopath(self,n,function() - --minetest.log("arrived jobsite "..minetest.pos_to_string(n) ) + mcl_log("It's a free job for me (".. minetest.pos_to_string(p) .. ")! I might be interested: "..minetest.pos_to_string(n) ) + + local gp = mcl_mobs:gopath(self,n,function(self) + mcl_log("Arrived at block callback") + if self and self.state == "stand" then + self.order = WORK + else + mcl_log("no self. passing param to callback failed") + end + end) - if gp then return end + if gp then + if n then + mcl_log("We can path to this block.. " .. tostring(n)) + end + return n + else + mcl_log("We could not path to block") + end end end + + return nil end + local function get_a_job(self) - --minetest.log("In get a job") - if self.child then return end + mcl_log("I'm unemployed or lost my job block and have traded. Can I get a job?") + --self.order = JOB_HUNTING + + local requested_jobsites = jobsites + if has_traded (self) then + --mcl_log("Has traded") + requested_jobsites = populate_jobsites(self._profession) + -- Only pass in my jobsite to two functions here + else + mcl_log("Has not traded") + end + local p = self.object:get_pos() - local n = minetest.find_node_near(p,1,jobsites) + + local n = minetest.find_node_near(p,1,requested_jobsites) + + --Ideally should check for closest available. It'll make pathing easier. + --local n = look_for_job(self) if not n then - --minetest.log("Job hunt failed. No job block near.") - else - --minetest.log("Found job block near. Is it free though?") + --mcl_log("Job hunt failed. Could not find block I have walked to") end if n and employ(self,n) then return true end - if self.state ~= "gowp" then look_for_job(self) end + if self.state ~= "gowp" then + mcl_log("Nothing near. Need to look for a job") + look_for_job(self, requested_jobsites) + end end -local function find_jobsite (self) +local function retrieve_my_jobsite (self) if not self or not self._jobsite then - --minetest.log("find_jobsite. Invalid params") + --mcl_log("find_jobsite. Invalid params") return end local n = mcl_vars.get_node(self._jobsite) local m = minetest.get_meta(self._jobsite) if m:get_string("villager") == self._id then - --minetest.log("find_jobsite. is my job.") + --mcl_log("find_jobsite. is my job.") return n else - --minetest.log("find_jobsite. Not my job") + --mcl_log("find_jobsite. Not my job") end return end -local function check_jobsite(self) - if self._traded then - return false - end - - if not find_jobsite (self) then - self._profession = "unemployed" - self._trades = nil - set_textures(self) +local function validate_jobsite(self) + if not retrieve_my_jobsite (self) then + if not self._traded then + mcl_log("Cannot retrieve my jobsite. I am now unemployed.") + self._jobsite = nil + self._profession = "unemployed" + self._trades = nil + set_textures(self) + if self.order == WORK then + self.order = nil + end + end return false else return true @@ -802,45 +919,67 @@ end local function do_work (self) - local jobsite2 = find_jobsite (self) - local jobsite = self._jobsite + if self.child then return end + --mcl_log("Time for work") - if self and jobsite2 and self._jobsite then - if self.order == WORK then - --minetest.log("I'm already working, boss!") + -- Don't try if looking_for_work, or gowp possibly + if validate_jobsite(self) then + mcl_log("My jobsite is valid. Do i need to travel?") - self.object:set_velocity({x = 0, y = 0, z = 0}) - --minetest.log("[mobs_mc] Villager velocity is: ".. minetest.pos_to_string(self.object:get_velocity())) - return - end + local jobsite2 = retrieve_my_jobsite (self) + local jobsite = self._jobsite - if vector.distance(self.object:get_pos(),self._jobsite) < 2 then - --minetest.log("Made it to work ok!") - self.order = WORK - else - --self.state = "go_to_work" - mcl_mobs:gopath(self, jobsite, function(self,jobsite) - if not self then - --minetest.log("missing self. not good") - return false - end - if not self._jobsite then - --minetest.log("Jobsite not valid") - return false - end - if vector.distance(self.object:get_pos(),self._jobsite) < 2 then - --minetest.log("Made it to work ok callback!") - return true + if self and jobsite2 and self._jobsite then + + mcl_log("Villager: ".. minetest.pos_to_string(self.object:get_pos()) .. ", jobsite: " .. minetest.pos_to_string(self._jobsite)) + if vector.distance(self.object:get_pos(),self._jobsite) < 2 then + mcl_log("Made it to work ok!") + + if not (self.state == "gowp") then + mcl_log("Setting order to work.") + self.order = WORK else - --minetest.log("Need to walk to work. Not sure we can get here.") + mcl_log("Not gowp. What is it: " .. self.state) end + -- Once we arrive at job block, we should unlock trades + unlock_trades(self) - end) + --self.state = "stand" + --self.object:set_velocity({x = 0, y = 0, z = 0}) + else + mcl_log("Not at job block. Need to commute.") + if self.order == WORK then + self.order = nil + return + end + --self.state = "go_to_work" + mcl_mobs:gopath(self, jobsite, function(self,jobsite) + if not self then + --mcl_log("missing self. not good") + return false + end + if not self._jobsite then + --mcl_log("Jobsite not valid") + return false + end + if vector.distance(self.object:get_pos(),self._jobsite) < 2 then + --mcl_log("Made it to work ok callback!") + return true + else + --mcl_log("Need to walk to work. Not sure we can get here.") + end + + end) + end end - else - --minetest.log("Cannot find the jobsite in do_work. Do nothing.") + elseif self._profession == "unemployed" then + --self.order == JOB_HUNTING + get_a_job(self) + elseif has_traded(self) then + mcl_log("My job site is invalid or gone. I cannot work.") + if self.order == WORK then self.order = nil end + get_a_job(self) end - end local function update_max_tradenum(self) @@ -915,7 +1054,7 @@ local function set_trade(trader, player, inv, concrete_tradenum) init_trades(trader) trades = minetest.deserialize(trader._trades) if not trades then - --minetest.log("error", "[mobs_mc] Failed to select villager trade!") + --minetest.log("error", "Failed to select villager trade!") return end end @@ -1490,11 +1629,20 @@ mcl_mobs:register_mob("mobs_mc:villager", { return it end, on_rightclick = function(self, clicker) - if self._jobsite then + if self.state == "attack" then + mcl_log("Somehow villager got into an invalid attack state. Removed.") + -- Need to stop villager getting in attack state. This is a workaround to allow players to fix broken villager. + self.state = "stand" + self.attack = nil + end + if validate_jobsite(self) then mcl_mobs:gopath(self,self._jobsite,function() --minetest.log("arrived at jobsite") end) + else + self.state = "stand" -- cancel gowp in case it has messed up end + if self.child or self._profession == "unemployed" or self._profession == "nitwit" then return end @@ -1567,48 +1715,36 @@ mcl_mobs:register_mob("mobs_mc:villager", { end if not self._bed then - --minetest.log("[mobs_mc] Villager has no bed. Currently at location: "..minetest.pos_to_string(self.object:get_pos())) + --mcl_log("Villager has no bed. Currently at location: "..minetest.pos_to_string(self.object:get_pos())) take_bed (self) end + if check_bed (self) then --self.state ~= "go_home" local wandered_too_far = ( self.state ~= "gowp" ) and (vector.distance(self.object:get_pos(),self._bed) > 50 ) - if wandered_too_far then minetest.log("[mobs_mc] Wandered too far! Return home ") end + --if wandered_too_far then minetest.log("Wandered too far! Return home ") end if wandered_too_far then go_home(self, false) return - elseif mcl_beds.is_night() then - --minetest.log("[mobs_mc] It's night. Better get to bed ") + elseif mcl_beds.is_night() or (weather_mod and mcl_weather.get_weather() == "thunder") then + mcl_log("It's night or thunderstorm. Better get to bed ") go_home(self, true) return - else - -- work - -- or gossip at town bell end else - --minetest.log("[mobs_mc] check bed failed ") + --mcl_log("check bed failed ") end -- Daytime is work and play time if not mcl_beds.is_night() then if self.order == SLEEP then self.order = nil end - if self._profession == "unemployed" then - --minetest.log("[mobs_mc] I'm unemployed. Can I get a job?") - get_a_job(self) + + if get_activity() == WORK then + do_work(self) else - if get_activity() == WORK then - --minetest.log("[mobs_mc] Time for work") - if check_jobsite(self) then - --minetest.log("[mobs_mc] My jobsite is valid. Let's do work ") - do_work(self) - -- at night or thunder, go to safety (bed) - else - --minetest.log("[mobs_mc] My job site is invalid or i'm already working. I cannot work.") - end - else - self.order = nil - end + -- gossip at town bell or stroll around + self.order = nil end else if self.order == WORK then self.order = nil end @@ -1643,6 +1779,19 @@ mcl_mobs:register_mob("mobs_mc:villager", { end end end + + local bed = self._bed + if bed then + local bed_meta = minetest.get_meta(bed) + bed_meta:set_string("villager", nil) + mcl_log("Died, so bye bye bed") + end + local jobsite = self._jobsite + if jobsite then + local jobsite_meta = minetest.get_meta(jobsite) + jobsite_meta:set_string("villager", nil) + mcl_log("Died, so bye bye jobsite") + end end, }) From e1b685429d947bb676ffe1a27d5d99febf809ecd Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sun, 23 Oct 2022 01:12:19 +0100 Subject: [PATCH 3/5] Texture now changes when villager loses job. --- mods/ENTITIES/mcl_mobs/api.lua | 19 +++++++++- mods/ENTITIES/mobs_mc/villager.lua | 59 ++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 41a23a9de..1ef8a9ab0 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -3128,7 +3128,10 @@ local gopath_last = os.time() function mcl_mobs:gopath(self,target,callback_arrived) if self.state == "gowp" then mcl_log("Already set as gowp, don't set another path until done.") return end - if os.time() - gopath_last < 15 then return end + if os.time() - gopath_last < 15 then + mcl_log("Not ready to path yet") + return + end gopath_last = os.time() self.order = nil @@ -3143,14 +3146,26 @@ function mcl_mobs:gopath(self,target,callback_arrived) --mcl_log("gowp. no wp. Look for door") local d = minetest.find_node_near(target,16,{"group:door"}) if d then + --mcl_log("Found a door near") 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 + if wp then + --mcl_log("Found a path to next to door".. minetest.pos_to_string(pos)) + break + + else + --mcl_log("This block next to door doesn't work.") + end + else + --mcl_log("Block is not air, it is: ".. n.name) end + end + else + mcl_log("No door found") end end if wp and #wp > 0 then diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index bd5173d23..885792995 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -550,6 +550,11 @@ end local function get_badge_textures(self) local t = professions[self._profession].texture + if self._profession == "unemployed" then + t = professions[self._profession].textures -- ideally both scenarios should be textures with a list containing 1 or multiple + --mcl_log("t: " .. tostring(t)) + end + if self._profession == "unemployed" or self._profession == "nitwit" then return t end local tier = self._max_trade_tier or 1 return { @@ -670,8 +675,8 @@ local function take_bed (entity) for _,n in pairs(nn) do local m=minetest.get_meta(n) - mcl_log("Bed owner: ".. m:get_string("villager")) - if m:get_string("villager") == "" and not entity.state == "gowp" then + --mcl_log("Bed owner: ".. m:get_string("villager")) + if m:get_string("villager") == "" and not (entity.state == "gowp") then mcl_log("Can we path to bed: "..minetest.pos_to_string(n) ) local gp = mcl_mobs:gopath(entity,n,function(self) if self then @@ -690,7 +695,7 @@ local function take_bed (entity) mcl_log("Awww. I can't find my bed.") end else - mcl_log("Currently gowp, or it's taken.") + mcl_log("Currently gowp, or it's taken: ".. m:get_string("villager")) end end end @@ -742,6 +747,12 @@ end local function has_traded (self) --mcl_log("Checking name: " .. self._trades) + + if not self._trades then + mcl_log("No trades set. has_traded is false") + return false + end + local cur_trades_tab = minetest.deserialize(self._trades) if cur_trades_tab and type(cur_trades_tab) == "table" then @@ -765,7 +776,7 @@ end local function unlock_trades (self) if self then - mcl_log("We should now try to unlock trades") + --mcl_log("We should now try to unlock trades") else mcl_log("Missing self") end @@ -784,15 +795,14 @@ local function employ(self,jobsite_pos) local p = get_profession_by_jobsite(n.name) if p and m:get_string("villager") == "" then mcl_log("Taking this jobsite") - + m:set_string("villager",self._id) self._jobsite = jobsite_pos - if not self.traded then + if not has_traded(self) then self._profession=p set_textures(self) end - return true else mcl_log("I can not steal someone's job!") @@ -801,15 +811,19 @@ end local function look_for_job(self, requested_jobsites) - if self.last_jobhunt and os.time() - self.last_jobhunt < 40 then return end - self.last_jobhunt = os.time() + math.random(0,30) + + --if self.last_jobhunt and os.time() - self.last_jobhunt < 15 then + -- mcl_log("Is time less than 40?" .. tostring(os.time() - self.last_jobhunt)) + -- return + --end + --self.last_jobhunt = os.time() + math.random(0,30) mcl_log("Looking for jobs") local looking_for_type = jobsites if requested_jobsites then - mcl_log("Looking for jobs of my type") - local looking_for_type = requested_jobsites + mcl_log("Looking for jobs of my type: " .. tostring(requested_jobsites)) + looking_for_type = requested_jobsites else mcl_log("Looking for any job type") end @@ -839,7 +853,7 @@ local function look_for_job(self, requested_jobsites) end return n else - mcl_log("We could not path to block") + mcl_log("We could not path to block or it's not ready to path yet.") end end end @@ -899,16 +913,21 @@ local function retrieve_my_jobsite (self) end local function validate_jobsite(self) + if self._profession == "unemployed" then return false end + if not retrieve_my_jobsite (self) then - if not self._traded then + self._jobsite = nil + if self.order == WORK then + self.order = nil + end + + if not has_traded(self) then mcl_log("Cannot retrieve my jobsite. I am now unemployed.") - self._jobsite = nil self._profession = "unemployed" self._trades = nil set_textures(self) - if self.order == WORK then - self.order = nil - end + else + mcl_log("Cannot retrieve my jobsite but I've traded so only remove jobsite.") end return false else @@ -931,12 +950,12 @@ local function do_work (self) if self and jobsite2 and self._jobsite then - mcl_log("Villager: ".. minetest.pos_to_string(self.object:get_pos()) .. ", jobsite: " .. minetest.pos_to_string(self._jobsite)) + --mcl_log("Villager: ".. minetest.pos_to_string(self.object:get_pos()) .. ", jobsite: " .. minetest.pos_to_string(self._jobsite)) if vector.distance(self.object:get_pos(),self._jobsite) < 2 then mcl_log("Made it to work ok!") if not (self.state == "gowp") then - mcl_log("Setting order to work.") + --mcl_log("Setting order to work.") self.order = WORK else mcl_log("Not gowp. What is it: " .. self.state) @@ -1728,7 +1747,7 @@ mcl_mobs:register_mob("mobs_mc:villager", { go_home(self, false) return elseif mcl_beds.is_night() or (weather_mod and mcl_weather.get_weather() == "thunder") then - mcl_log("It's night or thunderstorm. Better get to bed ") + mcl_log("It's night or thunderstorm. Better get to bed. Weather is: " .. mcl_weather.get_weather()) go_home(self, true) return end From ccf7b5448120cd3722e056a13b94b23f232f658c Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sun, 23 Oct 2022 01:17:51 +0100 Subject: [PATCH 4/5] Fix work times --- mods/ENTITIES/mobs_mc/villager.lua | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 885792995..87650e128 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -575,15 +575,11 @@ function get_activity(tod) end tod = ( tod * 24000 ) % 24000 - local lunch_start = 12000 - local lunch_end = 13000 - local work_start = 6500 - local work_end = 18000 - --local lunch_start = 12000 - --local lunch_end = 13300 - --local work_start = 8300 - --local work_end = 16300 + local lunch_start = 12000 + local lunch_end = 13500 + local work_start = 8500 + local work_end = 16300 local activity = nil @@ -614,8 +610,7 @@ local function go_home(entity, sleep) mcl_log("Cannot find bed. Unset it") return end - - + if vector.distance(entity.object:get_pos(),b) < 2 then if sleep then entity.order = SLEEP @@ -795,7 +790,7 @@ local function employ(self,jobsite_pos) local p = get_profession_by_jobsite(n.name) if p and m:get_string("villager") == "" then mcl_log("Taking this jobsite") - + m:set_string("villager",self._id) self._jobsite = jobsite_pos @@ -992,7 +987,6 @@ local function do_work (self) end end elseif self._profession == "unemployed" then - --self.order == JOB_HUNTING get_a_job(self) elseif has_traded(self) then mcl_log("My job site is invalid or gone. I cannot work.") From b9d58c2f79d93f3d30194f4246da1200d8d05d4b Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sun, 23 Oct 2022 22:16:35 +0100 Subject: [PATCH 5/5] Cows and villagers can go into attack state and become non-functional --- mods/ENTITIES/mobs_mc/cow+mooshroom.lua | 1 + mods/ENTITIES/mobs_mc/villager.lua | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua index eebe16f98..d0f7178bd 100644 --- a/mods/ENTITIES/mobs_mc/cow+mooshroom.lua +++ b/mods/ENTITIES/mobs_mc/cow+mooshroom.lua @@ -6,6 +6,7 @@ local cow_def = { description = S("Cow"), type = "animal", spawn_class = "passive", + passive = true, hp_min = 10, hp_max = 10, xp_min = 1, diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 87650e128..7df4c6ea7 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -610,7 +610,7 @@ local function go_home(entity, sleep) mcl_log("Cannot find bed. Unset it") return end - + if vector.distance(entity.object:get_pos(),b) < 2 then if sleep then entity.order = SLEEP @@ -1576,6 +1576,7 @@ mcl_mobs:register_mob("mobs_mc:villager", { description = S("Villager"), type = "npc", spawn_class = "passive", + passive = true, hp_min = 20, hp_max = 20, head_swivel = "head.control",