Occupations: Add schedule check function, allow enqueuing of schedule check
This commit is contained in:
parent
0f931d273c
commit
5a93800e77
@ -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)
|
||||
|
@ -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
|
2
init.lua
2
init.lua
@ -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
235
npc.lua
@ -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
|
||||
|
22
spawner.lua
22
spawner.lua
@ -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
|
||||
|
41
utils.lua
41
utils.lua
@ -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
|
Loading…
Reference in New Issue
Block a user