Spawner: Large code refactor to remove dependency on plotmarkers.

Most spawner functions can now be called without giving a plotmarker.
Move scanning functions to places.lua.
Places: Cleanup and add more area-scanning functions.
Schedules: Bugfix where schedules weren't being executed due to wrong
"end" order in the do_custom() function.
Data: Moved random data to "data" folder.
Textures: Add 14 male textures and 10 female textures.
Occupations: Small tweaks to "default_basic" occupation.
This commit is contained in:
Hector Franqui 2017-08-25 09:31:45 -04:00
parent 698d247aba
commit 4c102a70a4
28 changed files with 816 additions and 741 deletions

View File

@ -102,7 +102,7 @@ function npc.places.add_owned_accessible_place(self, nodes, place_type)
-- Set owner to this NPC -- Set owner to this NPC
nodes[i].owner = self.npc_id nodes[i].owner = self.npc_id
-- Assign node to NPC -- Assign node to NPC
npc.places.add_owned(self, place_type, place_type, npc.places.add_owned(self, place_type, place_type,
nodes[i].node_pos, empty_nodes[1].pos) nodes[i].node_pos, empty_nodes[1].pos)
npc.log("DEBUG", "Added node at "..minetest.pos_to_string(nodes[i].node_pos) npc.log("DEBUG", "Added node at "..minetest.pos_to_string(nodes[i].node_pos)
.." to NPC "..dump(self.npc_name)) .." to NPC "..dump(self.npc_name))
@ -126,7 +126,7 @@ function npc.places.add_shared_accessible_place(self, nodes, place_type, overrid
-- Check if node is accessible -- Check if node is accessible
if #empty_nodes > 0 then if #empty_nodes > 0 then
-- Assign node to NPC -- Assign node to NPC
npc.places.add_shared(self, place_type..dump(i), npc.places.add_shared(self, place_type..dump(i),
place_type, nodes[i].node_pos, empty_nodes[1].pos) place_type, nodes[i].node_pos, empty_nodes[1].pos)
end end
end end
@ -139,7 +139,7 @@ function npc.places.add_shared_accessible_place(self, nodes, place_type, overrid
-- Check if node is accessible -- Check if node is accessible
if #empty_nodes > 0 then if #empty_nodes > 0 then
-- Nodes is only one node -- Nodes is only one node
npc.places.add_shared(self, place_type, place_type, npc.places.add_shared(self, place_type, place_type,
nodes.node_pos, empty_nodes[1].pos) nodes.node_pos, empty_nodes[1].pos)
end end
end end
@ -155,6 +155,12 @@ function npc.places.get_by_type(self, place_type)
return result return result
end end
---------------------------------------------------------------------------------------
-- Utility functions
---------------------------------------------------------------------------------------
-- The following are utility functions that are used to operate on nodes for
-- specific conditions
-- This function searches on a squared are of the given radius -- This function searches on a squared are of the given radius
-- for nodes of the given type. The type should be npc.places.nodes -- for nodes of the given type. The type should be npc.places.nodes
function npc.places.find_node_nearby(pos, type, radius) function npc.places.find_node_nearby(pos, type, radius)
@ -169,7 +175,7 @@ end
-- TODO: This function can be improved to support a radius greater than 1. -- TODO: This function can be improved to support a radius greater than 1.
function npc.places.find_node_orthogonally(pos, nodes, y_adjustment) function npc.places.find_node_orthogonally(pos, nodes, y_adjustment)
-- Calculate orthogonal points -- Calculate orthogonal points
local points = {} local points = {}
table.insert(points, {x=pos.x+1,y=pos.y+y_adjustment,z=pos.z}) table.insert(points, {x=pos.x+1,y=pos.y+y_adjustment,z=pos.z})
table.insert(points, {x=pos.x-1,y=pos.y+y_adjustment,z=pos.z}) table.insert(points, {x=pos.x-1,y=pos.y+y_adjustment,z=pos.z})
@ -188,11 +194,64 @@ function npc.places.find_node_orthogonally(pos, nodes, y_adjustment)
return result return result
end end
-- Wrapper around minetest.find_nodes_in_area()
-- TODO: Verify if this wrapper is actually needed
function npc.places.find_node_in_area(start_pos, end_pos, type) function npc.places.find_node_in_area(start_pos, end_pos, type)
local nodes = minetest.find_nodes_in_area(start_pos, end_pos, type) local nodes = minetest.find_nodes_in_area(start_pos, end_pos, type)
return nodes return nodes
end end
-- Function used to filter all nodes in the first floor of a building
-- If floor height isn't given, it will assume 2
-- Notice that nodes is an array of entries {node_pos={}, type={}}
function npc.places.filter_first_floor_nodes(nodes, ground_pos, floor_height)
local height = floor_height or 2
local result = {}
for _,node in pairs(nodes) do
if node.node_pos.y <= ground_pos.y + height then
table.insert(result, node)
end
end
return result
end
-- Creates an array of {pos=<node_pos>, owner=''} for managing
-- which NPC owns what
function npc.places.get_nodes_by_type(start_pos, end_pos, type)
local result = {}
local nodes = npc.places.find_node_in_area(start_pos, end_pos, type)
--minetest.log("Found "..dump(#nodes).." nodes of type: "..dump(type))
for _,node_pos in pairs(nodes) do
local entry = {}
entry["node_pos"] = node_pos
entry["owner"] = ''
table.insert(result, entry)
end
return result
end
-- Scans an area for the supported nodes: beds, benches,
-- furnaces, storage (e.g. chests) and openable (e.g. doors).
-- Returns a table with these classifications
function npc.places.scan_area_for_usable_nodes(pos1, pos2)
local result = {
bed_type = {},
sittable_type = {},
furnace_type = {},
storage_type = {},
openable_type = {}
}
local start_pos, end_pos = vector.sort(pos1, pos2)
result.bed_type = npc.places.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.BED_TYPE)
result.sittable_type = npc.places.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.SITTABLE_TYPE)
result.furnace_type = npc.places.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.FURNACE_TYPE)
result.storage_type = npc.places.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.STORAGE_TYPE)
result.openable_type = npc.places.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.OPENABLE_TYPE)
return result
end
-- Specialized function to find doors that are an entrance to a building. -- Specialized function to find doors that are an entrance to a building.
-- The definition of an entrance is: -- The definition of an entrance is:
-- The openable node with the shortest path to the plotmarker node -- The openable node with the shortest path to the plotmarker node
@ -205,7 +264,7 @@ function npc.places.find_entrance_from_openable_nodes(all_openable_nodes, marker
-- Filter out all other openable nodes except MTG doors. -- Filter out all other openable nodes except MTG doors.
-- Why? For supported village types (which are: medieval, nore -- Why? For supported village types (which are: medieval, nore
-- and logcabin) all buildings use, as the main entrance, -- and logcabin) all buildings use, as the main entrance,
-- a MTG door. Some medieval building have "half_doors" (like farms) -- a MTG door. Some medieval building have "half_doors" (like farms)
-- which NPCs love to confuse with the right building entrance. -- which NPCs love to confuse with the right building entrance.
for i = 1, #all_openable_nodes do for i = 1, #all_openable_nodes do
@ -231,18 +290,8 @@ function npc.places.find_entrance_from_openable_nodes(all_openable_nodes, marker
local start_pos = {x=open_pos.x, y=open_pos.y, z=open_pos.z} local start_pos = {x=open_pos.x, y=open_pos.y, z=open_pos.z}
local end_pos = {x=marker_pos.x, y=marker_pos.y, z=marker_pos.z} local end_pos = {x=marker_pos.x, y=marker_pos.y, z=marker_pos.z}
-- 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))
-- 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
-- end_pos.y = start_pos.y
--end
-- This adjustment allows the map to be created correctly
--start_pos.y = start_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, {})
@ -258,7 +307,7 @@ function npc.places.find_entrance_from_openable_nodes(all_openable_nodes, marker
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
min = #path min = #path
result = openable_nodes[i] result = openable_nodes[i]
else else
-- Specific check to prefer mtg's doors to cottages' doors. -- Specific check to prefer mtg's doors to cottages' doors.
-- The reason? Sometimes a cottages' door could be closer to the -- The reason? Sometimes a cottages' door could be closer to the
@ -419,4 +468,4 @@ function npc.places.find_node_in_front_of_door(door_pos)
-- Looking west -- Looking west
return {x=door_pos.x + 1, y=door_pos.y, z=door_pos.z} return {x=door_pos.x + 1, y=door_pos.y, z=door_pos.z}
end end
end end

View File

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

327
npc.lua
View File

@ -1,5 +1,5 @@
-- Advanced NPC by Zorman2000 -- Advanced NPC by Zorman2000
-- Based on original NPC by Tenplus1 -- Based on original NPC by Tenplus1
local S = mobs.intllib local S = mobs.intllib
@ -133,10 +133,10 @@ local function get_random_texture(sex, age)
for i = 1, #textures do for i = 1, #textures do
local current_texture = textures[i][1] local current_texture = textures[i][1]
if (sex == npc.MALE if (sex == npc.MALE
and string.find(current_texture, sex) and string.find(current_texture, sex)
and not string.find(current_texture, npc.FEMALE)) and not string.find(current_texture, npc.FEMALE))
or (sex == npc.FEMALE or (sex == npc.FEMALE
and string.find(current_texture, sex)) then and string.find(current_texture, sex)) then
table.insert(filtered_textures, current_texture) table.insert(filtered_textures, current_texture)
end end
@ -156,18 +156,18 @@ function npc.get_random_texture_from_array(age, sex, textures)
for i = 1, #textures do for i = 1, #textures do
local current_texture = textures[i] local current_texture = textures[i]
-- Filter by age -- Filter by age
if (sex == npc.MALE if (sex == npc.MALE
and string.find(current_texture, sex) and string.find(current_texture, sex)
and not string.find(current_texture, npc.FEMALE) and not string.find(current_texture, npc.FEMALE)
and ((age == npc.age.adult and ((age == npc.age.adult
and not string.find(current_texture, npc.age.child)) and not string.find(current_texture, npc.age.child))
or (age == npc.age.child or (age == npc.age.child
and string.find(current_texture, npc.age.child)) and string.find(current_texture, npc.age.child))
) )
) )
or (sex == npc.FEMALE or (sex == npc.FEMALE
and string.find(current_texture, sex) and string.find(current_texture, sex)
and ((age == npc.age.adult and ((age == npc.age.adult
and not string.find(current_texture, npc.age.child)) and not string.find(current_texture, npc.age.child))
or (age == npc.age.child or (age == npc.age.child
and string.find(current_texture, npc.age.child)) and string.find(current_texture, npc.age.child))
@ -185,7 +185,7 @@ function npc.get_random_texture_from_array(age, sex, textures)
return filtered_textures[math.random(1, #filtered_textures)] return filtered_textures[math.random(1, #filtered_textures)]
end end
-- Choose whether NPC can have relationships. Only 30% of NPCs -- Choose whether NPC can have relationships. Only 30% of NPCs
-- cannot have relationships -- cannot have relationships
local function can_have_relationships(is_child) local function can_have_relationships(is_child)
-- Children can't have relationships -- Children can't have relationships
@ -201,11 +201,11 @@ end
local function choose_spawn_items(self) local function choose_spawn_items(self)
local number_of_items_to_add = math.random(1, 2) local number_of_items_to_add = math.random(1, 2)
local number_of_items = #npc.FAVORITE_ITEMS[self.sex].phase1 local number_of_items = #npc.FAVORITE_ITEMS[self.sex].phase1
for i = 1, number_of_items_to_add do for i = 1, number_of_items_to_add do
npc.add_item_to_inventory( npc.add_item_to_inventory(
self, self,
npc.FAVORITE_ITEMS[self.sex].phase1[math.random(1, number_of_items)].item, npc.FAVORITE_ITEMS[self.sex].phase1[math.random(1, number_of_items)].item,
math.random(1,5) math.random(1,5)
) )
end end
@ -244,7 +244,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- Avoid NPC to be removed by mobs_redo API -- Avoid NPC to be removed by mobs_redo API
ent.remove_ok = false ent.remove_ok = false
-- Determine sex and age -- Determine sex and age
-- If there's no previous NPC data, sex and age will be randomly chosen. -- If there's no previous NPC data, sex and age will be randomly chosen.
-- - Sex: Female or male will have each 50% of spawning -- - Sex: Female or male will have each 50% of spawning
@ -256,7 +256,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- - Age: For each two adults, the chance of spawning a child next will be 50% -- - 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 -- 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. -- 40% and keeps decreasing unless two adults have no child.
-- Use NPC stats if provided -- Use NPC stats if provided
if npc_stats then if npc_stats then
-- Default chances -- Default chances
local male_s, male_e = 0, 50 local male_s, male_e = 0, 50
@ -273,7 +273,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
end end
-- Determine age probabilities -- Determine age probabilities
if npc_stats["adult_total"] >= 2 then if npc_stats["adult_total"] >= 2 then
if npc_stats["adult_total"] % 2 == 0 if npc_stats["adult_total"] % 2 == 0
and (npc_stats["adult_total"] / 2 > npc_stats["child_total"]) then and (npc_stats["adult_total"] / 2 > npc_stats["child_total"]) then
child_s,child_e = 26, 100 child_s,child_e = 26, 100
adult_e = 25 adult_e = 25
@ -307,7 +307,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
ent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10} ent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10}
ent.is_child = true ent.is_child = true
-- For mobs_redo -- For mobs_redo
ent.child = true ent.child = true
end end
-- Store the selected age -- Store the selected age
ent.age = selected_age ent.age = selected_age
@ -340,7 +340,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- Set ID -- Set ID
ent.npc_id = tostring(math.random(1000, 9999))..":"..ent.npc_name ent.npc_id = tostring(math.random(1000, 9999))..":"..ent.npc_name
-- Initialize all gift data -- Initialize all gift data
ent.gift_data = { ent.gift_data = {
-- Choose favorite items. Choose phase1 per default -- Choose favorite items. Choose phase1 per default
@ -348,7 +348,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- Choose disliked items. Choose phase1 per default -- Choose disliked items. Choose phase1 per default
disliked_items = npc.relationships.select_random_disliked_items(ent.sex), disliked_items = npc.relationships.select_random_disliked_items(ent.sex),
} }
-- Flag that determines if NPC can have a relationship -- Flag that determines if NPC can have a relationship
ent.can_have_relationship = can_have_relationships(ent.is_child) ent.can_have_relationship = can_have_relationships(ent.is_child)
@ -365,7 +365,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- Initialize dialogues -- Initialize dialogues
ent.dialogues = npc.dialogue.select_random_dialogues_for_npc(ent, "phase1") ent.dialogues = npc.dialogue.select_random_dialogues_for_npc(ent, "phase1")
-- Declare NPC inventory -- Declare NPC inventory
ent.inventory = initialize_inventory() ent.inventory = initialize_inventory()
@ -435,14 +435,14 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
is_walking = false, is_walking = false,
-- Path that the NPC is following -- Path that the NPC is following
path = {}, path = {},
-- Target position the NPC is supposed to walk to in this step. NOTE: -- Target position the NPC is supposed to walk to in this step. NOTE:
-- This is NOT the end of the path, but the next position in the path -- This is NOT the end of the path, but the next position in the path
-- relative to the last position -- relative to the last position
target_pos = {} target_pos = {}
} }
} }
-- This flag is checked on every step. If it is true, the rest of -- This flag is checked on every step. If it is true, the rest of
-- Mobs Redo API is not executed -- Mobs Redo API is not executed
ent.freeze = nil ent.freeze = nil
@ -453,7 +453,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- Schedule data -- Schedule data
ent.schedules = { ent.schedules = {
-- Flag to enable or disable the schedules functionality -- Flag to enable or disable the schedules functionality
enabled = true, enabled = true,
-- Lock for when executing a schedule -- Lock for when executing a schedule
lock = false, lock = false,
-- Queue of schedules executed -- Queue of schedules executed
@ -463,7 +463,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
--- 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 -- one for each day of the week
generic = {}, generic = {},
-- An array of schedules, meant to be for specific dates in the -- An array of schedules, meant to be for specific dates in the
-- year. Can contain as many as possible. The keys will be strings -- year. Can contain as many as possible. The keys will be strings
-- in the format MM:DD -- in the format MM:DD
date_based = {}, date_based = {},
@ -474,7 +474,8 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- If occupation name given, override properties with -- If occupation name given, override properties with
-- occupation values and initialize schedules -- occupation values and initialize schedules
minetest.log("Entity age: "..dump(ent.age)..", afult? "..dump(ent.age==npc.age.adult)) --minetest.log("Entity age: "..dump(ent.age)..", afult? "..dump(ent.age==npc.age.adult))
npc.log("INFO", "Overriding NPC values with occupation: "..dump(occupation_name))
if occupation_name and occupation_name ~= "" and ent.age == npc.age.adult then if occupation_name and occupation_name ~= "" and ent.age == npc.age.adult then
npc.occupations.initialize_occupation_values(ent, occupation_name) npc.occupations.initialize_occupation_values(ent, occupation_name)
end end
@ -508,7 +509,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)
-- Set initialized flag on -- Set initialized flag on
ent.initialized = true ent.initialized = true
npc.log("WARNING", "Spawned entity: "..dump(ent)) --npc.log("WARNING", "Spawned entity: "..dump(ent))
npc.log("INFO", "Successfully initialized NPC with name "..dump(ent.npc_name) npc.log("INFO", "Successfully initialized NPC with name "..dump(ent.npc_name)
..", sex: "..ent.sex..", is child: "..dump(ent.is_child) ..", sex: "..ent.sex..", is child: "..dump(ent.is_child)
..", texture: "..dump(ent.textures)) ..", texture: "..dump(ent.textures))
@ -561,7 +562,7 @@ function npc.add_item_to_inventory(self, item_name, count)
local existing_count = npc.get_item_count(existing_item.item_string) local existing_count = npc.get_item_count(existing_item.item_string)
if (existing_count + count) < npc.INVENTORY_ITEM_MAX_STACK then if (existing_count + count) < npc.INVENTORY_ITEM_MAX_STACK then
-- Set item here -- Set item here
self.inventory[existing_item.slot] = self.inventory[existing_item.slot] =
npc.get_item_name(existing_item.item_string).." "..tostring(existing_count + count) npc.get_item_name(existing_item.item_string).." "..tostring(existing_count + count)
return true return true
else else
@ -569,7 +570,7 @@ function npc.add_item_to_inventory(self, item_name, count)
for i = 1, #self.inventory do for i = 1, #self.inventory do
if self.inventory[i] == "" then if self.inventory[i] == "" then
-- Found slot, set item -- Found slot, set item
self.inventory[i] = self.inventory[i] =
item_name.." "..tostring((existing_count + count) - npc.INVENTORY_ITEM_MAX_STACK) item_name.." "..tostring((existing_count + count) - npc.INVENTORY_ITEM_MAX_STACK)
return true return true
end end
@ -789,7 +790,7 @@ function npc.lock_actions(self)
-- Reset timer so that it has some time after interaction is done -- Reset timer so that it has some time after interaction is done
self.actions.action_timer = 0 self.actions.action_timer = 0
-- Check if there are is an action executing -- Check if there are is an action executing
if self.actions.current_action_state == npc.action_state.executing if self.actions.current_action_state == npc.action_state.executing
and self.freeze == false then and self.freeze == false then
-- Store the current action state -- Store the current action state
self.actions.state_before_lock.action_state = self.actions.current_action_state self.actions.state_before_lock.action_state = self.actions.current_action_state
@ -809,7 +810,7 @@ function npc.unlock_actions(self)
self.actions.action_timer_lock = false self.actions.action_timer_lock = false
-- Restore the value of self.freeze -- Restore the value of self.freeze
self.freeze = self.actions.state_before_lock.freeze self.freeze = self.actions.state_before_lock.freeze
if table.getn(self.actions.queue) == 0 then if table.getn(self.actions.queue) == 0 then
-- Allow mobs_redo API to execute since action queue is empty -- Allow mobs_redo API to execute since action queue is empty
self.freeze = true self.freeze = true
@ -822,7 +823,7 @@ end
-- Schedule functionality -- Schedule functionality
--------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------
-- Schedules allow the NPC to do different things depending on the time of the day. -- Schedules allow the NPC to do different things depending on the time of the day.
-- The time of the day is in 24 hours and is consistent with the Minetest Game -- The time of the day is in 24 hours and is consistent with the Minetest Game
-- /time command. Hours will be written as numbers: 1 for 1:00, 13 for 13:00 or 1:00 PM -- /time command. Hours will be written as numbers: 1 for 1:00, 13 for 13:00 or 1:00 PM
-- The API is as following: a schedule can be created for a specific date or for a -- The API is as following: a schedule can be created for a specific date or for a
-- day of the week. A date is a string in the format MM:DD -- day of the week. A date is a string in the format MM:DD
@ -839,7 +840,7 @@ npc.schedule_properties = {
can_receive_gifts = "can_receive_gifts" can_receive_gifts = "can_receive_gifts"
} }
local function get_time_in_hours() local function get_time_in_hours()
return minetest.get_timeofday() * 24 return minetest.get_timeofday() * 24
end end
@ -848,7 +849,7 @@ end
-- - Generic: Returns nil if there are already -- - Generic: Returns nil if there are already
-- seven schedules, one for each day of the -- seven schedules, one for each day of the
-- week or if the schedule attempting to add -- week or if the schedule attempting to add
-- already exists. The date parameter is the -- already exists. The date parameter is the
-- day of the week it represents as follows: -- day of the week it represents as follows:
-- - 1: Monday -- - 1: Monday
-- - 2: Tuesday -- - 2: Tuesday
@ -879,7 +880,7 @@ function npc.create_schedule(self, schedule_type, date)
elseif schedule_type == npc.schedule_types.date then elseif schedule_type == npc.schedule_types.date then
-- Check schedule doesn't exists already -- Check schedule doesn't exists already
if self.schedules.date_based[date] == nil then if self.schedules.date_based[date] == nil then
-- Add schedule -- Add schedule
self.schedules.date_based[date] = {} self.schedules.date_based[date] = {}
else else
-- Schedule already present -- Schedule already present
@ -895,7 +896,7 @@ end
-- Schedule entries API -- Schedule entries API
-- Allows to add, get, update and delete entries from each -- Allows to add, get, update and delete entries from each
-- schedule. Attempts to be as safe-fail as possible to avoid crashes. -- schedule. Attempts to be as safe-fail as possible to avoid crashes.
-- Actions is an array of actions and tasks that the NPC -- Actions is an array of actions and tasks that the NPC
-- will perform at the scheduled time on the scheduled date -- will perform at the scheduled time on the scheduled date
@ -996,11 +997,11 @@ function npc.add_schedule_check(self)
table.insert(self.actions.queue, {action="schedule_check", args={}, is_task=false}) table.insert(self.actions.queue, {action="schedule_check", args={}, is_task=false})
end end
-- Range: integer, radius in which nodes will be searched. Recommended radius is -- Range: integer, radius in which nodes will be searched. Recommended radius is
-- between 1-3 -- between 1-3
-- Nodes: array of node names -- Nodes: array of node names
-- Actions: map of node names to entries {action=<action_enum>, args={}}. -- Actions: map of node names to entries {action=<action_enum>, args={}}.
-- Arguments can be empty - the check function will try to determine most -- Arguments can be empty - the check function will try to determine most
-- arguments anyways (like pos and dir). -- arguments anyways (like pos and dir).
-- Special node "any" will execute those actions on any node except the -- Special node "any" will execute those actions on any node except the
-- already specified ones. -- already specified ones.
@ -1049,7 +1050,7 @@ function npc.schedule_check(self)
-- behavior for placing nodes is replacing digged nodes. A NPC farmer, -- behavior for placing nodes is replacing digged nodes. A NPC farmer,
-- for instance, might dig a plant node and plant another one on the -- for instance, might dig a plant node and plant another one on the
-- same position. -- same position.
-- Defaults: items will be taken from inventory if existing, -- Defaults: items will be taken from inventory if existing,
-- if not will be force-placed (item comes from thin air) -- if not will be force-placed (item comes from thin air)
-- Protection will be respected -- Protection will be respected
args = { args = {
@ -1076,7 +1077,7 @@ function npc.schedule_check(self)
elseif actions[node.name][i].action == npc.actions.cmd.WALK_TO_POS then elseif actions[node.name][i].action == npc.actions.cmd.WALK_TO_POS then
-- Optimize walking -- since distances can be really short, -- Optimize walking -- since distances can be really short,
-- a simple walk_step() action can do most of the times. For -- a simple walk_step() action can do most of the times. For
-- this, however, we need to calculate direction -- this, however, we need to calculate direction
-- First of all, check distance -- First of all, check distance
if vector.distance(start_pos, node_pos) < 3 then if vector.distance(start_pos, node_pos) < 3 then
-- Will do walk_step based instead -- Will do walk_step based instead
@ -1105,7 +1106,7 @@ function npc.schedule_check(self)
npc.add_action(self, action or actions[node.name][i].action, args or actions[node.name][i].args) npc.add_action(self, action or actions[node.name][i].action, args or actions[node.name][i].args)
end end
-- Enqueue next schedule check -- Enqueue next schedule check
if self.schedules.current_check_params.execution_count if self.schedules.current_check_params.execution_count
< self.schedules.current_check_params.execution_times then < self.schedules.current_check_params.execution_times then
npc.add_schedule_check() npc.add_schedule_check()
end end
@ -1118,7 +1119,7 @@ function npc.schedule_check(self)
npc.add_action(self, none_actions[i].action, none_actions[i].args) npc.add_action(self, none_actions[i].action, none_actions[i].args)
end end
-- Enqueue next schedule check -- Enqueue next schedule check
if self.schedules.current_check_params.execution_count if self.schedules.current_check_params.execution_count
< self.schedules.current_check_params.execution_times then < self.schedules.current_check_params.execution_times then
npc.add_schedule_check() npc.add_schedule_check()
end end
@ -1156,7 +1157,26 @@ mobs:register_mob("advanced_npc:npc", {
{"npc_male4.png"}, {"npc_male4.png"},
{"npc_male5.png"}, {"npc_male5.png"},
{"npc_male6.png"}, {"npc_male6.png"},
{"npc_male7.png"},
{"npc_male8.png"},
{"npc_male9.png"},
{"npc_male10.png"},
{"npc_male11.png"},
{"npc_male12.png"},
{"npc_male13.png"},
{"npc_male14.png"},
{"npc_male15.png"},
{"npc_female1.png"}, -- female by nuttmeg20 {"npc_female1.png"}, -- female by nuttmeg20
{"npc_female2.png"},
{"npc_female3.png"},
{"npc_female4.png"},
{"npc_female5.png"},
{"npc_female6.png"},
{"npc_female7.png"},
{"npc_female8.png"},
{"npc_female9.png"},
{"npc_female10.png"},
{"npc_female11.png"},
}, },
child_texture = { child_texture = {
{"npc_child_male1.png"}, {"npc_child_male1.png"},
@ -1206,13 +1226,13 @@ mobs:register_mob("advanced_npc:npc", {
local item = clicker:get_wielded_item() local item = clicker:get_wielded_item()
local name = clicker:get_player_name() local name = clicker:get_player_name()
npc.log("DEBUG", "Right-clicked NPC: "..dump(self)) npc.log("INFO", "Right-clicked NPC: "..dump(self))
-- Receive gift or start chat. If player has no item in hand -- Receive gift or start chat. If player has no item in hand
-- then it is going to start chat directly -- 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 if self.can_have_relationship
and self.can_receive_gifts and self.can_receive_gifts
and item:to_table() ~= nil then and item:to_table() ~= nil then
-- Get item name -- Get item name
local item = minetest.registered_items[item:get_name()] local item = minetest.registered_items[item:get_name()]
@ -1232,16 +1252,15 @@ mobs:register_mob("advanced_npc:npc", {
end, end,
name name
) )
else else
npc.start_dialogue(self, clicker, true) npc.start_dialogue(self, clicker, true)
end end
end,
end,
do_custom = function(self, dtime) do_custom = function(self, dtime)
if self.initialized == nil then if self.initialized == nil then
-- Initialize NPC if spawned using the spawn egg built in from -- Initialize NPC if spawned using the spawn egg built in from
-- mobs_redo. This functionality will be removed in the future in -- mobs_redo. This functionality will be removed in the future in
-- favor of a better manual spawning method with customization -- favor of a better manual spawning method with customization
npc.log("WARNING", "Initializing NPC from entity step. This message should only be appearing if an NPC is being spawned from inventory with egg!") npc.log("WARNING", "Initializing NPC from entity step. This message should only be appearing if an NPC is being spawned from inventory with egg!")
npc.initialize(self, self.object:getpos(), true) npc.initialize(self, self.object:getpos(), true)
self.tamed = false self.tamed = false
@ -1250,7 +1269,7 @@ mobs:register_mob("advanced_npc:npc", {
-- NPC is initialized, check other variables -- NPC is initialized, check other variables
-- Check child texture issues -- Check child texture issues
if self.is_child then if self.is_child then
-- Check texture -- Check texture
npc.texture_check.timer = npc.texture_check.timer + dtime npc.texture_check.timer = npc.texture_check.timer + dtime
if npc.texture_check.timer > npc.texture_check.interval then if npc.texture_check.timer > npc.texture_check.interval then
-- Reset timer -- Reset timer
@ -1266,19 +1285,20 @@ mobs:register_mob("advanced_npc:npc", {
-- Set interval to large interval so this code isn't called frequently -- Set interval to large interval so this code isn't called frequently
npc.texture_check.interval = 60 npc.texture_check.interval = 60
end end
end end
end
-- Timer function for casual traders to reset their trade offers -- Timer function for casual traders to reset their trade offers
self.trader_data.change_offers_timer = self.trader_data.change_offers_timer + dtime self.trader_data.change_offers_timer = self.trader_data.change_offers_timer + dtime
-- Check if time has come to change offers -- Check if time has come to change offers
if self.trader_data.trader_status == npc.trade.CASUAL and if self.trader_data.trader_status == npc.trade.CASUAL and
self.trader_data.change_offers_timer >= self.trader_data.change_offers_timer_interval then self.trader_data.change_offers_timer >= self.trader_data.change_offers_timer_interval then
-- Reset timer -- Reset timer
self.trader_data.change_offers_timer = 0 self.trader_data.change_offers_timer = 0
-- Re-select casual trade offers -- Re-select casual trade offers
npc.trade.generate_trade_offers_by_status(self) npc.trade.generate_trade_offers_by_status(self)
end end
-- Timer function for gifts -- Timer function for gifts
for i = 1, #self.relationships do for i = 1, #self.relationships do
local relationship = self.relationships[i] local relationship = self.relationships[i]
@ -1291,15 +1311,15 @@ mobs:register_mob("advanced_npc:npc", {
relationship.talk_timer_value = relationship.talk_timer_value + dtime relationship.talk_timer_value = relationship.talk_timer_value + dtime
else else
-- Relationship decrease timer -- Relationship decrease timer
if relationship.relationship_decrease_timer_value if relationship.relationship_decrease_timer_value
< relationship.relationship_decrease_interval then < relationship.relationship_decrease_interval then
relationship.relationship_decrease_timer_value = relationship.relationship_decrease_timer_value =
relationship.relationship_decrease_timer_value + dtime relationship.relationship_decrease_timer_value + dtime
else else
-- Check if married to decrease half -- Check if married to decrease half
if relationship.phase == "phase6" then if relationship.phase == "phase6" then
-- Avoid going below the marriage phase limit -- Avoid going below the marriage phase limit
if (relationship.points - 0.5) >= if (relationship.points - 0.5) >=
npc.relationships.RELATIONSHIP_PHASE["phase5"].limit then npc.relationships.RELATIONSHIP_PHASE["phase5"].limit then
relationship.points = relationship.points - 0.5 relationship.points = relationship.points - 0.5
end end
@ -1335,104 +1355,103 @@ mobs:register_mob("advanced_npc:npc", {
end end
end end
-- Schedule timer -- Schedule timer
-- Check if schedules are enabled -- Check if schedules are enabled
if self.schedules.enabled == true then if self.schedules.enabled == true then
-- Get time of day -- Get time of day
local time = get_time_in_hours() local time = get_time_in_hours()
-- Check if time is an hour -- Check if time is an hour
if time % 1 < 0.1 and self.schedules.lock == false then if ((time % 1) < dtime) and self.schedules.lock == false then
-- Activate lock to avoid more than one entry to this code -- Activate lock to avoid more than one entry to this code
self.schedules.lock = true self.schedules.lock = true
-- Get integer part of time -- Get integer part of time
time = (time) - (time % 1) time = (time) - (time % 1)
-- Check if there is a schedule entry for this time -- Check if there is a schedule entry for this time
-- Note: Currently only one schedule is supported, for day 0 -- Note: Currently only one schedule is supported, for day 0
--minetest.log("Time: "..dump(time)) minetest.log("Time: "..dump(time))
local schedule = self.schedules.generic[0] local schedule = self.schedules.generic[0]
if schedule ~= nil then if schedule ~= nil then
-- Check if schedule for this time exists -- Check if schedule for this time exists
--minetest.log("Found default schedule") if schedule[time] ~= nil then
if schedule[time] ~= nil then npc.log("WARNING", "Found schedule for time "..dump(time)..": "..dump(schedule[time]))
npc.log("WARNING", "Found schedule for time "..dump(time)..": "..dump(schedule[time])) npc.log("DEBUG", "Adding actions to action queue")
npc.log("DEBUG", "Adding actions to action queue") -- Add to action queue all actions on schedule
-- Add to action queue all actions on schedule for i = 1, #schedule[time] do
for i = 1, #schedule[time] do -- Check if schedule has a check function
-- Check if schedule has a check function if schedule[time][i].check then
if not schedule[time][i].check then -- Add parameters for check function and run for first time
-- 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()))
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]
local check_params = schedule[time][i] -- Calculates how many times check will be executed
-- Calculates how many times check will be executed local execution_times = check_params.count
local execution_times = check_params.count if check_params.random_execution_times then
if check_params.random_execution_times then execution_times = math.random(check_params.min_count, check_params.max_count)
execution_times = math.random(check_params.min_count, check_params.max_count) end
end -- Set current parameters
-- Set current parameters self.schedules.current_check_params = {
self.schedules.current_check_params = { range = check_params.range,
range = check_params.range, nodes = check_params.nodes,
nodes = check_params.nodes, actions = check_params.actions,
actions = check_params.actions, none_actions = check_params.none_actions,
none_actions = check_params.none_actions, execution_count = 0,
execution_count = 0, execution_times = execution_times
execution_times = execution_times }
} -- Execute check for the first time
-- Execute check for the first time npc.schedule_check(self)
npc.schedule_check(self) else
else npc.log("INFO", "Executing schedule entry: "..dump(schedule[time][i]))
-- Run usual schedule entry -- Run usual schedule entry
-- Check chance -- Check chance
local execution_chance = math.random(1, 100) local execution_chance = math.random(1, 100)
if not schedule[time][i].chance or if not schedule[time][i].chance or
(schedule[time][i].chance and execution_chance <= schedule[time][i].chance) then (schedule[time][i].chance and execution_chance <= schedule[time][i].chance) then
-- Check if entry has dependency on other entry -- Check if entry has dependency on other entry
local dependencies_met = nil local dependencies_met = nil
if schedule[time][i].depends then if schedule[time][i].depends then
dependencies_met = npc.utils.array_is_subset_of_array( dependencies_met = npc.utils.array_is_subset_of_array(
self.schedules.temp_executed_queue, self.schedules.temp_executed_queue,
schedule[time][i].depends) schedule[time][i].depends)
end end
-- Check for dependencies being met -- Check for dependencies being met
if dependencies_met == nil or dependencies_met == true then if dependencies_met == nil or dependencies_met == true then
-- Add tasks -- Add tasks
if schedule[time][i].task ~= nil then if schedule[time][i].task ~= nil then
-- Add task -- Add task
npc.add_task(self, schedule[time][i].task, schedule[time][i].args) npc.add_task(self, schedule[time][i].task, schedule[time][i].args)
elseif schedule[time][i].action ~= nil then elseif schedule[time][i].action ~= nil then
-- Add action -- Add action
npc.add_action(self, schedule[time][i].action, schedule[time][i].args) npc.add_action(self, schedule[time][i].action, schedule[time][i].args)
elseif schedule[time][i].property ~= nil then elseif schedule[time][i].property ~= nil then
-- Change NPC property -- Change NPC property
npc.schedule_change_property(self, schedule[time][i].property, schedule[time][i].args) npc.schedule_change_property(self, schedule[time][i].property, schedule[time][i].args)
end end
-- Backward compatibility check -- Backward compatibility check
if self.schedules.temp_executed_queue then if self.schedules.temp_executed_queue then
-- Add into execution queue to meet dependency -- Add into execution queue to meet dependency
table.insert(self.schedules.temp_executed_queue, i) table.insert(self.schedules.temp_executed_queue, i)
end
end end
else
-- TODO: Change to debug
npc.log("WARNING", "Skipping schedule entry for time "..dump(time)..": "..dump(schedule[time][i]))
end end
end else
end -- TODO: Change to debug
-- Clear execution queue npc.log("WARNING", "Skipping schedule entry for time "..dump(time)..": "..dump(schedule[time][i]))
self.schedules.temp_executed_queue = {} end
npc.log("WARNING", "New action queue: "..dump(self.actions)) end
end end
end -- Clear execution queue
end self.schedules.temp_executed_queue = {}
else npc.log("WARNING", "New action queue: "..dump(self.actions))
-- Check if lock can be released end
if time % 1 > 0.1 then end
-- Release lock else
self.schedules.lock = false -- Check if lock can be released
end if (time % 1) > dtime then
end -- Release lock
end self.schedules.lock = false
end
end
end
return self.freeze return self.freeze
end end
}) })

View File

@ -6,7 +6,7 @@
-- Occupations are essentially specific schedules, that can have slight -- Occupations are essentially specific schedules, that can have slight
-- random variations to provide diversity and make specific occupations -- random variations to provide diversity and make specific occupations
-- less predictable. Occupations are associated with textures, dialogues, -- less predictable. Occupations are associated with textures, dialogues,
-- specific initial items, type of building (and surroundings) where NPC -- specific initial items, type of building (and surroundings) where NPC
-- lives, etc. -- lives, etc.
-- Example of an occupation: farmer -- Example of an occupation: farmer
-- The farmer will have to live in a farm, or just beside a field. -- The farmer will have to live in a farm, or just beside a field.
@ -38,10 +38,10 @@
-- tags = {}, -- tags = {},
-- -- Array of tags to search for. This will have tags -- -- Array of tags to search for. This will have tags
-- -- if the type is either "mix" or "tags" -- -- if the type is either "mix" or "tags"
-- --
-- }, -- },
-- textures = {}, -- textures = {},
-- -- Textures are an array of textures, as usually given on -- -- Textures are an array of textures, as usually given on
-- -- an entity definition. If given, the NPC will be guaranteed -- -- an entity definition. If given, the NPC will be guaranteed
-- -- to have one of the given textures. Also, ensure they have sex -- -- to have one of the given textures. Also, ensure they have sex
-- -- as well in the filename so they can be chosen appropriately. -- -- as well in the filename so they can be chosen appropriately.
@ -76,10 +76,10 @@
-- [23] = {[1] = action=npc.action.cmd.freeze, args={freeze=false}} -- [23] = {[1] = action=npc.action.cmd.freeze, args={freeze=false}}
-- -- } -- -- }
-- -- The numbers, [1], [13] and [23] are the times when the entries -- -- The numbers, [1], [13] and [23] are the times when the entries
-- -- corresponding to each are supposed to happen. The tables with -- -- corresponding to each are supposed to happen. The tables with
-- -- [1], [1],[2] and [1] actions respectively are the entries that -- -- [1], [1],[2] and [1] actions respectively are the entries that
-- -- will happen at time 1, 13 and 23. -- -- will happen at time 1, 13 and 23.
-- } -- }
-- Public API -- Public API
npc.occupations = {} npc.occupations = {}
@ -91,14 +91,11 @@ local occupations = {}
-- The key is the name of the occupation. -- The key is the name of the occupation.
npc.occupations.registered_occupations = {} npc.occupations.registered_occupations = {}
-- This is the basic occupation definition, this is for all NPCs that -- This is the basic occupation definition, this is for all NPCs that
-- don't have a specific occupation. It serves as an example. -- don't have a specific occupation. It serves as an example.
npc.occupations.basic_def = { npc.occupations.basic_def = {
-- Use random textures -- Use random textures
textures = { textures = {},
{"npc_male1.png"},
{"npc_child_male1.png"}
},
-- Use random dialogues -- Use random dialogues
dialogues = {}, dialogues = {},
-- Initialize inventory with random items -- Initialize inventory with random items
@ -109,13 +106,13 @@ npc.occupations.basic_def = {
[7] = { [7] = {
-- Get out of bed -- Get out of bed
[1] = {task = npc.actions.cmd.USE_BED, args = { [1] = {task = npc.actions.cmd.USE_BED, args = {
pos = npc.places.PLACE_TYPE.BED.PRIMARY, pos = npc.places.PLACE_TYPE.BED.PRIMARY,
action = npc.actions.const.beds.GET_UP action = npc.actions.const.beds.GET_UP
} }
}, },
-- Walk to home inside -- Walk to home inside
[2] = {task = npc.actions.cmd.WALK_TO_POS, args = { [2] = {task = npc.actions.cmd.WALK_TO_POS, args = {
end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,
walkable = {} walkable = {}
}, },
chance = 75 chance = 75
@ -127,7 +124,7 @@ npc.occupations.basic_def = {
[8] = { [8] = {
-- Walk to outside of home -- Walk to outside of home
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = { [1] = {task = npc.actions.cmd.WALK_TO_POS, args = {
end_pos = npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, end_pos = npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE,
walkable = {} walkable = {}
}, },
chance = 75 chance = 75
@ -142,7 +139,7 @@ npc.occupations.basic_def = {
end_pos = {place_type=npc.places.PLACE_TYPE.SITTABLE.PRIMARY, use_access_node=true}, end_pos = {place_type=npc.places.PLACE_TYPE.SITTABLE.PRIMARY, use_access_node=true},
walkable = {"cottages:bench"} walkable = {"cottages:bench"}
}, },
chance = 75 chance = 75
}, },
-- Sit on the node -- Sit on the node
[2] = {task = npc.actions.cmd.USE_SITTABLE, args = { [2] = {task = npc.actions.cmd.USE_SITTABLE, args = {
@ -155,13 +152,13 @@ npc.occupations.basic_def = {
[3] = {action = npc.actions.cmd.SET_INTERVAL, args = { [3] = {action = npc.actions.cmd.SET_INTERVAL, args = {
freeze = true, freeze = true,
interval = 35 interval = 35
}, },
depends = {2} depends = {2}
}, },
[4] = {action = npc.actions.cmd.SET_INTERVAL, args = { [4] = {action = npc.actions.cmd.SET_INTERVAL, args = {
freeze = true, freeze = true,
interval = npc.actions.default_interval interval = npc.actions.default_interval
}, },
depends = {3} depends = {3}
}, },
-- Get up from sit -- Get up from sit
@ -210,9 +207,9 @@ npc.occupations.basic_def = {
}, },
-- Get inside home -- Get inside home
[3] = {task = npc.actions.cmd.WALK_TO_POS, args = { [3] = {task = npc.actions.cmd.WALK_TO_POS, args = {
end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,
walkable = {} walkable = {}
} }
}, },
-- Allow mobs_redo wandering -- Allow mobs_redo wandering
[4] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}} [4] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
@ -220,16 +217,16 @@ npc.occupations.basic_def = {
-- Schedule entry for 10 in the evening -- Schedule entry for 10 in the evening
[22] = { [22] = {
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = { [1] = {task = npc.actions.cmd.WALK_TO_POS, args = {
end_pos = {place_type=npc.places.PLACE_TYPE.BED.PRIMARY, use_access_node=true}, end_pos = {place_type=npc.places.PLACE_TYPE.BED.PRIMARY, use_access_node=true},
walkable = {} walkable = {}
} }
}, },
-- Use bed -- Use bed
[2] = {task = npc.actions.cmd.USE_BED, args = { [2] = {task = npc.actions.cmd.USE_BED, args = {
pos = npc.places.PLACE_TYPE.BED.PRIMARY, pos = npc.places.PLACE_TYPE.BED.PRIMARY,
action = npc.actions.const.beds.LAY action = npc.actions.const.beds.LAY
} }
}, },
-- Stay put on bed -- Stay put on bed
[3] = {action = npc.actions.cmd.FREEZE, args = {freeze = true}} [3] = {action = npc.actions.cmd.FREEZE, args = {freeze = true}}
} }
@ -239,7 +236,8 @@ npc.occupations.basic_def = {
-- This function registers an occupation -- This function registers an occupation
function npc.occupations.register_occupation(name, def) function npc.occupations.register_occupation(name, def)
npc.occupations.registered_occupations[name] = def npc.occupations.registered_occupations[name] = def
npc.log("INFO", "Successfully registered occupation with name: "..dump(name))
end end
-- This function scans all registered occupations and filter them by -- This function scans all registered occupations and filter them by
@ -256,13 +254,13 @@ function npc.occupations.get_for_building(building_type, surrounding_building_ty
-- Check if building type is contained in the def's building types -- Check if building type is contained in the def's building types
if npc.utils.array_contains(def.building_types, building_type) then if npc.utils.array_contains(def.building_types, building_type) then
-- Check for empty or nil surrounding building types -- Check for empty or nil surrounding building types
if def.surrounding_building_types == nil if def.surrounding_building_types == nil
or def.surrounding_building_types == {} then or def.surrounding_building_types == {} then
-- Add this occupation -- Add this occupation
table.insert(result, name) table.insert(result, name)
else else
-- Check if surround type is included in the def -- Check if surround type is included in the def
if npc.utils.array_is_subset_of_array(def.surrounding_building_types, if npc.utils.array_is_subset_of_array(def.surrounding_building_types,
surrounding_building_types) then surrounding_building_types) then
-- Add this occupation -- Add this occupation
table.insert(result, name) table.insert(result, name)
@ -291,7 +289,7 @@ function npc.occupations.initialize_occupation_values(self, occupation_name)
-- Initialize textures, else it will leave the current textures -- Initialize textures, else it will leave the current textures
minetest.log("Texture entries: "..dump(table.getn(def.textures))) minetest.log("Texture entries: "..dump(table.getn(def.textures)))
if def.textures and table.getn(def.textures) > 0 then if def.textures and table.getn(def.textures) > 0 then
self.selected_texture = self.selected_texture =
npc.get_random_texture_from_array(self.sex, self.age, def.textures) npc.get_random_texture_from_array(self.sex, self.age, def.textures)
-- Set texture if it found for sex and age -- Set texture if it found for sex and age
minetest.log("No textures found for sex "..dump(self.sex).." and age "..dump(self.age)) minetest.log("No textures found for sex "..dump(self.sex).." and age "..dump(self.age))
@ -351,7 +349,7 @@ function npc.occupations.initialize_occupation_values(self, occupation_name)
-- Add keys to set of dialogue keys -- Add keys to set of dialogue keys
for _, key in npc.utils.get_map_keys(dialogues) do for _, key in npc.utils.get_map_keys(dialogues) do
table.insert(dialogue_keys, key) table.insert(dialogue_keys, key)
end end
elseif def.dialogues.type == "tags" then elseif def.dialogues.type == "tags" then
-- We need to find the dialogues from tags. def.dialogues.tags contains -- We need to find the dialogues from tags. def.dialogues.tags contains
-- an array of tags that we will use to search. -- an array of tags that we will use to search.
@ -361,7 +359,7 @@ function npc.occupations.initialize_occupation_values(self, occupation_name)
end end
-- Add dialogues to NPC -- Add dialogues to NPC
-- Check if there is a max of dialogues to be added -- Check if there is a max of dialogues to be added
local max_dialogue_count = npc.dialogue.MAX_DIALOGUES local max_dialogue_count = npc.dialogue.MAX_DIALOGUES
if def.dialogues.max_count and def.max_dialogue_count > 0 then if def.dialogues.max_count and def.max_dialogue_count > 0 then
max_dialogue_count = def.dialogues.max_count max_dialogue_count = def.dialogues.max_count
end end
@ -385,4 +383,4 @@ function npc.occupations.initialize_occupation_values(self, occupation_name)
npc.log("INFO", "Successfully initialized NPC with occupation values") npc.log("INFO", "Successfully initialized NPC with occupation values")
end end

File diff suppressed because it is too large Load Diff

3
spawner_marker.lua Normal file
View File

@ -0,0 +1,3 @@
-- Spawner markers
-- Specialized functionality to allow players do NPC spawning
-- on their own custom buildings.

BIN
textures/npc_female10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
textures/npc_female11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
textures/npc_female2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
textures/npc_female3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
textures/npc_female4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
textures/npc_female5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
textures/npc_female6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
textures/npc_female7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
textures/npc_female8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
textures/npc_female9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
textures/npc_male10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
textures/npc_male11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
textures/npc_male12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
textures/npc_male13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
textures/npc_male14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
textures/npc_male15.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
textures/npc_male8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
textures/npc_male9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB