Teach villagers to open and walk through doors

This commit is contained in:
Wuzzy 2024-03-24 13:26:31 +01:00
parent 5542edf5d9
commit eb30490dff
3 changed files with 78 additions and 28 deletions

View File

@ -75,7 +75,7 @@ end
rp_mobs.microtasks = {} rp_mobs.microtasks = {}
rp_mobs.microtasks.pathfind_and_walk_to = function(target_pos, walk_speed, jump_strength, set_yaw, searchdistance, max_jump, max_drop) rp_mobs.microtasks.pathfind_and_walk_to = function(start_pos, target_pos, walk_speed, jump_strength, set_yaw, searchdistance, max_jump, max_drop)
local mtask = {} local mtask = {}
mtask.label = "pathfind and walk to coordinate" mtask.label = "pathfind and walk to coordinate"
mtask.on_start = function(self, mob) mtask.on_start = function(self, mob)
@ -89,8 +89,10 @@ rp_mobs.microtasks.pathfind_and_walk_to = function(target_pos, walk_speed, jump_
self.statedata.stuck_last_position = nil self.statedata.stuck_last_position = nil
self.statedata.stuck_recheck_timer = 0 self.statedata.stuck_recheck_timer = 0
local start_pos = mob.object:get_pos() if not start_pos then
start_pos = mob.object:get_pos()
start_pos.y = math.floor(start_pos.y) start_pos.y = math.floor(start_pos.y)
end
start_pos = vector.round(start_pos) start_pos = vector.round(start_pos)
local path = minetest.find_path(start_pos, target_pos, searchdistance, max_jump, max_drop, "A*") local path = minetest.find_path(start_pos, target_pos, searchdistance, max_jump, max_drop, "A*")
self.statedata.path = path self.statedata.path = path

View File

@ -101,13 +101,24 @@ end
-- This algorithm performs up to 5 path searches so it is less efficient -- This algorithm performs up to 5 path searches so it is less efficient
-- than calling minetest.find_path. -- than calling minetest.find_path.
-- --
-- Returns: list of paths on success, nil on failure. -- Returns: Lists of 'goals' the mob has to reach to get to the
-- destination. Each goal is a table with a 'goal_type' field and other fields.
-- Possible goal types:
--
-- { goal_type = "path", path = <path> }
-- Path goal. path is a list of positions the mob has to walk in that order
--
-- { goal_type = "door", pos = <door position> }
-- Door obstacle. Mob may have to interact with the door in order to
-- get through. pos is the position of the lower door segment.
--
-- If no path was found, returns nil.
local find_path_advanced = function(pos1, pos2, searchdistance, max_jump, max_drop) local find_path_advanced = function(pos1, pos2, searchdistance, max_jump, max_drop)
local algorithm = "A*_noprefetch" local algorithm = "A*_noprefetch"
-- First check if we can find a direct path -- First check if we can find a direct path
local path = minetest.find_path(pos1, pos2, searchdistance, max_jump, max_drop, algorithm) local path = minetest.find_path(pos1, pos2, searchdistance, max_jump, max_drop, algorithm)
if path then if path then
return { path } return { { goal_type = "path", path = path } }
end end
local doorarea_min = vector.add(pos2, vector.new(-12, -6, -12)) local doorarea_min = vector.add(pos2, vector.new(-12, -6, -12))
local doorarea_max = vector.add(pos2, vector.new(12, 6, 12)) local doorarea_max = vector.add(pos2, vector.new(12, 6, 12))
@ -172,8 +183,12 @@ local find_path_advanced = function(pos1, pos2, searchdistance, max_jump, max_dr
if path1 then if path1 then
local path2 = minetest.find_path(splitpos2, pos2, searchdistance, max_jump, max_drop, algorithm) local path2 = minetest.find_path(splitpos2, pos2, searchdistance, max_jump, max_drop, algorithm)
if path2 then if path2 then
-- Both paths found. Return them -- Both paths found. Join them by putting the door between them
return { path1, path2 } return {
{ goal_type = "path", path = path1 },
{ goal_type = "door", pos = doorpos },
{ goal_type = "path", path = path2 },
}
end end
end end
-- On failure, try it again but do it from start to the *other* side of the door (splitpos2) first. -- On failure, try it again but do it from start to the *other* side of the door (splitpos2) first.
@ -181,7 +196,11 @@ local find_path_advanced = function(pos1, pos2, searchdistance, max_jump, max_dr
if path3 then if path3 then
local path4 = minetest.find_path(splitpos1, splitpos2, searchdistance, max_jump, max_drop, algorithm) local path4 = minetest.find_path(splitpos1, splitpos2, searchdistance, max_jump, max_drop, algorithm)
if path4 then if path4 then
return { path3, path4 } return {
{ goal_type = "path", path = path3 },
{ goal_type = "door", pos = doorpos },
{ goal_type = "path", path = path4 },
}
end end
end end
end end
@ -247,9 +266,9 @@ local find_reachable_node = function(startpos, nodenames, searchdistance, under_
searchpos = npos searchpos = npos
end end
if searchpos then if searchpos then
local path_to_node = find_path_advanced(startpos, searchpos, searchdistance, MAX_JUMP, MAX_DROP) local goals = find_path_advanced(startpos, searchpos, searchdistance, MAX_JUMP, MAX_DROP)
if path_to_node then if goals then
return npos, path_to_node return npos, goals
end end
end end
table.remove(nodes, r) table.remove(nodes, r)
@ -281,6 +300,18 @@ local microtask_find_new_home_bed = rp_mobs.create_microtask({
end, end,
}) })
local create_microtask_open_door = function(door_pos)
return rp_mobs.create_microtask({
label = "open door",
singlestep = true,
on_step = function(self, mob)
if door.is_open(door_pos) == false then
door.toggle_door(door_pos)
end
end,
})
end
local movement_decider = function(task_queue, mob) local movement_decider = function(task_queue, mob)
local task_stand = rp_mobs.create_task({label="stand still"}) local task_stand = rp_mobs.create_task({label="stand still"})
local yaw = math.random(0, 360) / 360 * (math.pi*2) local yaw = math.random(0, 360) / 360 * (math.pi*2)
@ -302,17 +333,19 @@ local movement_decider = function(task_queue, mob)
if mob._custom_state.home_bed then if mob._custom_state.home_bed then
local mobpos = mob.object:get_pos() local mobpos = mob.object:get_pos()
local searchpos = find_free_horizontal_neighbor(mob._custom_state.home_bed) local searchpos = find_free_horizontal_neighbor(mob._custom_state.home_bed)
local pathlist_to_bed = find_path_advanced(mobpos, searchpos, HOME_BED_PATHFIND_DISTANCE, MAX_JUMP, MAX_DROP) local goals = find_path_advanced(mobpos, searchpos, HOME_BED_PATHFIND_DISTANCE, MAX_JUMP, MAX_DROP)
if pathlist_to_bed then if goals then
local path = pathlist_to_bed[1] if goals[1] and goals[1].goal_type == "path" then
local path = goals[1].path
local target = path[#path] local target = path[#path]
local mt_walk_to_bed = rp_mobs.microtasks.pathfind_and_walk_to(target, WALK_SPEED, JUMP_STRENGTH, true, HOME_BED_PATHFIND_DISTANCE, MAX_JUMP, MAX_DROP) local mt_walk_to_bed = rp_mobs.microtasks.pathfind_and_walk_to(nil, target, WALK_SPEED, JUMP_STRENGTH, true, HOME_BED_PATHFIND_DISTANCE, MAX_JUMP, MAX_DROP)
mt_walk_to_bed.start_animation = "walk" mt_walk_to_bed.start_animation = "walk"
local task_walk_to_bed = rp_mobs.create_task({label="walk to bed"}) local task_walk_to_bed = rp_mobs.create_task({label="walk to bed"})
rp_mobs.add_microtask_to_task(mob, mt_walk_to_bed, task_walk_to_bed) rp_mobs.add_microtask_to_task(mob, mt_walk_to_bed, task_walk_to_bed)
rp_mobs.add_task_to_task_queue(task_queue, task_walk_to_bed) rp_mobs.add_task_to_task_queue(task_queue, task_walk_to_bed)
end end
end end
end
elseif day_phase == "day" then elseif day_phase == "day" then
local r = math.random(1, 2) local r = math.random(1, 2)
local profession = mob._custom_state.profession local profession = mob._custom_state.profession
@ -357,14 +390,29 @@ local movement_decider = function(task_queue, mob)
if targetnodes then if targetnodes then
-- Go to workplace/recreational node at day -- Go to workplace/recreational node at day
local mobpos = mob.object:get_pos() local mobpos = mob.object:get_pos()
local targetpos, pathlist_to_target = find_reachable_node(mobpos, targetnodes, WORK_DISTANCE, under_air) local targetpos, goals = find_reachable_node(mobpos, targetnodes, WORK_DISTANCE, under_air)
if targetpos and pathlist_to_target then if targetpos and goals and #goals > 0 then
local path = pathlist_to_target[1]
local target = path[#path]
local mt_walk_to_target = rp_mobs.microtasks.pathfind_and_walk_to(target, WALK_SPEED, JUMP_STRENGTH, true, WORK_DISTANCE, MAX_JUMP, MAX_DROP)
mt_walk_to_target.start_animation = "walk"
local task_walk_to_target = rp_mobs.create_task({label="walk to recreation/workplace"}) local task_walk_to_target = rp_mobs.create_task({label="walk to recreation/workplace"})
for g=1, #goals do
local goal = goals[g]
-- Open door
if goal.goal_type == "door" then
local mt_open_door = create_microtask_open_door(goal.pos)
mt_open_door.start_animation = "idle"
rp_mobs.add_microtask_to_task(mob, mt_open_door, task_walk_to_target)
-- Traverse path
elseif goal.goal_type == "path" then
local path = goal.path
local target = path[#path]
local start = path[1]
local mt_walk_to_target = rp_mobs.microtasks.pathfind_and_walk_to(start, target, WALK_SPEED, JUMP_STRENGTH, true, WORK_DISTANCE, MAX_JUMP, MAX_DROP)
mt_walk_to_target.start_animation = "walk"
rp_mobs.add_microtask_to_task(mob, mt_walk_to_target, task_walk_to_target) rp_mobs.add_microtask_to_task(mob, mt_walk_to_target, task_walk_to_target)
else
minetest.log("error", "[rp_mobs_mobs] Villager walk algorithm: Invalid goal_type!")
return
end
end
rp_mobs.add_task_to_task_queue(task_queue, task_walk_to_target) rp_mobs.add_task_to_task_queue(task_queue, task_walk_to_target)
end end
end end

View File

@ -1,5 +1,5 @@
name = rp_mobs_mobs name = rp_mobs_mobs
title = Repixture Mobs title = Repixture Mobs
description = Repixture Mobs: Mob definitions, behavior, graphics, models, sounds description = Repixture Mobs: Mob definitions, behavior, graphics, models, sounds
depends = rp_mobs, rp_achievements, rp_tnt depends = rp_mobs, rp_achievements, rp_tnt, rp_door
optional_depends = rp_nav optional_depends = rp_nav