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.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 = {}
mtask.label = "pathfind and walk to coordinate"
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_recheck_timer = 0
local start_pos = mob.object:get_pos()
start_pos.y = math.floor(start_pos.y)
if not start_pos then
start_pos = mob.object:get_pos()
start_pos.y = math.floor(start_pos.y)
end
start_pos = vector.round(start_pos)
local path = minetest.find_path(start_pos, target_pos, searchdistance, max_jump, max_drop, "A*")
self.statedata.path = path

View File

@ -101,13 +101,24 @@ end
-- This algorithm performs up to 5 path searches so it is less efficient
-- 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 algorithm = "A*_noprefetch"
-- First check if we can find a direct path
local path = minetest.find_path(pos1, pos2, searchdistance, max_jump, max_drop, algorithm)
if path then
return { path }
return { { goal_type = "path", path = path } }
end
local doorarea_min = 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
local path2 = minetest.find_path(splitpos2, pos2, searchdistance, max_jump, max_drop, algorithm)
if path2 then
-- Both paths found. Return them
return { path1, path2 }
-- Both paths found. Join them by putting the door between them
return {
{ goal_type = "path", path = path1 },
{ goal_type = "door", pos = doorpos },
{ goal_type = "path", path = path2 },
}
end
end
-- 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
local path4 = minetest.find_path(splitpos1, splitpos2, searchdistance, max_jump, max_drop, algorithm)
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
@ -247,9 +266,9 @@ local find_reachable_node = function(startpos, nodenames, searchdistance, under_
searchpos = npos
end
if searchpos then
local path_to_node = find_path_advanced(startpos, searchpos, searchdistance, MAX_JUMP, MAX_DROP)
if path_to_node then
return npos, path_to_node
local goals = find_path_advanced(startpos, searchpos, searchdistance, MAX_JUMP, MAX_DROP)
if goals then
return npos, goals
end
end
table.remove(nodes, r)
@ -281,6 +300,18 @@ local microtask_find_new_home_bed = rp_mobs.create_microtask({
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 task_stand = rp_mobs.create_task({label="stand still"})
local yaw = math.random(0, 360) / 360 * (math.pi*2)
@ -302,15 +333,17 @@ local movement_decider = function(task_queue, mob)
if mob._custom_state.home_bed then
local mobpos = mob.object:get_pos()
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)
if pathlist_to_bed then
local path = pathlist_to_bed[1]
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)
mt_walk_to_bed.start_animation = "walk"
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_task_to_task_queue(task_queue, task_walk_to_bed)
local goals = find_path_advanced(mobpos, searchpos, HOME_BED_PATHFIND_DISTANCE, MAX_JUMP, MAX_DROP)
if goals then
if goals[1] and goals[1].goal_type == "path" then
local path = goals[1].path
local target = path[#path]
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"
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_task_to_task_queue(task_queue, task_walk_to_bed)
end
end
end
elseif day_phase == "day" then
@ -357,14 +390,29 @@ local movement_decider = function(task_queue, mob)
if targetnodes then
-- Go to workplace/recreational node at day
local mobpos = mob.object:get_pos()
local targetpos, pathlist_to_target = find_reachable_node(mobpos, targetnodes, WORK_DISTANCE, under_air)
if targetpos and pathlist_to_target 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 targetpos, goals = find_reachable_node(mobpos, targetnodes, WORK_DISTANCE, under_air)
if targetpos and goals and #goals > 0 then
local task_walk_to_target = rp_mobs.create_task({label="walk to recreation/workplace"})
rp_mobs.add_microtask_to_task(mob, mt_walk_to_target, task_walk_to_target)
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)
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)
end
end

View File

@ -1,5 +1,5 @@
name = rp_mobs_mobs
title = Repixture Mobs
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