328 lines
10 KiB
Lua
328 lines
10 KiB
Lua
local func = smart_villages.func
|
|
smart_villages.hasbasic_materials = false
|
|
for key, value in ipairs(minetest.get_modnames()) do
|
|
if(value=="basic_materials") then
|
|
smart_villages.hasbasic_materials = true
|
|
end
|
|
end
|
|
|
|
function smart_villages.func.villager_state_machine_job(job_name,job_description,actions, sprop)
|
|
minetest.log("warning","old util jobdef should be replaced by jobfunc registration")
|
|
minetest.log("warning","old util jobdef: "..job_name)
|
|
|
|
--special properties
|
|
if sprop.night_active ~= true then
|
|
sprop.night_active = false
|
|
end
|
|
if sprop.search_idle ~= true then
|
|
sprop.search_idle = false
|
|
end
|
|
if sprop.searching_range == nil or sprop.searching_range == {} then
|
|
sprop.searching_range = {x = 10, y = 3, z = 10}
|
|
end
|
|
|
|
--controlling state
|
|
local function walk_randomly(self)
|
|
local CHANGE_DIRECTION_TIME_INTERVAL = 50
|
|
if self:timer_exceeded(1,20) then
|
|
self:count_timer(2)
|
|
local myJob = self:get_job()
|
|
if myJob.states.WALK_HOME and myJob.states.WALK_HOME.self_condition(self) then
|
|
myJob.states.WALK_HOME.to_state(self)
|
|
self.job_state=myJob.states.WALK_HOME
|
|
return
|
|
end
|
|
for _,search_state in pairs(myJob.states) do
|
|
local self_cond = false
|
|
if search_state.self_condition then
|
|
if search_state.self_condition(self) then
|
|
self_cond=true
|
|
end
|
|
elseif search_state.search_condition ~= nil or search_state.target_getter ~= nil then
|
|
self_cond=true
|
|
end
|
|
if search_state.search_condition ~= nil and self_cond then
|
|
local target = smart_villages.func.search_surrounding(
|
|
self.object:getpos(), search_state.search_condition, sprop.searching_range)
|
|
if target ~= nil then
|
|
local destination = func.find_adjacent_clear(target)
|
|
if destination==false then
|
|
print("failure: no adjacent walkable found")
|
|
destination = target
|
|
end
|
|
local val_pos = smart_villages.func.validate_pos(self.object:getpos())
|
|
if smart_villages.debug_logging then
|
|
minetest.log("info","looking for a path from " .. minetest.pos_to_string(val_pos) ..
|
|
" to " .. minetest.pos_to_string(destination))
|
|
end
|
|
if smart_villages.pathfinder.get_reachable(val_pos,destination,self) then
|
|
--print("path found to: " .. minetest.pos_to_string(destination))
|
|
if search_state.to_state then
|
|
search_state.to_state(self, destination, target)
|
|
end
|
|
self.job_state=search_state
|
|
return
|
|
end
|
|
end
|
|
elseif search_state.target_getter ~= nil and self_cond then
|
|
local target = search_state.target_getter(self, sprop.searching_range)
|
|
if target ~= nil then
|
|
local distance = vector.subtract(target, self.object:getpos())
|
|
if distance.x<=sprop.searching_range.x
|
|
and distance.y<=sprop.searching_range.y
|
|
and distance.z<=sprop.searching_range.z then
|
|
|
|
local destination = smart_villages.func.validate_pos(target)
|
|
local val_pos = smart_villages.func.validate_pos(self.object:getpos())
|
|
if smart_villages.debug_logging then
|
|
minetest.log("info","looking for a path from " .. minetest.pos_to_string(val_pos) ..
|
|
" to " .. minetest.pos_to_string(destination))
|
|
end
|
|
if smart_villages.pathfinder.get_reachable(val_pos,destination,self) then
|
|
--print("path found to: " .. minetest.pos_to_string(destination))
|
|
if search_state.to_state then
|
|
search_state.to_state(self, destination, target)
|
|
end
|
|
self.job_state=search_state
|
|
return
|
|
end
|
|
end
|
|
end
|
|
elseif self_cond then
|
|
if search_state.to_state then
|
|
search_state.to_state(self)
|
|
end
|
|
self.job_state=search_state
|
|
return
|
|
end
|
|
end
|
|
elseif self:timer_exceeded(2,CHANGE_DIRECTION_TIME_INTERVAL) then
|
|
self:count_timer(1)
|
|
self:change_direction_randomly()
|
|
return
|
|
else
|
|
self:count_timer(1)
|
|
self:count_timer(2)
|
|
|
|
self:handle_obstacles()
|
|
return
|
|
end
|
|
end
|
|
|
|
local function to_walk_randomly(self)
|
|
self:set_timer(1,20)
|
|
self:set_timer(2,0)
|
|
self:set_animation(smart_villages.animation_frames.WALK)
|
|
end
|
|
|
|
local function s_search_idle(self)
|
|
if self:timer_exceeded(1,20) then
|
|
self:set_timer(1,0)
|
|
local myJob = self:get_job()
|
|
for _,search_state in pairs(myJob.states) do
|
|
local self_cond = false
|
|
if search_state.self_condition then
|
|
if search_state.self_condition(self) then
|
|
self_cond=true
|
|
end
|
|
elseif search_state.search_condition ~= nil then
|
|
self_cond=true
|
|
end
|
|
if search_state.search_condition ~= nil and self_cond then
|
|
local target = smart_villages.func.search_surrounding(self.object:getpos(),
|
|
search_state.search_condition, sprop.searching_range)
|
|
if target ~= nil then
|
|
local destination = func.find_adjacent_clear(target)
|
|
if not(destination) then
|
|
destination = target
|
|
end
|
|
local val_pos = smart_villages.func.validate_pos(self.object:getpos())
|
|
if smart_villages.pathfinder.get_reachable(val_pos,destination,self) then
|
|
if search_state.to_state then
|
|
search_state.to_state(self, destination, target)
|
|
end
|
|
self.job_state=search_state
|
|
return
|
|
end
|
|
end
|
|
elseif self_cond then
|
|
if search_state.to_state then
|
|
search_state.to_state(self)
|
|
end
|
|
self.job_state=search_state
|
|
return
|
|
end
|
|
end
|
|
else
|
|
self:count_timer(1)
|
|
return
|
|
end
|
|
end
|
|
|
|
local function to_search_idle(self)
|
|
self:set_timer(1,0)
|
|
self:set_timer(2,0)
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
self:set_animation(smart_villages.animation_frames.STAND)
|
|
end
|
|
|
|
--sleeping states
|
|
local function s_sleep(self)
|
|
if not(minetest.get_timeofday() < 0.2 or minetest.get_timeofday() > 0.76) then
|
|
local pos=self.object:getpos()
|
|
self.object:setpos({x=pos.x,y=pos.y+0.5,z=pos.z})
|
|
minetest.log("action","a villager gets up")
|
|
self:set_animation(smart_villages.animation_frames.STAND)
|
|
self.pause="active"
|
|
self:update_infotext()
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
local function to_sleep(self)
|
|
minetest.log("action","a villager is laying down")
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
local bed_pos=self:get_home():get_bed()
|
|
local bed_top = smart_villages.func.find_adjacent_pos(bed_pos,
|
|
function(p) return string.find(minetest.get_node(p).name,"_top") end)
|
|
local bed_bottom = smart_villages.func.find_adjacent_pos(bed_pos,
|
|
function(p) return string.find(minetest.get_node(p).name,"_bottom") end)
|
|
if bed_top and bed_bottom then
|
|
self:set_yaw_by_direction(vector.subtract(bed_bottom, bed_top))
|
|
else
|
|
minetest.log("info","no bed found")
|
|
end
|
|
self:set_animation(smart_villages.animation_frames.LAY)
|
|
self.object:setpos(vector.add(bed_pos,{x=0,y=1.5,z=0}))
|
|
self.pause="sleeping"
|
|
self:update_infotext()
|
|
end
|
|
local function to_walk_home(self)
|
|
if smart_villages.debug_logging then
|
|
minetest.log("action","a villager is going home")
|
|
end
|
|
self.destination=self:get_home():get_bed()
|
|
if not self.destination then
|
|
minetest.log("warning","villager couldn't find his bed")
|
|
return
|
|
end
|
|
if smart_villages.debug_logging then
|
|
minetest.log("info","his bed is at:" .. self.destination.x .. ",".. self.destination.y .. ",".. self.destination.z)
|
|
end
|
|
self:set_state("goto_dest")
|
|
end
|
|
--list all states
|
|
local newStates={}
|
|
if sprop.search_idle then
|
|
newStates.SEARCH = {number=0,
|
|
func=s_search_idle,
|
|
to_state=to_search_idle}
|
|
else
|
|
newStates.SEARCH = {number=0,
|
|
func=walk_randomly,
|
|
to_state=to_walk_randomly}
|
|
end
|
|
local i = 0
|
|
if not sprop.night_active then
|
|
newStates.GO_OUT = {number=1,
|
|
func=function() return true end,
|
|
to_state=function(self)
|
|
if smart_villages.debug_logging then
|
|
minetest.log("action","a villager stood up and is going outside")
|
|
end
|
|
self.destination=self:get_home():get_door()
|
|
self:set_state("goto_dest")
|
|
end,
|
|
next_state=newStates.SEARCH}
|
|
newStates.SLEEP = {number=2,
|
|
func=s_sleep,
|
|
to_state=to_sleep,
|
|
next_state=newStates.GO_OUT}
|
|
newStates.WALK_HOME = {number=3,
|
|
func=function() return true end,
|
|
self_condition = function (self)
|
|
if self:has_home() then
|
|
if not self:get_home():get_bed() then
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
if minetest.get_timeofday() < 0.2 or minetest.get_timeofday() > 0.76 then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end,
|
|
to_state=to_walk_home,
|
|
next_state=newStates.SLEEP}
|
|
i = 3
|
|
end
|
|
for k, v in pairs(actions) do
|
|
i = i + 1
|
|
newStates[k] = v
|
|
newStates[k].number = i
|
|
end
|
|
--job definitions
|
|
local function on_start(self)
|
|
self.object:setacceleration{x = 0, y = -10, z = 0}
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
self.job_state = self:get_job().states.SEARCH
|
|
self.time_counters = {}
|
|
self.path = nil
|
|
self:get_job().states.SEARCH.to_state(self)
|
|
end
|
|
local function on_stop(self)
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
self.job_state = nil
|
|
self.time_counters = nil
|
|
self.path = nil
|
|
self:set_animation(smart_villages.animation_frames.STAND)
|
|
end
|
|
local function on_resume(self)
|
|
local job = self:get_job()
|
|
self.object:setacceleration{x = 0, y = -10, z = 0}
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
self.job_state = job.states.SEARCH
|
|
job.states.SEARCH.to_state(self)
|
|
end
|
|
local function on_pause(self)
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
self.job_state = nil
|
|
self:set_animation(smart_villages.animation_frames.STAND)
|
|
end
|
|
local function on_step(self)
|
|
if self.job_state.next_state ~= nil then
|
|
if self.job_state.func==nil or self.job_state.func(self) then
|
|
self.job_state=self.job_state.next_state
|
|
if self.job_state.to_state ~= nil then
|
|
self.job_state.to_state(self)
|
|
end
|
|
end
|
|
else
|
|
if self.job_state.func==nil or self.job_state.func(self) then
|
|
smart_villages.func.get_back_to_searching(self)
|
|
end
|
|
end
|
|
end
|
|
smart_villages.register_job("smart_villages:"..job_name, {
|
|
description = "smart_villages job : "..job_description,
|
|
inventory_image = "default_paper.png^memorandum_letters.png",
|
|
on_start = on_start,
|
|
on_stop = on_stop,
|
|
on_resume = on_resume,
|
|
on_pause = on_pause,
|
|
on_step = on_step,
|
|
states = newStates
|
|
})
|
|
end
|
|
|
|
function smart_villages.func.get_back_to_searching(self)
|
|
local myJob = self:get_job()
|
|
if myJob and myJob.states and myJob.states.SEARCH then
|
|
self.job_state = myJob.states.SEARCH
|
|
if myJob.states.SEARCH.to_state then
|
|
myJob.states.SEARCH.to_state(self)
|
|
end
|
|
end
|
|
end |