Pathfinding (WIP): Use MarkBu's pathfinder mod as the new pathfinder.
Actions: Modify walk_to_pos to use new pathfinding code. Allow support for giving positions as strings or tables referring to specific places on Places Map. Spawner: Assign beds to NPCs. Increase amount of NPCs now being spawned as they are able to reach top floor beds. Places: Slight overhaul and changes. Add more info to places. NPC: Hide nametag.
This commit is contained in:
parent
d4e8744cfb
commit
adeeecd43e
@ -194,12 +194,20 @@ function npc.actions.walk_step(self, args)
|
|||||||
|
|
||||||
if dir == npc.direction.north then
|
if dir == npc.direction.north then
|
||||||
vel = {x=0, y=0, z=speed}
|
vel = {x=0, y=0, z=speed}
|
||||||
|
elseif dir == npc.direction.north_east then
|
||||||
|
vel = {x=speed, y=0, z=speed}
|
||||||
elseif dir == npc.direction.east then
|
elseif dir == npc.direction.east then
|
||||||
vel = {x=speed, y=0, z=0}
|
vel = {x=speed, y=0, z=0}
|
||||||
|
elseif dir == npc.direction.south_east then
|
||||||
|
vel = {x=speed, y=0, z=-speed}
|
||||||
elseif dir == npc.direction.south then
|
elseif dir == npc.direction.south then
|
||||||
vel = {x=0, y=0, z=-speed}
|
vel = {x=0, y=0, z=-speed}
|
||||||
|
elseif dir == npc.direction.south_west then
|
||||||
|
vel = {x=-speed, y=0, z=-speed}
|
||||||
elseif dir == npc.direction.west then
|
elseif dir == npc.direction.west then
|
||||||
vel = {x=-speed, y=0, z=0}
|
vel = {x=-speed, y=0, z=0}
|
||||||
|
elseif dir == npc.direction.north_west then
|
||||||
|
vel = {x=-speed, y=0, z=speed}
|
||||||
end
|
end
|
||||||
-- Rotate NPC
|
-- Rotate NPC
|
||||||
npc.actions.rotate(self, {dir=dir})
|
npc.actions.rotate(self, {dir=dir})
|
||||||
@ -403,13 +411,67 @@ end
|
|||||||
-- walking from one place to another, operating a furnace, storing or taking
|
-- walking from one place to another, operating a furnace, storing or taking
|
||||||
-- items from a chest, are provided here.
|
-- items from a chest, are provided here.
|
||||||
|
|
||||||
|
local function get_pos_argument(self, pos)
|
||||||
|
minetest.log("Type of pos: "..dump(type(pos)))
|
||||||
|
-- Check which type of position argument we received
|
||||||
|
if type(pos) == "table" then
|
||||||
|
minetest.log("Received table pos: "..dump(pos))
|
||||||
|
-- Check if table is position
|
||||||
|
if pos.x ~= nil and pos.y ~= nil and pos.z ~= nil then
|
||||||
|
-- Position received, return position
|
||||||
|
return pos
|
||||||
|
elseif pos.place_type ~= nil then
|
||||||
|
-- Received table in the following format:
|
||||||
|
-- {place_type = "", index = 1, use_access_node = false}
|
||||||
|
local index = pos.index or 1
|
||||||
|
local use_access_node = pos.use_access_node or false
|
||||||
|
local places = npc.places.get_by_type(self, pos.place_type)
|
||||||
|
-- Check index is valid on the places map
|
||||||
|
if #places >= index then
|
||||||
|
-- Check if access node is desired
|
||||||
|
if use_access_node then
|
||||||
|
-- Return actual node pos
|
||||||
|
return places[index].access_node
|
||||||
|
else
|
||||||
|
-- Return node pos that allows access to node
|
||||||
|
return places[index].pos
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif type(pos) == "string" then
|
||||||
|
-- Received name of place, so we are going to look for the actual pos
|
||||||
|
local places_pos = npc.places.get_by_type(self, pos)
|
||||||
|
-- Return nil if no position found
|
||||||
|
if places_pos == nil or #places_pos == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
-- Check if received more than one position
|
||||||
|
if #places_pos > 1 then
|
||||||
|
-- Check all places, return owned if existent, else return the first one
|
||||||
|
for i = 1, #places_pos do
|
||||||
|
if places_pos[i].status == "owned" then
|
||||||
|
return places_pos[i].pos
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Return the first position only if it couldn't find an owned
|
||||||
|
-- place, or if it there is only oneg
|
||||||
|
return places_pos[1].pos
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- This function allows a NPC to use a furnace using only items from
|
-- This function allows a NPC to use a furnace using only items from
|
||||||
-- its own inventory. Fuel is not provided. Once the furnace is finished
|
-- its own inventory. Fuel is not provided. Once the furnace is finished
|
||||||
-- with the fuel items the NPC will take whatever was cooked and whatever
|
-- with the fuel items the NPC will take whatever was cooked and whatever
|
||||||
-- remained to cook. The function received the position of the furnace
|
-- remained to cook. The function received the position of the furnace
|
||||||
-- to use, and the item to cook in furnace. Item is an itemstring
|
-- to use, and the item to cook in furnace. Item is an itemstring
|
||||||
function npc.actions.use_furnace(self, args)
|
function npc.actions.use_furnace(self, args)
|
||||||
local pos = args.pos
|
local pos = get_pos_argument(self, args.pos)
|
||||||
|
if pos == nil then
|
||||||
|
minetest.log("[advanced_npc] WARNING Got nil position in 'use_furnace' using args.pos: "..dump(args.pos))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local item = args.item
|
local item = args.item
|
||||||
local freeze = args.freeze
|
local freeze = args.freeze
|
||||||
-- Define which items are usable as fuels. The NPC
|
-- Define which items are usable as fuels. The NPC
|
||||||
@ -530,7 +592,11 @@ end
|
|||||||
-- This function makes the NPC lay or stand up from a bed. The
|
-- This function makes the NPC lay or stand up from a bed. The
|
||||||
-- pos is the location of the bed, action can be lay or get up
|
-- pos is the location of the bed, action can be lay or get up
|
||||||
function npc.actions.use_bed(self, args)
|
function npc.actions.use_bed(self, args)
|
||||||
local pos = args.pos
|
local pos = get_pos_argument(self, args.pos)
|
||||||
|
if pos == nil then
|
||||||
|
minetest.log("[advanced_npc] WARNING Got nil position in 'use_bed' using args.pos: "..dump(args.pos))
|
||||||
|
return
|
||||||
|
end
|
||||||
local action = args.action
|
local action = args.action
|
||||||
local node = minetest.get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
minetest.log(dump(node))
|
minetest.log(dump(node))
|
||||||
@ -586,7 +652,11 @@ end
|
|||||||
-- This function makes the NPC lay or stand up from a bed. The
|
-- This function makes the NPC lay or stand up from a bed. The
|
||||||
-- pos is the location of the bed, action can be lay or get up
|
-- pos is the location of the bed, action can be lay or get up
|
||||||
function npc.actions.use_sittable(self, args)
|
function npc.actions.use_sittable(self, args)
|
||||||
local pos = args.pos
|
local pos = get_pos_argument(self, args.pos)
|
||||||
|
if pos == nil then
|
||||||
|
minetest.log("[advanced_npc] WARNING Got nil position in 'use_sittable' using args.pos: "..dump(args.pos))
|
||||||
|
return
|
||||||
|
end
|
||||||
local action = args.action
|
local action = args.action
|
||||||
local node = minetest.get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
|
|
||||||
@ -621,13 +691,24 @@ end
|
|||||||
-- for the moving from v1 to v2
|
-- for the moving from v1 to v2
|
||||||
function npc.actions.get_direction(v1, v2)
|
function npc.actions.get_direction(v1, v2)
|
||||||
local dir = vector.subtract(v2, v1)
|
local dir = vector.subtract(v2, v1)
|
||||||
if dir.x ~= 0 then
|
|
||||||
|
if dir.x ~= 0 and dir.z ~= 0 then
|
||||||
|
if dir.x > 0 and dir.z > 0 then
|
||||||
|
return npc.direction.north_east
|
||||||
|
elseif dir.x > 0 and dir.z < 0 then
|
||||||
|
return npc.direction.south_east
|
||||||
|
elseif dir.x < 0 and dir.z > 0 then
|
||||||
|
return npc.direction.north_west
|
||||||
|
elseif dir.x < 0 and dir.z < 0 then
|
||||||
|
return npc.direction.south_west
|
||||||
|
end
|
||||||
|
elseif dir.x ~= 0 and dir.z == 0 then
|
||||||
if dir.x > 0 then
|
if dir.x > 0 then
|
||||||
return npc.direction.east
|
return npc.direction.east
|
||||||
else
|
else
|
||||||
return npc.direction.west
|
return npc.direction.west
|
||||||
end
|
end
|
||||||
elseif dir.z ~= 0 then
|
elseif dir.z ~= 0 and dir.x == 0 then
|
||||||
if dir.z > 0 then
|
if dir.z > 0 then
|
||||||
return npc.direction.north
|
return npc.direction.north
|
||||||
else
|
else
|
||||||
@ -636,6 +717,7 @@ function npc.actions.get_direction(v1, v2)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- This function can be used to make the NPC walk from one
|
-- This function can be used to make the NPC walk from one
|
||||||
-- position to another. If the optional parameter walkable_nodes
|
-- position to another. If the optional parameter walkable_nodes
|
||||||
-- is included, which is a table of node names, these nodes are
|
-- is included, which is a table of node names, these nodes are
|
||||||
@ -643,14 +725,20 @@ end
|
|||||||
-- path.
|
-- path.
|
||||||
function npc.actions.walk_to_pos(self, args)
|
function npc.actions.walk_to_pos(self, args)
|
||||||
-- Get arguments for this task
|
-- Get arguments for this task
|
||||||
local end_pos = args.end_pos
|
local end_pos = get_pos_argument(self, args.end_pos)
|
||||||
|
if end_pos == nil then
|
||||||
|
minetest.log("[advanced_npc] WARNING Got nil position in 'walk_to_pos' using args.pos: "..dump(args.end_pos))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local enforce_move = args.enforce_move or true
|
||||||
local walkable_nodes = args.walkable
|
local walkable_nodes = args.walkable
|
||||||
|
|
||||||
-- Round start_pos to make sure it can find start and end
|
-- Round start_pos to make sure it can find start and end
|
||||||
local start_pos = vector.round(self.object:getpos())
|
local start_pos = vector.round(self.object:getpos())
|
||||||
-- Use y of end_pos (this can only be done assuming flat terrain)
|
-- Use y of end_pos (this can only be done assuming flat terrain)
|
||||||
start_pos.y = self.object:getpos().y
|
--start_pos.y = self.object:getpos().y
|
||||||
minetest.log("Walk to pos: Using start position: "..dump(start_pos))
|
minetest.log("[advanced_npc] INFO walk_to_pos: Start pos: "..minetest.pos_to_string(start_pos))
|
||||||
|
minetest.log("[advanced_npc] INFO walk_to_pos: End pos: "..minetest.pos_to_string(end_pos))
|
||||||
|
|
||||||
-- Set walkable nodes to empty if the parameter hasn't been used
|
-- Set walkable nodes to empty if the parameter hasn't been used
|
||||||
if walkable_nodes == nil then
|
if walkable_nodes == nil then
|
||||||
@ -658,10 +746,24 @@ function npc.actions.walk_to_pos(self, args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Find path
|
-- Find path
|
||||||
local path = pathfinder.find_path(start_pos, end_pos, 20, walkable_nodes)
|
--local path = pathfinder.find_path(start_pos, end_pos, 20, walkable_nodes)
|
||||||
|
local path = pathfinder.find_path(start_pos, end_pos, self)
|
||||||
|
|
||||||
if path ~= nil then
|
if path ~= nil and #path > 1 then
|
||||||
minetest.log("[advanced_npc] Found path to node: "..minetest.pos_to_string(end_pos))
|
-- Get details from path nodes
|
||||||
|
-- This might get moved to proper place, pathfinder.lua code
|
||||||
|
local path_detail = {}
|
||||||
|
for i = 1, #path do
|
||||||
|
local node = minetest.get_node(path[i])
|
||||||
|
table.insert(path_detail, {pos=path[i], type=npc.pathfinder.is_good_node(node, {})})
|
||||||
|
end
|
||||||
|
path = path_detail
|
||||||
|
|
||||||
|
--minetest.log("Found path: "..dump(path))
|
||||||
|
|
||||||
|
--minetest.log("Path detail: "..dump(path_detail))
|
||||||
|
--minetest.log("New path: "..dump(path))
|
||||||
|
minetest.log("[advanced_npc] INFO walk_to_pos Found path to node: "..minetest.pos_to_string(end_pos))
|
||||||
-- Store path
|
-- Store path
|
||||||
self.actions.walking.path = path
|
self.actions.walking.path = path
|
||||||
|
|
||||||
@ -689,11 +791,11 @@ function npc.actions.walk_to_pos(self, args)
|
|||||||
-- Get direction to move from path[i] to path[i+1]
|
-- Get direction to move from path[i] to path[i+1]
|
||||||
local dir = npc.actions.get_direction(path[i].pos, path[i+1].pos)
|
local dir = npc.actions.get_direction(path[i].pos, path[i+1].pos)
|
||||||
-- Check if next node is a door, if it is, open it, then walk
|
-- Check if next node is a door, if it is, open it, then walk
|
||||||
if path[i+1].type == pathfinder.node_types.openable then
|
if path[i+1].type == npc.pathfinder.node_types.openable then
|
||||||
-- Check if door is already open
|
-- Check if door is already open
|
||||||
local node = minetest.get_node(path[i+1].pos)
|
local node = minetest.get_node(path[i+1].pos)
|
||||||
if npc.actions.get_openable_node_state(node, dir) == npc.actions.const.doors.state.CLOSED then
|
if npc.actions.get_openable_node_state(node, dir) == npc.actions.const.doors.state.CLOSED then
|
||||||
minetest.log("Opening action to open door")
|
--minetest.log("Opening action to open door")
|
||||||
-- Stop to open door, this avoids misplaced movements later on
|
-- Stop to open door, this avoids misplaced movements later on
|
||||||
npc.add_action(self, npc.actions.cmd.STAND, {dir=dir})
|
npc.add_action(self, npc.actions.cmd.STAND, {dir=dir})
|
||||||
-- Open door
|
-- Open door
|
||||||
@ -701,29 +803,33 @@ function npc.actions.walk_to_pos(self, args)
|
|||||||
|
|
||||||
door_opened = true
|
door_opened = true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add walk action to action queue
|
-- Add walk action to action queue
|
||||||
npc.add_action(self, npc.actions.cmd.WALK_STEP, {dir = dir, speed = speed, target_pos = path[i+1].pos})
|
npc.add_action(self, npc.actions.cmd.WALK_STEP, {dir = dir, speed = speed, target_pos = path[i+1].pos})
|
||||||
|
|
||||||
if door_opened then
|
if door_opened then
|
||||||
-- Stop to close door, this avoids misplaced movements later on
|
-- Stop to close door, this avoids misplaced movements later on
|
||||||
local x_adj, z_adj = 0, 0
|
-- local x_adj, z_adj = 0, 0
|
||||||
if dir == 0 then
|
-- if dir == 0 then
|
||||||
z_adj = 0.1
|
-- z_adj = 0.1
|
||||||
elseif dir == 1 then
|
-- elseif dir == 1 then
|
||||||
x_adj = 0.1
|
-- x_adj = 0.1
|
||||||
elseif dir == 2 then
|
-- elseif dir == 2 then
|
||||||
z_adj = -0.1
|
-- z_adj = -0.1
|
||||||
elseif dir == 3 then
|
-- elseif dir == 3 then
|
||||||
x_adj = -0.1
|
-- x_adj = -0.1
|
||||||
end
|
-- end
|
||||||
local pos_on_close = {x=path[i+1].pos.x + x_adj, y=path[i+1].pos.y + 1, z=path[i+1].pos.z + z_adj}
|
-- local pos_on_close = {x=path[i+1].pos.x + x_adj, y=path[i+1].pos.y + 1, z=path[i+1].pos.z + z_adj}
|
||||||
npc.add_action(self, npc.actions.cmd.STAND, {dir=(dir + 2)% 4, pos=pos_on_close})
|
-- Add extra walk step to ensure that one is standing at other side of openable node
|
||||||
-- Close door
|
npc.add_action(self, npc.actions.cmd.WALK_STEP, {dir = dir, speed = speed, target_pos = path[i+2].pos})
|
||||||
npc.add_action(self, npc.actions.cmd.USE_OPENABLE, {pos=path[i+1].pos, action=npc.actions.const.doors.action.CLOSE})
|
-- Stop to close the door
|
||||||
|
npc.add_action(self, npc.actions.cmd.STAND, {dir=(dir + 2) % 4 })--, pos=pos_on_close})
|
||||||
|
-- Close door
|
||||||
|
npc.add_action(self, npc.actions.cmd.USE_OPENABLE, {pos=path[i+1].pos, action=npc.actions.const.doors.action.CLOSE})
|
||||||
|
|
||||||
door_opened = false
|
door_opened = false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -733,6 +839,12 @@ function npc.actions.walk_to_pos(self, args)
|
|||||||
npc.add_action(self, npc.actions.cmd.SET_INTERVAL, {interval=1, freeze=true})
|
npc.add_action(self, npc.actions.cmd.SET_INTERVAL, {interval=1, freeze=true})
|
||||||
|
|
||||||
else
|
else
|
||||||
minetest.log("Unable to find path.")
|
-- Unable to find path
|
||||||
|
minetest.log("[advanced_npc] INFO walk_to_pos Unable to find path. Teleporting to: "..minetest.pos_to_string(end_pos))
|
||||||
|
-- Check if movement is enforced
|
||||||
|
if enforce_move then
|
||||||
|
-- Move to end pos
|
||||||
|
self.object:moveto(end_pos)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -10,9 +10,11 @@
|
|||||||
-- (https://github.com/Yonaba/Jumper).
|
-- (https://github.com/Yonaba/Jumper).
|
||||||
-- Mapping algorithm: transforms a Minetest map surface to a 2d grid.
|
-- Mapping algorithm: transforms a Minetest map surface to a 2d grid.
|
||||||
|
|
||||||
pathfinder = {}
|
npc.pathfinder = {}
|
||||||
|
|
||||||
pathfinder.node_types = {
|
local pathfinder = {}
|
||||||
|
|
||||||
|
npc.pathfinder.node_types = {
|
||||||
start = 0,
|
start = 0,
|
||||||
goal = 1,
|
goal = 1,
|
||||||
walkable = 2,
|
walkable = 2,
|
||||||
@ -20,7 +22,7 @@ pathfinder.node_types = {
|
|||||||
non_walkable = 4
|
non_walkable = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
pathfinder.nodes = {
|
npc.pathfinder.nodes = {
|
||||||
openable_prefix = {
|
openable_prefix = {
|
||||||
"doors:",
|
"doors:",
|
||||||
"cottages:gate",
|
"cottages:gate",
|
||||||
@ -32,7 +34,7 @@ pathfinder.nodes = {
|
|||||||
-- This function uses the mapping functions and the A* algorithm implementation
|
-- This function uses the mapping functions and the A* algorithm implementation
|
||||||
-- of the Jumper library to find a path from start_pos to end_pos. The range is
|
-- of the Jumper library to find a path from start_pos to end_pos. The range is
|
||||||
-- an extra amount of nodes to search in both the x and z coordinates.
|
-- an extra amount of nodes to search in both the x and z coordinates.
|
||||||
function pathfinder.find_path(start_pos, end_pos, range, walkable_nodes)
|
function npc.pathfinder.find_path(start_pos, end_pos, range, walkable_nodes)
|
||||||
-- Check that start and end position are not the same
|
-- Check that start and end position are not the same
|
||||||
if start_pos.x == end_pos.x and start_pos.z == end_pos.z then
|
if start_pos.x == end_pos.x and start_pos.z == end_pos.z then
|
||||||
return nil
|
return nil
|
||||||
@ -69,13 +71,16 @@ end
|
|||||||
|
|
||||||
-- This function is used to determine if a node is walkable
|
-- This function is used to determine if a node is walkable
|
||||||
-- or openable, in which case is good to use when finding a path
|
-- or openable, in which case is good to use when finding a path
|
||||||
local function is_good_node(node, exceptions)
|
|
||||||
|
function npc.pathfinder.is_good_node(node, exceptions)
|
||||||
|
--local function is_good_node(node, exceptions)
|
||||||
-- Is openable is to support doors, fence gates and other
|
-- Is openable is to support doors, fence gates and other
|
||||||
-- doors from other mods. Currently, default doors, gates
|
-- doors from other mods. Currently, default doors, gates
|
||||||
-- and cottages doors are supported.
|
-- and cottages doors are supported.
|
||||||
--minetest.log("Is good node: "..dump(node))
|
--minetest.log("Is good node: "..dump(node))
|
||||||
local is_openable = false
|
local is_openable = false
|
||||||
for _,node_prefix in pairs(pathfinder.nodes.openable_prefix) do
|
for _,node_prefix in pairs(npc.pathfinder.nodes.openable_prefix) do
|
||||||
|
--for _,node_prefix in pairs(pathfinder.nodes.openable_prefix) do
|
||||||
local start_i,end_i = string.find(node.name, node_prefix)
|
local start_i,end_i = string.find(node.name, node_prefix)
|
||||||
if start_i ~= nil then
|
if start_i ~= nil then
|
||||||
is_openable = true
|
is_openable = true
|
||||||
@ -83,16 +88,20 @@ local function is_good_node(node, exceptions)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if node ~= nil and node.name ~= nil and not minetest.registered_nodes[node.name].walkable then
|
if node ~= nil and node.name ~= nil and not minetest.registered_nodes[node.name].walkable then
|
||||||
return pathfinder.node_types.walkable
|
--return pathfinder.node_types.walkable
|
||||||
|
return npc.pathfinder.node_types.walkable
|
||||||
elseif is_openable then
|
elseif is_openable then
|
||||||
return pathfinder.node_types.openable
|
return npc.pathfinder.node_types.openable
|
||||||
|
--return pathfinder.node_types.openable
|
||||||
else
|
else
|
||||||
for i = 1, #exceptions do
|
for i = 1, #exceptions do
|
||||||
if node.name == exceptions[i] then
|
if node.name == exceptions[i] then
|
||||||
return pathfinder.node_types.walkable
|
return npc.pathfinder.node_types.walkable
|
||||||
|
--return pathfinder.node_types.walkable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return pathfinder.node_types.non_walkable
|
return npc.pathfinder.node_types.non_walkable
|
||||||
|
--return pathfinder.node_types.non_walkable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,40 +53,42 @@ npc.places.nodes = {
|
|||||||
|
|
||||||
npc.places.PLACE_TYPE = {
|
npc.places.PLACE_TYPE = {
|
||||||
BED = {
|
BED = {
|
||||||
PRIMARY = "primary"
|
PRIMARY = "bed_primary"
|
||||||
},
|
},
|
||||||
SITTABLE = {
|
SITTABLE = {
|
||||||
PRIMARY = "primary"
|
PRIMARY = "sit_primary"
|
||||||
},
|
},
|
||||||
OPENABLE = {
|
OPENABLE = {
|
||||||
HOME_ENTRANCE_DOOR = "home_entrance_door"
|
HOME_ENTRANCE_DOOR = "home_entrance_door"
|
||||||
},
|
},
|
||||||
OTHER = {
|
OTHER = {
|
||||||
|
HOME_PLOTMARKER = "home_plotmarker",
|
||||||
HOME_INSIDE = "home_inside",
|
HOME_INSIDE = "home_inside",
|
||||||
HOME_OUTSIDE = "home_outside"
|
HOME_OUTSIDE = "home_outside"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function npc.places.add_public(self, place_name, place_type, pos)
|
function npc.places.add_public(self, place_name, place_type, pos, access_node)
|
||||||
--minetest.log("Place name: "..dump(place_name)..", type: "..dump(place_type))
|
--minetest.log("Place name: "..dump(place_name)..", type: "..dump(place_type))
|
||||||
self.places_map[place_name] = {type=place_type, pos=pos, status="shared"}
|
self.places_map[place_name] = {type=place_type, pos=pos, access_node=access_node or pos, status="shared"}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Adds a specific node to the NPC places, and modifies the
|
-- Adds a specific node to the NPC places, and modifies the
|
||||||
-- node metadata to identify the NPC as the owner. This allows
|
-- node metadata to identify the NPC as the owner. This allows
|
||||||
-- other NPCs to avoid to take this as their own.
|
-- other NPCs to avoid to take this as their own.
|
||||||
function npc.places.add_owned(self, place_name, place_type, pos)
|
function npc.places.add_owned(self, place_name, place_type, pos, access_node)
|
||||||
-- Get node metadata
|
-- Get node metadata
|
||||||
local meta = minetest.get_meta(pos)
|
--local meta = minetest.get_meta(pos)
|
||||||
-- Check if it is owned by an NPC?
|
-- Check if it is owned by an NPC?
|
||||||
if meta:get_string("npc_owner") == "" then
|
--if meta:get_string("npc_owner") == "" then
|
||||||
-- Set owned by NPC
|
-- Set owned by NPC
|
||||||
meta:set_string("npc_owner", self.npc_id)
|
--meta:set_string("npc_owner", self.npc_id)
|
||||||
-- Add place to list
|
-- Add place to list
|
||||||
npc.places.add_public(self, place_name, place_type, pos)
|
self.places_map[place_name] = {type=place_type, pos=pos, access_node=access_node or pos, status="owned"}
|
||||||
|
--npc.places.add_public(self, place_name, place_type, pos)
|
||||||
return true
|
return true
|
||||||
end
|
--end
|
||||||
return false
|
--return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function npc.places.get_by_type(self, place_type)
|
function npc.places.get_by_type(self, place_type)
|
||||||
@ -178,19 +180,26 @@ function npc.places.find_entrance_from_openable_nodes(all_openable_nodes, marker
|
|||||||
-- Check if there's any difference in vertical position
|
-- Check if there's any difference in vertical position
|
||||||
-- minetest.log("Openable node pos: "..minetest.pos_to_string(open_pos))
|
-- minetest.log("Openable node pos: "..minetest.pos_to_string(open_pos))
|
||||||
-- minetest.log("Plotmarker node pos: "..minetest.pos_to_string(marker_pos))
|
-- minetest.log("Plotmarker node pos: "..minetest.pos_to_string(marker_pos))
|
||||||
if start_pos.y ~= end_pos.y then
|
-- NOTE: Commented out while testing MarkBu's pathfinder
|
||||||
|
--if start_pos.y ~= end_pos.y then
|
||||||
-- Adjust to make pathfinder find nodes one node above
|
-- Adjust to make pathfinder find nodes one node above
|
||||||
end_pos.y = start_pos.y
|
-- end_pos.y = start_pos.y
|
||||||
end
|
--end
|
||||||
|
|
||||||
-- This adjustment allows the map to be created correctly
|
-- This adjustment allows the map to be created correctly
|
||||||
start_pos.y = start_pos.y + 1
|
--start_pos.y = start_pos.y + 1
|
||||||
end_pos.y = end_pos.y + 1
|
--end_pos.y = end_pos.y + 1
|
||||||
|
|
||||||
-- Find path from the openable node to the plotmarker
|
-- Find path from the openable node to the plotmarker
|
||||||
local path = pathfinder.find_path(start_pos, end_pos, 20, {})
|
--local path = pathfinder.find_path(start_pos, end_pos, 20, {})
|
||||||
|
local entity = {}
|
||||||
|
entity.collisionbox = {-0.20,-1.0,-0.20, 0.20,0.8,0.20}
|
||||||
|
minetest.log("Start pos: "..minetest.pos_to_string(start_pos))
|
||||||
|
minetest.log("End pos: "..minetest.pos_to_string(end_pos))
|
||||||
|
local path = pathfinder.find_path(start_pos, end_pos, entity)
|
||||||
|
--minetest.log("Found path: "..dump(path))
|
||||||
if path ~= nil then
|
if path ~= nil then
|
||||||
minetest.log("Path distance: "..dump(#path))
|
--minetest.log("Path distance: "..dump(#path))
|
||||||
-- Check if path length is less than the minimum found so far
|
-- Check if path length is less than the minimum found so far
|
||||||
if #path < min then
|
if #path < min then
|
||||||
-- Set min to path length and the result to the currently found node
|
-- Set min to path length and the result to the currently found node
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
default
|
default
|
||||||
mobs
|
mobs
|
||||||
intllib?
|
intllib?
|
||||||
|
pathfinder
|
||||||
|
@ -296,7 +296,7 @@ function npc.dialogue.process_dialogue(self, dialogue, player_name)
|
|||||||
|
|
||||||
-- Send dialogue line
|
-- Send dialogue line
|
||||||
if dialogue.text then
|
if dialogue.text then
|
||||||
minetest.chat_send_player(player_name, self.nametag..": "..dialogue.text)
|
minetest.chat_send_player(player_name, self.name..": "..dialogue.text)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if dialogue has responses. If it doesn't, unlock the actions
|
-- Check if dialogue has responses. If it doesn't, unlock the actions
|
||||||
|
54
npc.lua
54
npc.lua
@ -24,7 +24,11 @@ npc.direction = {
|
|||||||
north = 0,
|
north = 0,
|
||||||
east = 1,
|
east = 1,
|
||||||
south = 2,
|
south = 2,
|
||||||
west = 3
|
west = 3,
|
||||||
|
north_east = 4,
|
||||||
|
north_west = 5,
|
||||||
|
south_east = 6,
|
||||||
|
south_west = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
npc.action_state = {
|
npc.action_state = {
|
||||||
@ -41,7 +45,7 @@ function npc.get_entity_name(entity)
|
|||||||
if entity:is_player() then
|
if entity:is_player() then
|
||||||
return entity:get_player_name()
|
return entity:get_player_name()
|
||||||
else
|
else
|
||||||
return entity:get_luaentity().nametag
|
return entity:get_luaentity().name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -149,11 +153,14 @@ function npc.initialize(entity, pos, is_lua_entity)
|
|||||||
ent.sex = npc.MALE
|
ent.sex = npc.MALE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Nametag is initialized to blank
|
||||||
|
ent.nametag = ""
|
||||||
|
|
||||||
-- Set name
|
-- Set name
|
||||||
ent.nametag = get_random_name(ent.sex)
|
ent.name = get_random_name(ent.sex)
|
||||||
|
|
||||||
-- Set ID
|
-- Set ID
|
||||||
ent.npc_id = tostring(math.random(1000, 9999))..":"..ent.nametag
|
ent.npc_id = tostring(math.random(1000, 9999))..":"..ent.name
|
||||||
|
|
||||||
-- Initialize all gift data
|
-- Initialize all gift data
|
||||||
ent.gift_data = {
|
ent.gift_data = {
|
||||||
@ -325,33 +332,6 @@ function npc.initialize(entity, pos, is_lua_entity)
|
|||||||
local offer2 = npc.trade.create_custom_sell_trade_offer("Do you want me to fix your mese sword?", "Fix mese sword", "Fix mese sword", "default:sword_mese", {"default:sword_mese", "default:copper_lump 10"})
|
local offer2 = npc.trade.create_custom_sell_trade_offer("Do you want me to fix your mese sword?", "Fix mese sword", "Fix mese sword", "default:sword_mese", {"default:sword_mese", "default:copper_lump 10"})
|
||||||
table.insert(ent.trader_data.custom_trades, offer2)
|
table.insert(ent.trader_data.custom_trades, offer2)
|
||||||
|
|
||||||
-- Add a simple schedule for testing
|
|
||||||
npc.create_schedule(ent, npc.schedule_types.generic, 0)
|
|
||||||
-- Add schedule entries
|
|
||||||
local morning_actions = {
|
|
||||||
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = {end_pos=nodes[1], walkable={}} } ,
|
|
||||||
[2] = {task = npc.actions.cmd.USE_SITTABLE, args = {pos=nodes[1], action=npc.actions.const.sittable.SIT} },
|
|
||||||
[3] = {action = npc.actions.cmd.FREEZE, args = {freeze = true}}
|
|
||||||
}
|
|
||||||
--npc.add_schedule_entry(ent, npc.schedule_types.generic, 0, 7, nil, morning_actions)
|
|
||||||
--local afternoon_actions = { [1] = {action = npc.actions.stand, args = {}} }
|
|
||||||
local afternoon_actions = {[1] = {task = npc.actions.cmd.USE_SITTABLE, args = {pos=nodes[1], action=npc.actions.const.sittable.GET_UP} } }
|
|
||||||
--npc.add_schedule_entry(ent, npc.schedule_types.generic, 0, 9, nil, afternoon_actions)
|
|
||||||
-- local night_actions = {action: npc.action, args: {}}
|
|
||||||
-- npc.add_schedule_entry(self, npc.schedule_type.generic, 0, 19, check, actions)
|
|
||||||
|
|
||||||
-- npc.add_action(ent, npc.action.stand, {self = ent})
|
|
||||||
-- npc.add_action(ent, npc.action.stand, {self = ent})
|
|
||||||
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
|
|
||||||
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
|
|
||||||
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
|
|
||||||
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
|
|
||||||
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
|
|
||||||
-- npc.add_action(ent, npc.action.walk_step, {self = ent, dir = npc.direction.east})
|
|
||||||
-- npc.add_action(ent, npc.action.sit, {self = ent})
|
|
||||||
-- npc.add_action(ent, npc.action.rotate, {self = ent, dir = npc.direction.south})
|
|
||||||
-- npc.add_action(ent, npc.action.lay, {self = ent})
|
|
||||||
|
|
||||||
-- Temporary initialization of places
|
-- Temporary initialization of places
|
||||||
-- local bed_nodes = npc.places.find_new_nearby(ent, npc.places.nodes.BEDS, 8)
|
-- local bed_nodes = npc.places.find_new_nearby(ent, npc.places.nodes.BEDS, 8)
|
||||||
-- minetest.log("Number of bed nodes: "..dump(#bed_nodes))
|
-- minetest.log("Number of bed nodes: "..dump(#bed_nodes))
|
||||||
@ -360,7 +340,7 @@ function npc.initialize(entity, pos, is_lua_entity)
|
|||||||
-- end
|
-- end
|
||||||
|
|
||||||
--minetest.log(dump(ent))
|
--minetest.log(dump(ent))
|
||||||
minetest.log("Successfully spawned NPC with name "..dump(ent.nametag))
|
minetest.log("[advanced_npc] INFO Successfully initialized NPC with name "..dump(ent.name))
|
||||||
-- Refreshes entity
|
-- Refreshes entity
|
||||||
ent.object:set_properties(ent)
|
ent.object:set_properties(ent)
|
||||||
end
|
end
|
||||||
@ -530,7 +510,7 @@ end
|
|||||||
function npc.execute_action(self)
|
function npc.execute_action(self)
|
||||||
-- Check if an action was interrupted
|
-- Check if an action was interrupted
|
||||||
if self.actions.current_action_state == npc.action_state.interrupted then
|
if self.actions.current_action_state == npc.action_state.interrupted then
|
||||||
minetest.log("[advanced_npc] DEBUG Re-inserting interrupted action for NPC: '"..dump(self.nametag).."': "..dump(self.actions.state_before_lock.interrupted_action))
|
minetest.log("[advanced_npc] DEBUG Re-inserting interrupted action for NPC: '"..dump(self.name).."': "..dump(self.actions.state_before_lock.interrupted_action))
|
||||||
-- Insert into queue the interrupted action
|
-- Insert into queue the interrupted action
|
||||||
table.insert(self.actions.queue, 1, self.actions.state_before_lock.interrupted_action)
|
table.insert(self.actions.queue, 1, self.actions.state_before_lock.interrupted_action)
|
||||||
-- Clear the action
|
-- Clear the action
|
||||||
@ -552,7 +532,7 @@ function npc.execute_action(self)
|
|||||||
-- If the entry is a task, then push all this new operations in
|
-- If the entry is a task, then push all this new operations in
|
||||||
-- stack fashion
|
-- stack fashion
|
||||||
if action_obj.is_task == true then
|
if action_obj.is_task == true then
|
||||||
minetest.log("[advanced_npc] DEBUG Executing task for NPC '"..dump(self.nametag).."': "..dump(action_obj))
|
minetest.log("[advanced_npc] DEBUG Executing task for NPC '"..dump(self.name).."': "..dump(action_obj))
|
||||||
-- Backup current queue
|
-- Backup current queue
|
||||||
local backup_queue = self.actions.queue
|
local backup_queue = self.actions.queue
|
||||||
-- Remove this "task" action from queue
|
-- Remove this "task" action from queue
|
||||||
@ -568,7 +548,7 @@ function npc.execute_action(self)
|
|||||||
table.insert(self.actions.queue, backup_queue[i])
|
table.insert(self.actions.queue, backup_queue[i])
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
minetest.log("[advanced_npc] DEBUG Executing action for NPC '"..dump(self.nametag).."': "..dump(action_obj))
|
minetest.log("[advanced_npc] DEBUG Executing action for NPC '"..dump(self.name).."': "..dump(action_obj))
|
||||||
-- Store the action that is being executed
|
-- Store the action that is being executed
|
||||||
self.actions.state_before_lock.interrupted_action = action_obj
|
self.actions.state_before_lock.interrupted_action = action_obj
|
||||||
-- Store current position
|
-- Store current position
|
||||||
@ -855,7 +835,7 @@ mobs:register_mob("advanced_npc:npc", {
|
|||||||
-- Show dialogue to confirm that player is giving item as gift
|
-- Show dialogue to confirm that player is giving item as gift
|
||||||
npc.dialogue.show_yes_no_dialogue(
|
npc.dialogue.show_yes_no_dialogue(
|
||||||
self,
|
self,
|
||||||
"Do you want to give "..item_name.." to "..self.nametag.."?",
|
"Do you want to give "..item_name.." to "..self.name.."?",
|
||||||
npc.dialogue.POSITIVE_GIFT_ANSWER_PREFIX..item_name,
|
npc.dialogue.POSITIVE_GIFT_ANSWER_PREFIX..item_name,
|
||||||
function()
|
function()
|
||||||
npc.relationships.receive_gift(self, clicker)
|
npc.relationships.receive_gift(self, clicker)
|
||||||
@ -937,7 +917,7 @@ mobs:register_mob("advanced_npc:npc", {
|
|||||||
-- Check if NPC is walking
|
-- Check if NPC is walking
|
||||||
if self.actions.walking.is_walking == true then
|
if self.actions.walking.is_walking == true then
|
||||||
local pos = self.actions.walking.target_pos
|
local pos = self.actions.walking.target_pos
|
||||||
self.object:moveto({x=pos.x, y=pos.y + 1, z=pos.z})
|
self.object:moveto({x=pos.x, y=pos.y + 0.5, z=pos.z})
|
||||||
end
|
end
|
||||||
-- Execute action
|
-- Execute action
|
||||||
self.freeze = npc.execute_action(self)
|
self.freeze = npc.execute_action(self)
|
||||||
|
196
spawner.lua
196
spawner.lua
@ -63,6 +63,58 @@ npc.spawner.spawn_data = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local function get_basic_schedule()
|
||||||
|
return {
|
||||||
|
-- Create schedule entries
|
||||||
|
-- Morning actions: get out of bed, walk to outside of house
|
||||||
|
-- This will be executed around 8 AM MTG time
|
||||||
|
morning_actions = {
|
||||||
|
-- Get out of bed
|
||||||
|
[1] = {task = npc.actions.cmd.USE_BED, args = {
|
||||||
|
pos = npc.places.PLACE_TYPE.BED.PRIMARY,
|
||||||
|
action = npc.actions.const.beds.GET_UP
|
||||||
|
}
|
||||||
|
},
|
||||||
|
-- Walk outside
|
||||||
|
[2] = {task = npc.actions.cmd.WALK_TO_POS, args = {
|
||||||
|
end_pos = npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE,
|
||||||
|
walkable = {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
-- Allow mobs_redo wandering
|
||||||
|
[3] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
|
||||||
|
},
|
||||||
|
-- Afternoon actions: go inside the house
|
||||||
|
-- This will be executed around 6 PM MTG time
|
||||||
|
afternoon_actions = {
|
||||||
|
-- Get inside home
|
||||||
|
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = {
|
||||||
|
end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,
|
||||||
|
walkable = {}}
|
||||||
|
},
|
||||||
|
-- Allow mobs_redo wandering
|
||||||
|
[2] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
|
||||||
|
},
|
||||||
|
-- Evening actions: walk to bed and use it.
|
||||||
|
-- This will be executed around 10 PM MTG time
|
||||||
|
evening_actions = {
|
||||||
|
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = {
|
||||||
|
end_pos = {place_type=npc.places.PLACE_TYPE.BED.PRIMARY, use_access_node=true},
|
||||||
|
walkable = {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
-- Use bed
|
||||||
|
[2] = {task = npc.actions.cmd.USE_BED, args = {
|
||||||
|
pos = npc.places.PLACE_TYPE.BED.PRIMARY,
|
||||||
|
action = npc.actions.const.beds.LAY
|
||||||
|
}
|
||||||
|
},
|
||||||
|
-- Stay put on bed
|
||||||
|
[3] = {action = npc.actions.cmd.FREEZE, args = {freeze = true}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------
|
||||||
-- Scanning functions
|
-- Scanning functions
|
||||||
---------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------
|
||||||
@ -70,7 +122,7 @@ npc.spawner.spawn_data = {
|
|||||||
function spawner.filter_first_floor_nodes(nodes, ground_pos)
|
function spawner.filter_first_floor_nodes(nodes, ground_pos)
|
||||||
local result = {}
|
local result = {}
|
||||||
for _,node in pairs(nodes) do
|
for _,node in pairs(nodes) do
|
||||||
if node.node_pos.y <= ground_pos.y + 3 then
|
if node.node_pos.y <= ground_pos.y + 2 then
|
||||||
table.insert(result, node)
|
table.insert(result, node)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -130,40 +182,80 @@ end
|
|||||||
-- - Let the NPC know all doors to the house. Identify the front one as the entrance
|
-- - Let the NPC know all doors to the house. Identify the front one as the entrance
|
||||||
function spawner.assign_places(self, pos)
|
function spawner.assign_places(self, pos)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local doors = minetest.deserialize(meta:get_string("node_data")).openable_type
|
local entrance = minetest.deserialize(meta:get_string("entrance"))
|
||||||
minetest.log("Found "..dump(#doors).." openable nodes")
|
local node_data = minetest.deserialize(meta:get_string("node_data"))
|
||||||
|
|
||||||
local entrance = npc.places.find_entrance_from_openable_nodes(doors, pos)
|
-- Assign plotmarker
|
||||||
if entrance then
|
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_PLOTMARKER,
|
||||||
minetest.log("Found building entrance at: "..minetest.pos_to_string(entrance.node_pos))
|
npc.places.PLACE_TYPE.OTHER.HOME_PLOTMARKER, pos)
|
||||||
else
|
|
||||||
minetest.log("Unable to find building entrance!")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Assign entrance door and related locations
|
-- Assign entrance door and related locations
|
||||||
if entrance ~= nil and entrance.node_pos ~= nil then
|
if entrance ~= nil and entrance.node_pos ~= nil then
|
||||||
-- For debug purposes:
|
|
||||||
--local meta = minetest.get_meta(entrance.node_pos)
|
|
||||||
--meta:set_string("infotext", "Entrance for '"..dump(self.nametag).."': "..dump(entrance.node_pos))
|
|
||||||
--minetest.log("Self: "..dump(self))
|
|
||||||
--minetest.log("Places map: "..dump(self.places_map))
|
|
||||||
npc.places.add_public(self, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, entrance.node_pos)
|
npc.places.add_public(self, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, entrance.node_pos)
|
||||||
-- Find the position inside and outside the door
|
-- Find the position inside and outside the door
|
||||||
local entrance_inside = npc.places.find_node_behind_door(entrance.node_pos)
|
local entrance_inside = npc.places.find_node_behind_door(entrance.node_pos)
|
||||||
local entrance_outside = npc.places.find_node_in_front_of_door(entrance.node_pos)
|
local entrance_outside = npc.places.find_node_in_front_of_door(entrance.node_pos)
|
||||||
--minetest.set_node(entrance_inside, {name="default:apple"})
|
|
||||||
-- Assign these places to NPC
|
-- Assign these places to NPC
|
||||||
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, entrance_inside)
|
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, entrance_inside)
|
||||||
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, entrance_outside)
|
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, entrance_outside)
|
||||||
-- Make NPC go into their house
|
|
||||||
--minetest.log("Place: "..dump(npc.places.get_by_type(self, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE)))
|
|
||||||
npc.add_task(self, npc.actions.cmd.WALK_TO_POS, {end_pos=npc.places.get_by_type(self, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE)[1].pos, walkable={}})
|
|
||||||
npc.add_action(self, npc.actions.cmd.FREEZE, {freeze = false})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local plot_info = minetest.deserialize(meta:get_string("plot_info"))
|
-- Assign beds
|
||||||
minetest.log("Plot info:"..dump(plot_info))
|
if #node_data.bed_type > 0 then
|
||||||
|
-- Find unowned bed
|
||||||
|
for i = 1, #node_data.bed_type do
|
||||||
|
-- Check if bed has owner
|
||||||
|
minetest.log("Node: "..dump(node_data.bed_type[i]))
|
||||||
|
if node_data.bed_type[i].owner == "" then
|
||||||
|
-- If bed has no owner, check if it is accessible
|
||||||
|
local empty_nodes = npc.places.find_node_orthogonally(
|
||||||
|
node_data.bed_type[i].node_pos, {"air"}, 0)
|
||||||
|
-- Check if bed is accessible
|
||||||
|
if #empty_nodes > 0 then
|
||||||
|
-- Set owner to this NPC
|
||||||
|
node_data.bed_type[i].owner = self.npc_id
|
||||||
|
-- Assign node to NPC
|
||||||
|
npc.places.add_owned(self, npc.places.PLACE_TYPE.BED.PRIMARY,
|
||||||
|
npc.places.PLACE_TYPE.BED.PRIMARY, node_data.bed_type[i].node_pos, empty_nodes[1].pos)
|
||||||
|
-- Store changes to node_data
|
||||||
|
meta:set_string("node_data", minetest.serialize(node_data))
|
||||||
|
minetest.log("Added bed at "..minetest.pos_to_string(node_data.bed_type[i].node_pos)
|
||||||
|
.." to NPC "..dump(self.name))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--local plot_info = minetest.deserialize(meta:get_string("plot_info"))
|
||||||
|
--minetest.log("Plot info:"..dump(plot_info))
|
||||||
|
minetest.log("places: "..dump(self.places_map))
|
||||||
|
|
||||||
|
-- Make NPC go into their house
|
||||||
|
npc.add_task(self,
|
||||||
|
npc.actions.cmd.WALK_TO_POS,
|
||||||
|
{end_pos=npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,
|
||||||
|
walkable={}})
|
||||||
|
npc.add_action(self, npc.actions.cmd.FREEZE, {freeze = false})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function spawner.assign_schedules(self, pos)
|
||||||
|
local basic_schedule = get_basic_schedule()
|
||||||
|
-- Add a simple schedule for testing
|
||||||
|
npc.create_schedule(self, npc.schedule_types.generic, 0)
|
||||||
|
-- Add schedule entry for morning actions
|
||||||
|
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 8, nil, basic_schedule.morning_actions)
|
||||||
|
|
||||||
|
-- Add schedule entry for afternoon actions
|
||||||
|
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 18, nil, basic_schedule.afternoon_actions)
|
||||||
|
|
||||||
|
-- Add schedule entry for evening actions
|
||||||
|
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 22, nil, basic_schedule.evening_actions)
|
||||||
|
|
||||||
|
minetest.log("Schedules: "..dump(self.schedules))
|
||||||
|
--local afternoon_actions = { [1] = {action = npc.actions.stand, args = {}} }
|
||||||
|
--local afternoon_actions = {[1] = {task = npc.actions.cmd.USE_SITTABLE, args = {pos=nodes[1], action=npc.actions.const.sittable.GET_UP} } }
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -187,6 +279,8 @@ function npc.spawner.spawn_npc(pos)
|
|||||||
npc.initialize(ent, pos)
|
npc.initialize(ent, pos)
|
||||||
-- Assign nodes
|
-- Assign nodes
|
||||||
spawner.assign_places(ent:get_luaentity(), pos)
|
spawner.assign_places(ent:get_luaentity(), pos)
|
||||||
|
-- Assign schedules
|
||||||
|
spawner.assign_schedules(ent:get_luaentity(), pos)
|
||||||
-- Increase NPC spawned count
|
-- Increase NPC spawned count
|
||||||
spawned_npc_count = spawned_npc_count + 1
|
spawned_npc_count = spawned_npc_count + 1
|
||||||
-- Store count into node
|
-- Store count into node
|
||||||
@ -196,8 +290,10 @@ function npc.spawner.spawn_npc(pos)
|
|||||||
-- TODO: Add more information here at some time...
|
-- TODO: Add more information here at some time...
|
||||||
local entry = {
|
local entry = {
|
||||||
status = npc.spawner.spawn_data.status.alive,
|
status = npc.spawner.spawn_data.status.alive,
|
||||||
name = ent:get_luaentity().nametag,
|
name = ent:get_luaentity().name,
|
||||||
id = ent:get_luaentity().npc_id,
|
id = ent:get_luaentity().npc_id,
|
||||||
|
sex = ent:get_luaentity().sex,
|
||||||
|
age = ent:get_luaentity().
|
||||||
born_day = minetest.get_day_count()
|
born_day = minetest.get_day_count()
|
||||||
}
|
}
|
||||||
table.insert(npc_table, entry)
|
table.insert(npc_table, entry)
|
||||||
@ -205,16 +301,17 @@ function npc.spawner.spawn_npc(pos)
|
|||||||
meta:set_string("npcs", minetest.serialize(npc_table))
|
meta:set_string("npcs", minetest.serialize(npc_table))
|
||||||
-- Temp
|
-- Temp
|
||||||
meta:set_string("infotext", meta:get_string("infotext")..", "..spawned_npc_count)
|
meta:set_string("infotext", meta:get_string("infotext")..", "..spawned_npc_count)
|
||||||
minetest.log("[advanced_npc] Spawning successful!")
|
minetest.log("[advanced_npc] INFO Spawning successful!")
|
||||||
-- Check if there are more NPCs to spawn
|
-- Check if there are more NPCs to spawn
|
||||||
if spawned_npc_count >= npc_count then
|
if spawned_npc_count >= npc_count then
|
||||||
-- Stop timer
|
-- Stop timer
|
||||||
minetest.log("[advanced_npc] No more NPCs to spawn at this location")
|
minetest.log("[advanced_npc] INFO No more NPCs to spawn at this location")
|
||||||
timer:stop()
|
timer:stop()
|
||||||
else
|
else
|
||||||
-- Start another timer to spawn more NPC
|
-- Start another timer to spawn more NPC
|
||||||
minetest.log("[advanced_npc] Spawning one more NPC in "..dump(npc.spawner.spawn_delay).."s")
|
local new_delay = math.random(npc.spawner.spawn_delay)
|
||||||
timer:start(npc.spawner.spawn_delay)
|
minetest.log("[advanced_npc] INFO Spawning one more NPC in "..dump(npc.spawner.spawn_delay).."s")
|
||||||
|
timer:start(new_delay)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
@ -242,7 +339,8 @@ function spawner.calculate_npc_spawning(pos)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- Check number of beds
|
-- Check number of beds
|
||||||
local beds_count = #node_data.bed_type
|
local beds_count = #node_data.bed_type--#spawner.filter_first_floor_nodes(node_data.bed_type, pos)
|
||||||
|
|
||||||
minetest.log("[advanced_npc] INFO: Found "..dump(beds_count).." beds in the building at "..minetest.pos_to_string(pos))
|
minetest.log("[advanced_npc] INFO: Found "..dump(beds_count).." beds in the building at "..minetest.pos_to_string(pos))
|
||||||
local npc_count = 0
|
local npc_count = 0
|
||||||
-- If number of beds is zero or beds/2 is less than one, spawn
|
-- If number of beds is zero or beds/2 is less than one, spawn
|
||||||
@ -251,17 +349,18 @@ function spawner.calculate_npc_spawning(pos)
|
|||||||
-- Spawn a single NPC
|
-- Spawn a single NPC
|
||||||
npc_count = 1
|
npc_count = 1
|
||||||
else
|
else
|
||||||
-- Spawn beds_count/2 NPCs
|
-- Spawn (beds_count/2) NPCs
|
||||||
npc_count = ((beds_count / 2) - ((beds_count / 2) % 1))
|
npc_count = ((beds_count / 2) - ((beds_count / 2) % 1))
|
||||||
end
|
end
|
||||||
minetest.log("Will spawn "..dump(npc_count).." NPCs at "..minetest.pos_to_string(pos))
|
minetest.log("[advanced_npc] INFO: Will spawn "..dump(npc_count).." NPCs at "..minetest.pos_to_string(pos))
|
||||||
-- Store amount of NPCs to spawn
|
-- Store amount of NPCs to spawn
|
||||||
meta:set_int("npc_count", npc_count)
|
meta:set_int("npc_count", npc_count)
|
||||||
-- Store amount of NPCs spawned
|
-- Store amount of NPCs spawned
|
||||||
meta:set_int("spawned_npc_count", 0)
|
meta:set_int("spawned_npc_count", 0)
|
||||||
-- Start timer
|
-- Start timer
|
||||||
local timer = minetest.get_node_timer(pos)
|
local timer = minetest.get_node_timer(pos)
|
||||||
timer:start(npc.spawner.spawn_delay)
|
local delay = math.random(npc.spawner.spawn_delay)
|
||||||
|
timer:start(delay)
|
||||||
end
|
end
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------
|
||||||
@ -273,9 +372,9 @@ end
|
|||||||
-- point and the building_data to get the x, y and z-coordinate size
|
-- point and the building_data to get the x, y and z-coordinate size
|
||||||
-- of the building schematic
|
-- of the building schematic
|
||||||
function spawner.scan_mg_villages_building(pos, building_data)
|
function spawner.scan_mg_villages_building(pos, building_data)
|
||||||
minetest.log("--------------------------------------------")
|
--minetest.log("--------------------------------------------")
|
||||||
minetest.log("Building data: "..dump(building_data))
|
--minetest.log("Building data: "..dump(building_data))
|
||||||
minetest.log("--------------------------------------------")
|
--minetest.log("--------------------------------------------")
|
||||||
-- Get area of the building
|
-- Get area of the building
|
||||||
local x_size = building_data.bsizex
|
local x_size = building_data.bsizex
|
||||||
local y_size = building_data.ysize
|
local y_size = building_data.ysize
|
||||||
@ -285,6 +384,7 @@ function spawner.scan_mg_villages_building(pos, building_data)
|
|||||||
local x_sign, z_sign = 1, 1
|
local x_sign, z_sign = 1, 1
|
||||||
|
|
||||||
-- Check plot direction
|
-- Check plot direction
|
||||||
|
-- NOTE: Below values may be wrong, very wrong!
|
||||||
-- 0 - facing West, -X
|
-- 0 - facing West, -X
|
||||||
-- 1 - facing North, +Z
|
-- 1 - facing North, +Z
|
||||||
-- 2 - facing East, +X
|
-- 2 - facing East, +X
|
||||||
@ -370,6 +470,17 @@ function spawner.replace_mg_villages_plotmarker(pos)
|
|||||||
meta:set_string("plot_info", minetest.serialize(plot_info))
|
meta:set_string("plot_info", minetest.serialize(plot_info))
|
||||||
-- Scan building for nodes
|
-- Scan building for nodes
|
||||||
local nodedata = spawner.scan_mg_villages_building(pos, plot_info)
|
local nodedata = spawner.scan_mg_villages_building(pos, plot_info)
|
||||||
|
-- Find building entrance
|
||||||
|
local doors = nodedata.openable_type
|
||||||
|
--minetest.log("Found "..dump(#doors).." openable nodes")
|
||||||
|
local entrance = npc.places.find_entrance_from_openable_nodes(doors, pos)
|
||||||
|
if entrance then
|
||||||
|
minetest.log("Found building entrance at: "..minetest.pos_to_string(entrance.node_pos))
|
||||||
|
else
|
||||||
|
minetest.log("Unable to find building entrance!")
|
||||||
|
end
|
||||||
|
-- Store building entrance
|
||||||
|
meta:set_string("entrance", minetest.serialize(entrance))
|
||||||
-- Store nodedata into the spawner's metadata
|
-- Store nodedata into the spawner's metadata
|
||||||
meta:set_string("node_data", minetest.serialize(nodedata))
|
meta:set_string("node_data", minetest.serialize(nodedata))
|
||||||
-- Initialize NPCs
|
-- Initialize NPCs
|
||||||
@ -399,13 +510,26 @@ if minetest.get_modpath("mg_villages") ~= nil then
|
|||||||
type = "fixed",
|
type = "fixed",
|
||||||
fixed = {
|
fixed = {
|
||||||
{-0.5+2/16, -0.5, -0.5+2/16, 0.5-2/16, -0.5+2/16, 0.5-2/16},
|
{-0.5+2/16, -0.5, -0.5+2/16, 0.5-2/16, -0.5+2/16, 0.5-2/16},
|
||||||
|
--{-0.5+0/16, -0.5, -0.5+0/16, 0.5-0/16, -0.5+0/16, 0.5-0/16},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
walkable = false,
|
||||||
groups = {cracky=3,stone=2},
|
groups = {cracky=3,stone=2},
|
||||||
|
|
||||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||||
-- Get all openable-type nodes for this building
|
|
||||||
-- NOTE: This is temporary code for testing...
|
-- NOTE: This is temporary code for testing...
|
||||||
|
local nodedata = minetest.deserialize(minetest.get_meta(pos):get_string("node_data"))
|
||||||
|
--minetest.log("Node data: "..dump(nodedata))
|
||||||
|
--minetest.log("Entrance: "..dump(minetest.deserialize(minetest.get_meta(pos):get_string("entrance"))))
|
||||||
|
--minetest.log("First-floor beds: "..dump(spawner.filter_first_floor_nodes(nodedata.bed_type, pos)))
|
||||||
|
--local entrance = npc.places.find_entrance_from_openable_nodes(nodedata.openable_type, pos)
|
||||||
|
--minetest.log("Found entrance: "..dump(entrance))
|
||||||
|
|
||||||
|
for i = 1, #nodedata.bed_type do
|
||||||
|
nodedata.bed_type[i].owner = ""
|
||||||
|
end
|
||||||
|
minetest.get_meta(pos):set_string("node_data", minetest.serialize(nodedata))
|
||||||
|
minetest.log("Cleared bed owners")
|
||||||
|
|
||||||
return mg_villages.plotmarker_formspec( pos, nil, {}, clicker )
|
return mg_villages.plotmarker_formspec( pos, nil, {}, clicker )
|
||||||
end,
|
end,
|
||||||
@ -450,8 +574,8 @@ if minetest.get_modpath("mg_villages") ~= nil then
|
|||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Replace mg_villages:plotmarker with Advanced NPC auto spawners",
|
label = "Replace mg_villages:plotmarker with Advanced NPC auto spawners",
|
||||||
nodenames = {"mg_villages:plotmarker"},
|
nodenames = {"mg_villages:plotmarker"},
|
||||||
interval = npc.spawner.replacement_interval,
|
interval = 10,--npc.spawner.replacement_interval,
|
||||||
chance = 5,
|
chance = 1, --5,
|
||||||
catch_up = true,
|
catch_up = true,
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||||
-- Check if replacement is activated
|
-- Check if replacement is activated
|
||||||
|
@ -112,7 +112,7 @@ function npc.trade.show_trade_offer_formspec(self, player, offer_type)
|
|||||||
default.gui_bg..
|
default.gui_bg..
|
||||||
default.gui_bg_img..
|
default.gui_bg_img..
|
||||||
default.gui_slots..
|
default.gui_slots..
|
||||||
"label[2,0.1;"..self.nametag..prompt_string.."]"..
|
"label[2,0.1;"..self.name..prompt_string.."]"..
|
||||||
"item_image_button[2,1.3;1.2,1.2;"..trade_offer.item..";item;]"..
|
"item_image_button[2,1.3;1.2,1.2;"..trade_offer.item..";item;]"..
|
||||||
"label[3.75,1.75;"..for_string.."]"..
|
"label[3.75,1.75;"..for_string.."]"..
|
||||||
"item_image_button[4.8,1.3;1.2,1.2;"..trade_offer.price[1]..";price;]"..
|
"item_image_button[4.8,1.3;1.2,1.2;"..trade_offer.price[1]..";price;]"..
|
||||||
@ -221,7 +221,7 @@ function npc.trade.show_custom_trade_offer(self, player, offer)
|
|||||||
default.gui_bg..
|
default.gui_bg..
|
||||||
default.gui_bg_img..
|
default.gui_bg_img..
|
||||||
default.gui_slots..
|
default.gui_slots..
|
||||||
"label[2,0.1;"..self.nametag..": "..offer.dialogue_prompt.."]"..
|
"label[2,0.1;"..self.name..": "..offer.dialogue_prompt.."]"..
|
||||||
price_grid..
|
price_grid..
|
||||||
"label["..(margin_x + 3.75)..",1.75;"..for_string.."]"..
|
"label["..(margin_x + 3.75)..",1.75;"..for_string.."]"..
|
||||||
"item_image_button["..(margin_x + 4.8)..",1.3;1.2,1.2;"..offer.item..";item;]"..
|
"item_image_button["..(margin_x + 4.8)..",1.3;1.2,1.2;"..offer.item..";item;]"..
|
||||||
|
Loading…
Reference in New Issue
Block a user