Occupations: Add schedule check function, allow enqueuing of schedule check

This commit is contained in:
Hector Franqui 2017-08-11 12:03:45 -04:00
parent 0f931d273c
commit 5a93800e77
6 changed files with 1116 additions and 772 deletions

View File

@ -13,6 +13,8 @@
npc.actions = {}
npc.actions.default_interval = 1
-- Describes actions with doors or openable nodes
npc.actions.const = {
doors = {
@ -50,7 +52,9 @@ npc.actions.cmd = {
USE_FURNACE = 11,
USE_BED = 12,
USE_SITTABLE = 13,
WALK_TO_POS = 14
WALK_TO_POS = 14,
DIG = 15,
PLACE = 16
}
--npc.actions.one_nps_speed = 0.98
@ -60,6 +64,10 @@ npc.actions.one_nps_speed = 1
npc.actions.one_half_nps_speed = 1.5
npc.actions.two_nps_speed = 2
npc.actions.take_from_inventory = "take_from_inventory"
npc.actions.take_from_inventory_forced = "take_from_inventory_forced"
npc.actions.force_place = "force_place"
-- Executor --
--------------
-- Function references aren't reliable in Minetest entities. Objects get serialized
@ -115,6 +123,12 @@ function npc.actions.execute(self, command, args)
-- Call walk to position task
--minetest.log("Self: "..dump(self)..", Command: "..dump(command)..", args: "..dump(args))
return npc.actions.walk_to_pos(self, args)
elseif command == npc.actions.cmd.DIG then
-- Call dig node action
return npc.actions.dig(self, args)
elseif command == npc.actions.cmd.PLACE then
-- Call place node action
return npc.actions.place(self, args)
end
end
@ -146,6 +160,85 @@ function npc.actions.freeze(self, args)
return not(freeze_mobs_api)
end
-- This action digs the node at the given position
-- If 'add_to_inventory' is true, it will put the digged node in the NPC
-- inventory.
-- Returns true if dig is successful, otherwise false
function npc.actions.dig(self, args)
local pos = args.pos
local add_to_inventory = args.add_to_inventory
local bypass_protection = args.bypass_protection
local node = minetest.get_node_or_nil(pos)
if node then
-- Check if protection not enforced
if not force_dig then
-- Try to dig node
if minetest.dig_node(pos) then
-- Add to inventory the node drops
if add_to_inventory then
-- Get node drop
local drop = minetest.registered_nodes[node.name].drop
-- Add to NPC inventory
npc.npc.add_item_to_inventory(self, drop, 1)
end
return true
end
else
-- Add to inventory
if add_to_inventory then
-- Get node drop
local drop = minetest.registered_nodes[node.name].drop
-- Add to NPC inventory
npc.npc.add_item_to_inventory(self, drop, 1)
end
-- Dig node
minetest.set_node(pos, {name="air"})
end
end
return false
end
-- This action places a given node at the given position
-- There are three ways to source the node:
-- 1. take_from_inventory: takes node from inventory. If not in inventory,
-- node isn't placed.
-- 2. take_from_inventory_forced: takes node from inventory. If not in
-- inventory, node will be placed anyways.
-- 3. force_place: places node regardless of inventory - will not touch
-- the NPCs inventory
function npc.actions.place(self, args)
local pos = args.pos
local node = args.node
local source = args.source
local bypass_protection = args.bypass_protection
local node_at_pos = minetest.get_node_or_nil(pos)
-- Check if position is empty or has a node that can be built to
if node_at_pos and
(node_at_pos.name == "air" or minetest.registered_nodes(node_at_pos.name).buildable_to == true) then
-- Check protection
if (not bypass_protection and not minetest.is_protected(pos, self.npc_name))
or bypass_protection == true then
-- Take from inventory if necessary
local place_item = false
if source == npc.actions.take_from_inventory then
if npc.take_item_from_inventory(self, node.name, 1) then
place_item = true
end
elseif source == npc.actions.take_from_inventory_forced then
npc.take_item_from_inventory(self, node.name, 1)
place_item = true
elseif source == npc.actions.force_place then
place_item = true
end
-- Place node
if place_item then
minetest.set_node(pos, node)
end
end
end
end
-- This action is to rotate to mob to a specifc direction. Currently, the code
-- contains also for diagonals, but remaining in the orthogonal domain is preferrable.
function npc.actions.rotate(self, args)

View File

@ -72,6 +72,9 @@ npc.places.PLACE_TYPE = {
OPENABLE = {
HOME_ENTRANCE_DOOR = "home_entrance_door"
},
SCHEDULE = {
TARGET = "schedule_target_pos"
},
OTHER = {
HOME_PLOTMARKER = "home_plotmarker",
HOME_INSIDE = "home_inside",
@ -87,7 +90,7 @@ function npc.places.add_owned(self, place_name, place_type, pos, access_node)
self.places_map[place_name] = {type=place_type, pos=pos, access_node=access_node or pos, status="owned"}
end
function npc.places.add_unowned_accessible_place(self, nodes, place_type)
function npc.places.add_owned_accessible_place(self, nodes, place_type)
for i = 1, #nodes do
-- Check if node has owner
if nodes[i].owner == "" then
@ -109,14 +112,18 @@ function npc.places.add_unowned_accessible_place(self, nodes, place_type)
end
end
function npc.places.add_shared_accessible_place(self, nodes, place_type)
-- Override flag allows to overwrite a place in the places_map.
-- The only valid use right now is for schedules - don't use this
-- anywhere else unless you have a position that changes over time.
function npc.places.add_shared_accessible_place(self, nodes, place_type, override)
if not override then
for i = 1, #nodes do
-- Check of not adding same owned sit
-- Check if not adding same owned place
if nodes[i].owner ~= self.npc_id then
-- Check if it is accessible
local empty_nodes = npc.places.find_node_orthogonally(
nodes[i].node_pos, {"air"}, 0)
-- Check if bed is accessible
-- Check if node is accessible
if #empty_nodes > 0 then
-- Assign node to NPC
npc.places.add_shared(self, place_type..dump(i),
@ -124,6 +131,18 @@ function npc.places.add_shared_accessible_place(self, nodes, place_type)
end
end
end
elseif override then
-- Note: Nodes is only *one* node in case override = true
-- Check if it is accessible
local empty_nodes = npc.places.find_node_orthogonally(
nodes.node_pos, {"air"}, 0)
-- Check if node is accessible
if #empty_nodes > 0 then
-- Nodes is only one node
npc.places.add_shared(self, place_type, place_type,
nodes.node_pos, empty_nodes[1].pos)
end
end
end
function npc.places.get_by_type(self, place_type)
@ -396,8 +415,8 @@ function npc.places.find_node_in_front_of_door(door_pos)
elseif door.param2 == 2 then
-- Looking north
return {x=door_pos.x, y=door_pos.y, z=door_pos.z + 1}
-- Looking west
elseif door.param2 == 3 then
-- Looking west
return {x=door_pos.x + 1, y=door_pos.y, z=door_pos.z}
end
end

View File

@ -34,10 +34,12 @@ dofile(path .. "/actions/actions.lua")
dofile(path .. "/actions/places.lua")
dofile(path .. "/actions/pathfinder.lua")
dofile(path .. "/actions/node_registry.lua")
dofile(path .. "/occupations/occupations.lua")
-- Load random data definitions
dofile(path .. "/random_data.lua")
dofile(path .. "/random_data/dialogues_data.lua")
dofile(path .. "/random_data/gift_items_data.lua")
dofile(path .. "/random_data/names_data.lua")
dofile(path .. "/random_data/occupations_data.lua")
print (S("[Mod] Advanced NPC loaded"))

235
npc.lua
View File

@ -24,6 +24,8 @@ npc.ANIMATION_LAY_START = 162
npc.ANIMATION_LAY_END = 166
npc.ANIMATION_WALK_START = 168
npc.ANIMATION_WALK_END = 187
npc.ANIMATION_MINE_START = 189
npc.ANIMATION_MINE_END =198
npc.direction = {
north = 0,
@ -175,6 +177,11 @@ function npc.get_random_texture_from_array(age, sex, textures)
end
end
-- Check if there are no textures
if #filtered_textures == 0 then
return nil
end
return filtered_textures[math.random(1, #filtered_textures)]
end
@ -226,7 +233,7 @@ end
-- Spawn function. Initializes all variables that the
-- NPC will have and choose random, starting values
function npc.initialize(entity, pos, is_lua_entity, npc_stats)
function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
npc.log("INFO", "Initializing NPC at "..minetest.pos_to_string(pos))
-- Get variables
@ -249,12 +256,13 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
-- - Age: For each two adults, the chance of spawning a child next will be 50%
-- If there's a child for two adults, the chance of spawning a child goes to
-- 40% and keeps decreasing unless two adults have no child.
-- Use NPC stats if provided
if npc_stats then
-- Default chances
local male_s, male_e = 0, 50
local female_s, female_e = 51, 100
local adult_s, adult_e = 0, 90
local child_s, child_e = 91, 100
local adult_s, adult_e = 0, 85
local child_s, child_e = 86, 100
-- Determine sex probabilities
if npc_stats[npc.FEMALE].total > npc_stats[npc.MALE].total then
male_e = 75
@ -293,8 +301,8 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
elseif child_s <= age_chance and age_chance <= child_e then
selected_age = npc.age.child
ent.visual_size = {
x = 0.60,
y = 0.60
x = 0.65,
y = 0.65
}
ent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10}
ent.is_child = true
@ -321,6 +329,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
else
ent.sex = npc.MALE
end
ent.age = npc.age.adult
end
-- Nametag is initialized to blank
@ -406,7 +415,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
action_timer = 0,
-- Determines the interval for each action in the action queue
-- Default is 1. This can be changed via actions
action_interval = 1,
action_interval = npc.actions.default_interval,
-- Avoid the execution of the action timer
action_timer_lock = false,
-- Defines the state of the current action
@ -451,15 +460,26 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
-- Used to calculate dependencies
temp_executed_queue = {},
-- An array of schedules, meant to be one per day at some point
-- when calendars are implemented. Allows for only 7 schedules,
--- when calendars are implemented. Allows for only 7 schedules,
-- one for each day of the week
generic = {},
-- An array of schedules, meant to be for specific dates in the
-- year. Can contain as many as possible. The keys will be strings
-- in the format MM:DD
date_based = {}
date_based = {},
-- The following holds the check parameters provided by the
-- current schedule
current_check_params = {}
}
-- If occupation name given, override properties with
-- occupation values and initialize schedules
minetest.log("Entity age: "..dump(ent.age)..", afult? "..dump(ent.age==npc.age.adult))
if occupation_name and occupation_name ~= "" and ent.age == npc.age.adult then
npc.occupations.initialize_occupation_values(ent, occupation_name)
end
-- TODO: Remove this - do inside occupation
-- Dedicated trade test
ent.trader_data.trade_list.both = {
["default:tree"] = {},
@ -477,16 +497,18 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
["default:axe_stone"] = {}
}
-- Generate trade offers
npc.trade.generate_trade_offers_by_status(ent)
-- Add a custom trade offer
local offer1 = npc.trade.create_custom_sell_trade_offer("Do you want me to fix your steel sword?", "Fix steel sword", "Fix steel sword", "default:sword_steel", {"default:sword_steel", "default:iron_lump 5"})
table.insert(ent.trader_data.custom_trades, offer1)
-- local offer1 = npc.trade.create_custom_sell_trade_offer("Do you want me to fix your steel sword?", "Fix steel sword", "Fix steel sword", "default:sword_steel", {"default:sword_steel", "default:iron_lump 5"})
-- table.insert(ent.trader_data.custom_trades, offer1)
--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)
-- Set initialized flag on
ent.initialized = true
--minetest.log(dump(ent))
npc.log("WARNING", "Spawned entity: "..dump(ent))
npc.log("INFO", "Successfully initialized NPC with name "..dump(ent.npc_name)
..", sex: "..ent.sex..", is child: "..dump(ent.is_child)
..", texture: "..dump(ent.textures))
@ -693,9 +715,19 @@ function npc.execute_action(self)
return self.freeze
end
local action_obj = self.actions.queue[1]
-- Check if action is null
if action_obj.action == nil then
return
end
-- Check if action is an schedule check
if action_obj.action == "schedule_check" then
-- Execute schedule check
npc.schedule_check(self)
-- Remove table entry
table.remove(self.actions.queue, 1)
-- Return
return false
end
-- If the entry is a task, then push all this new operations in
-- stack fashion
if action_obj.is_task == true then
@ -722,7 +754,6 @@ function npc.execute_action(self)
self.actions.state_before_lock.pos = self.object:getpos()
-- Execute action as normal
result = npc.actions.execute(self, action_obj.action, action_obj.args)
--result = action_obj.action(self, action_obj.args)
-- Remove task
table.remove(self.actions.queue, 1)
-- Set state
@ -961,6 +992,141 @@ function npc.schedule_change_property(self, property, args)
end
end
function npc.add_schedule_check(self)
table.insert(self.actions.queue, {action="schedule_check", args={}, is_task=false})
end
-- Range: integer, radius in which nodes will be searched. Recommended radius is
-- between 1-3
-- Nodes: array of node names
-- Actions: map of node names to entries {action=<action_enum>, args={}}.
-- Arguments can be empty - the check function will try to determine most
-- arguments anyways (like pos and dir).
-- Special node "any" will execute those actions on any node except the
-- already specified ones.
-- None-action: array of entries {action=<action_enum>, args={}}.
-- Will be executed when no node is found.
function npc.schedule_check(self)
local range = self.schedules.current_check_params.range
local nodes = self.schedules.current_check_params.nodes
local actions = self.schedules.current_check_params.actions
local none_actions = self.schedules.current_check_params.none_actions
-- Get NPC position
local start_pos = self.object:getpos()
-- Search nodes
local found_nodes = npc.places.find_node_nearby(start_pos, nodes, range)
-- Check if any node was found
if found_nodes then
-- Pick a random node to act upon
local node_pos = found_nodes[math.random(1, #found_nodes)]
local node = minetest.get_node(node_pos)
-- Set node as a place
-- Note: Code below isn't *adding* a node, but overwriting the
-- place with "schedule_target_pos" place type
npc.places.add_shared_accessible_place(
self, node, npc.places.PLACE_TYPE.SCHEDULE.TARGET, true)
-- Get actions related to node and enqueue them
for i = 1, #actions[node.name] do
local args = {}
local action = nil
-- Calculate arguments for the following supported actions:
-- - Dig
-- - Place
-- - Walk step
-- - Walk to position
-- - Use furnace
if actions[node.name][i].action == npc.actions.cmd.DIG then
-- Defaults: items will be added to inventory if not specified
-- otherwise, and protection will be respected, if not specified
-- otherwise
args = {
pos = node_pos,
add_to_inventory = action[node.name][i].args.add_to_inventory or true,
bypass_protection = action[node.name][i].args.bypass_protection or false
}
elseif actions[node.name][i].action == npc.actions.cmd.PLACE then
-- Position: providing node_pos is because the currently planned
-- behavior for placing nodes is replacing digged nodes. A NPC farmer,
-- for instance, might dig a plant node and plant another one on the
-- same position.
-- Defaults: items will be taken from inventory if existing,
-- if not will be force-placed (item comes from thin air)
-- Protection will be respected
args = {
pos = action[node.name][i].args.pos or node_pos,
source = action[node.name][i].args.source or npc.actions.take_from_inventory_forced,
node = action[node.name][i].args.node,
bypass_protection = action[node.name][i].args.bypass_protection or false
}
elseif actions[node.name][i].action == npc.actions.cmd.WALK_STEP then
-- Defaults: direction is calculated from start node to node_pos.
-- Speed is default wandering speed. Target pos is node_pos
-- Calculate dir if dir is random
local dir = npc.actions.get_direction(start_pos, node_pos)
if actions[node.name][i].args.dir == "random" then
dir = math.random(0,7)
elseif type(actions[node.name][i].args.dir) == "number" then
dir = actions[node.name][i].args.dir
end
args = {
dir = dir,
speed = actions[node.name][i].args.speed or npc.actions.one_nps_speed,
target_pos = actions[node.name][i].args.target_pos or node_pos
}
elseif actions[node.name][i].action == npc.actions.cmd.WALK_TO_POS then
-- Optimize walking -- since distances can be really short,
-- a simple walk_step() action can do most of the times. For
-- this, however, we need to calculate direction
-- First of all, check distance
if vector.distance(start_pos, node_pos) < 3 then
-- Will do walk_step based instead
action = npc.actions.cmd.WALK_STEP
args = {
dir = npc.actions.get_direction(start_pos, node_pos),
speed = npc.actions.one_nps_speed,
target_pos = node_pos
}
else
-- Set end pos to be node_pos
args = {
end_pos = actions[node.name][i].args.end_pos or node_pos,
walkable = actions[node.name][i].args.walkable or {}
}
end
elseif actions[node.name][i].action == npc.actions.cmd.USE_FURNACE then
-- Defaults: pos is node_pos. Freeze is true
args = {
pos = actions[node.name][i].args.pos or node_pos,
item = actions[node.name][i].args.item,
freeze = actions[node.name][i].args.freeze or true
}
end
-- Enqueue actions
npc.add_action(self, action or actions[node.name][i].action, args or actions[node.name][i].args)
end
-- Enqueue next schedule check
if self.schedules.current_check_params.execution_count
< self.schedules.current_check_params.execution_times then
npc.add_schedule_check()
end
-- Nodes found
return true
else
-- No nodes found, enqueue none_actions
for i = 1, #none_actions do
-- Enqueue actions
npc.add_action(self, none_actions[i].action, none_actions[i].args)
end
-- Enqueue next schedule check
if self.schedules.current_check_params.execution_count
< self.schedules.current_check_params.execution_times then
npc.add_schedule_check()
end
-- No nodes found
return false
end
end
---------------------------------------------------------------------------------------
-- NPC Definition
---------------------------------------------------------------------------------------
@ -977,7 +1143,8 @@ mobs:register_mob("advanced_npc:npc", {
hp_min = 10,
hp_max = 20,
armor = 100,
collisionbox = {-0.20,-1.0,-0.20, 0.20,0.8,0.20},
collisionbox = {-0.20,0,-0.20, 0.20,1.8,0.20},
--collisionbox = {-0.20,-1.0,-0.20, 0.20,0.8,0.20},
--collisionbox = {-0.35,-1.0,-0.35, 0.35,0.8,0.35},
visual = "mesh",
mesh = "character.b3d",
@ -1043,7 +1210,7 @@ mobs:register_mob("advanced_npc:npc", {
-- Receive gift or start chat. If player has no item in hand
-- then it is going to start chat directly
minetest.log("self.can_have_relationship: "..dump(self.can_have_relationship)..", self.can_receive_gifts: "..dump(self.can_receive_gifts)..", table: "..dump(item:to_table()))
--minetest.log("self.can_have_relationship: "..dump(self.can_have_relationship)..", self.can_receive_gifts: "..dump(self.can_receive_gifts)..", table: "..dump(item:to_table()))
if self.can_have_relationship
and self.can_receive_gifts
and item:to_table() ~= nil then
@ -1155,8 +1322,9 @@ mobs:register_mob("advanced_npc:npc", {
self.actions.action_timer = 0
-- Check if NPC is walking
if self.actions.walking.is_walking == true then
-- Move NPC to expected position to ensure not getting lost
local pos = self.actions.walking.target_pos
self.object:moveto({x=pos.x, y=pos.y + 0.5, z=pos.z})
self.object:moveto({x=pos.x, y=pos.y-0.5, z=pos.z})
end
-- Execute action
self.freeze = npc.execute_action(self)
@ -1186,16 +1354,33 @@ mobs:register_mob("advanced_npc:npc", {
-- Check if schedule for this time exists
--minetest.log("Found default schedule")
if schedule[time] ~= nil then
-- Check if schedule has a check function
if schedule[time].check ~= nil then
-- Execute check function and then add corresponding action
-- to action queue. This is for jobs.
-- TODO: Need to implement
else
npc.log("WARNING", "Found schedule for time "..dump(time)..": "..dump(schedule[time]))
npc.log("DEBUG", "Adding actions to action queue")
-- Add to action queue all actions on schedule
for i = 1, #schedule[time] do
--minetest.log("schedule[time]: "..dump(schedule[time]))
-- Check if schedule has a check function
if not schedule[time][i].check then
-- Add parameters for check function and run for first time
npc.log("INFO", "NPC "..dump(self.npc_name).." is starting check on "..minetest.pos_to_string(self.object:getpos()))
local check_params = schedule[time][i]
-- Calculates how many times check will be executed
local execution_times = check_params.count
if check_params.random_execution_times then
execution_times = math.random(check_params.min_count, check_params.max_count)
end
-- Set current parameters
self.schedules.current_check_params = {
range = check_params.range,
nodes = check_params.nodes,
actions = check_params.actions,
none_actions = check_params.none_actions,
execution_count = 0,
execution_times = execution_times
}
-- Execute check for the first time
npc.schedule_check(self)
else
-- Run usual schedule entry
-- Check chance
local execution_chance = math.random(1, 100)
if not schedule[time][i].chance or
@ -1227,11 +1412,15 @@ mobs:register_mob("advanced_npc:npc", {
table.insert(self.schedules.temp_executed_queue, i)
end
end
else
-- TODO: Change to debug
npc.log("WARNING", "Skipping schedule entry for time "..dump(time)..": "..dump(schedule[time][i]))
end
end
end
-- Clear execution queue
self.schedules.temp_executed_queue = {}
npc.log("DEBUG", "New action queue: "..dump(self.actions))
npc.log("WARNING", "New action queue: "..dump(self.actions))
end
end
end

View File

@ -264,7 +264,7 @@ function spawner.assign_places(self, pos)
-- Assign beds
if #node_data.bed_type > 0 then
-- Assign a specific sittable node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.bed_type,
npc.places.add_owned_accessible_place(self, node_data.bed_type,
npc.places.PLACE_TYPE.BED.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
@ -275,7 +275,7 @@ function spawner.assign_places(self, pos)
-- Check if there are same or more amount of sits as beds
if #node_data.sittable_type >= #node_data.bed_type then
-- Assign a specific sittable node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.sittable_type,
npc.places.add_owned_accessible_place(self, node_data.sittable_type,
npc.places.PLACE_TYPE.SITTABLE.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
@ -291,7 +291,7 @@ function spawner.assign_places(self, pos)
-- Check if there are same or more amount of furnace as beds
if #node_data.furnace_type >= #node_data.bed_type then
-- Assign a specific furnace node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.furnace_type,
npc.places.add_owned_accessible_place(self, node_data.furnace_type,
npc.places.PLACE_TYPE.FURNACE.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
@ -307,7 +307,7 @@ function spawner.assign_places(self, pos)
-- Check if there are same or more amount of storage as beds
if #node_data.storage_type >= #node_data.bed_type then
-- Assign a specific storage node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.storage_type,
npc.places.add_owned_accessible_place(self, node_data.storage_type,
npc.places.PLACE_TYPE.STORAGE.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
@ -372,17 +372,19 @@ function npc.spawner.spawn_npc(pos)
local ent = minetest.add_entity({x=pos.x, y=pos.y+1, z=pos.z}, "advanced_npc:npc")
if ent and ent:get_luaentity() then
ent:get_luaentity().initialized = false
-- Determine NPC occupation
local occupation_name = "default_basic"
-- Initialize NPC
-- Call with stats if there are NPCs
if #npc_table > 0 then
npc.initialize(ent, pos, false, npc_stats)
if npc_table and #npc_table > 0 then
npc.initialize(ent, pos, false, npc_stats, occupation_name)
else
npc.initialize(ent, pos)
npc.initialize(ent, pos, nil, nil, occupation_name)
end
-- Assign nodes
spawner.assign_places(ent:get_luaentity(), pos)
-- Assign schedules
spawner.assign_schedules(ent:get_luaentity(), pos)
--spawner.assign_schedules(ent:get_luaentity(), pos)
-- Increase NPC spawned count
spawned_npc_count = spawned_npc_count + 1
-- Store count into node
@ -750,8 +752,8 @@ minetest.register_chatcommand("restore_plotmarkers", {
end
-- Search for nodes
local radius = tonumber(param)
local start_pos = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
local end_pos = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
local start_pos = {x=pos.x - radius, y=pos.y - 5, z=pos.z - radius}
local end_pos = {x=pos.x + radius, y=pos.y + 5, z=pos.z + radius}
local nodes = minetest.find_nodes_in_area_under_air(start_pos, end_pos,
{"mg_villages:plotmarker"})
-- Check if we have nodes to replace

View File

@ -1,4 +1,4 @@
-- Basic utilities to work with array operations in Lua
-- Basic utilities to work with table operations in Lua, and specific querying
-- By Zorman2000
npc.utils = {}
@ -35,3 +35,42 @@ function npc.utils.get_map_keys(map)
end
return result
end
-- This function searches for a node given the conditions specified in the
-- query object, starting from the given start_pos and up to a certain, specified
-- range.
-- Query object:
-- search_type: determines the direction to search nodes.
-- Valid values are: orthogonal, cross, cube
-- - orthogonal search means only nodes which are parallel to the search node's faces
-- will be considered. This limits the search to only 6 nodes.
-- - cross search will look at the same nodes as orthogonal, plus will also
-- check nodes diagonal to the node four horizontal nodes. This search looks at 14 nodes
-- - cube search means to look every node surrounding the node, including all diagonals.
-- This search looks at 26 nodes.
-- search_nodes: array of nodes to search for
-- surrounding_nodes: object specifying which neighbor nodes are to be expected and
-- at which locations. Valid keys are:
-- - North (+Z dir)
-- - East (+x dir)
-- - South (-Z dir)
-- - West (-X dir)
-- - Top (+Y dir)
-- - Bottom (-Y dir)
-- Example: ["bottom"] = {nodes={"default:dirt"}, criteria="all"}
-- Each object will contain nodes, and criteria for acceptance.
-- Criteria values are:
-- - any: true as long as one of the nodes on this side is one of the specified
-- in "nodes"
-- - all: true when the set of neighboring nodes on this side contain one or many of
-- the specified "nodes"
-- - all-exact: true when the set of neighboring nodes on this side contain all nodes
-- specified in "nodes"
-- - shape: true when the set of neighboring nodes on this side contains nodes in
-- the exact given shape. If so, nodes will not be an array, but a 2d array
-- of three rows and three columns, with the specific shape. Notice that
-- the nodes on the side can vary depending on the search type (orthogonal,
-- cross, cube)
function npc.utils.search_node(query, start_pos, range)
end