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.
@ -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
|
||||||
|
8
init.lua
@ -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
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
|
1074
spawner.lua
3
spawner_marker.lua
Normal 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
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/npc_female11.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/npc_female2.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
textures/npc_female3.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
textures/npc_female4.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
textures/npc_female5.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/npc_female6.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
textures/npc_female7.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/npc_female8.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
textures/npc_female9.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/npc_male10.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
textures/npc_male11.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
textures/npc_male12.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
textures/npc_male13.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
textures/npc_male14.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
textures/npc_male15.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
textures/npc_male8.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
textures/npc_male9.png
Normal file
After Width: | Height: | Size: 2.4 KiB |