diff --git a/.idea/shelf/Add_move_states_to_avoid_laying_sitting_bug__WIP_.xml b/.idea/shelf/Add_move_states_to_avoid_laying_sitting_bug__WIP_.xml
new file mode 100644
index 0000000..abe74e4
--- /dev/null
+++ b/.idea/shelf/Add_move_states_to_avoid_laying_sitting_bug__WIP_.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/shelf/Add_move_states_to_avoid_laying_sitting_bug__WIP_/shelved.patch b/.idea/shelf/Add_move_states_to_avoid_laying_sitting_bug__WIP_/shelved.patch
new file mode 100644
index 0000000..446ccc0
--- /dev/null
+++ b/.idea/shelf/Add_move_states_to_avoid_laying_sitting_bug__WIP_/shelved.patch
@@ -0,0 +1,84 @@
+Index: npc.lua
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>-- Advanced NPC by Zorman2000\n-- Based on original NPC by Tenplus1\n\nlocal S = mobs.intllib\n\nnpc = {}\n\n-- Constants\nnpc.FEMALE = \"female\"\nnpc.MALE = \"male\"\n\nnpc.age = {\n\tadult = \"adult\",\n\tchild = \"child\"\n}\n\nnpc.INVENTORY_ITEM_MAX_STACK = 99\n\nnpc.ANIMATION_STAND_START = 0\nnpc.ANIMATION_STAND_END = 79\nnpc.ANIMATION_SIT_START = 81\nnpc.ANIMATION_SIT_END = 160\nnpc.ANIMATION_LAY_START = 162\nnpc.ANIMATION_LAY_END = 166\nnpc.ANIMATION_WALK_START = 168\nnpc.ANIMATION_WALK_END = 187\nnpc.ANIMATION_MINE_START = 189\nnpc.ANIMATION_MINE_END =198\n\nnpc.direction = {\n\tnorth = 0,\n\teast = 1,\n\tsouth = 2,\n\twest = 3,\n\tnorth_east = 4,\n\tnorth_west = 5,\n\tsouth_east = 6,\n\tsouth_west = 7\n}\n\nnpc.action_state = {\n\tnone = 0,\n\texecuting = 1,\n\tinterrupted = 2\n}\n\nnpc.log_level = {\n\tINFO = true,\n\tWARNING = true,\n\tERROR = true,\n\tDEBUG = false,\n\tDEBUG_ACTION = false,\n\tDEBUG_SCHEDULE = false\n}\n\nnpc.texture_check = {\n\ttimer = 0,\n\tinterval = 2\n}\n\n---------------------------------------------------------------------------------------\n-- General functions\n---------------------------------------------------------------------------------------\n-- Logging\nfunction npc.log(level, message)\n\tif npc.log_level[level] then\n\t\tminetest.log(\"[advanced_npc] \"..level..\": \"..message)\n\tend\nend\n\n-- NPC chat\nfunction npc.chat(npc_name, player_name, message)\n\tminetest.chat_send_player(player_name, npc_name..\": \"..message)\nend\n\n-- Simple wrapper over minetest.add_particle()\n-- Copied from mobs_redo/api.lua\nfunction npc.effect(pos, amount, texture, min_size, max_size, radius, gravity, glow)\n\n\tradius = radius or 2\n\tmin_size = min_size or 0.5\n\tmax_size = max_size or 1\n\tgravity = gravity or -10\n\tglow = glow or 0\n\n\tminetest.add_particlespawner({\n\t\tamount = amount,\n\t\ttime = 0.25,\n\t\tminpos = pos,\n\t\tmaxpos = pos,\n\t\tminvel = {x = -radius, y = -radius, z = -radius},\n\t\tmaxvel = {x = radius, y = radius, z = radius},\n\t\tminacc = {x = 0, y = gravity, z = 0},\n\t\tmaxacc = {x = 0, y = gravity, z = 0},\n\t\tminexptime = 0.1,\n\t\tmaxexptime = 1,\n\t\tminsize = min_size,\n\t\tmaxsize = max_size,\n\t\ttexture = texture,\n\t\tglow = glow,\n\t})\nend\n\n-- Gets name of player or NPC\nfunction npc.get_entity_name(entity)\n\tif entity:is_player() then\n\t\treturn entity:get_player_name()\n\telse\n\t\treturn entity:get_luaentity().name\n\tend\nend\n\n-- Returns the item \"wielded\" by player or NPC\n-- TODO: Implement NPC\nfunction npc.get_entity_wielded_item(entity)\n\tif entity:is_player() then\n\t\treturn entity:get_wielded_item()\n\tend\nend\n\n---------------------------------------------------------------------------------------\n-- Spawning functions\n---------------------------------------------------------------------------------------\n-- These functions are used at spawn time to determine several\n-- random attributes for the NPC in case they are not already\n-- defined. On a later phase, pre-defining many of the NPC values\n-- will be allowed.\n\nlocal function get_random_name(sex)\n\tlocal i = math.random(#npc.data.FIRST_NAMES[sex])\n\treturn npc.data.FIRST_NAMES[sex][i]\nend\n\nlocal function initialize_inventory()\n\treturn {\n\t\t[1] = \"\", [2] = \"\", [3] = \"\", [4] = \"\",\n\t\t[5] = \"\", [6] = \"\", [7] = \"\", [8] = \"\",\n\t\t[9] = \"\", [10] = \"\", [11] = \"\", [12] = \"\",\n\t\t[13] = \"\", [14] = \"\", [15] = \"\", [16] = \"\",\n\t}\nend\n\n-- This function checks for \"female\" text on the texture name\nlocal function is_female_texture(textures)\n\tfor i = 1, #textures do\n\t\tif string.find(textures[i], \"female\") ~= nil then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nfunction npc.assign_sex_from_texture(self)\n\tif is_female_texture(self.base_texture) then\n\t\treturn npc.FEMALE\n\telse\n\t\treturn npc.MALE\n\tend\nend\n\nlocal function get_random_texture(sex, age)\n\tlocal textures = {}\n\tlocal filtered_textures = {}\n\t-- Find textures by sex and age\n\tif age == npc.age.adult then\n\t\t--minetest.log(\"Registered: \"..dump(minetest.registered_entities[\"advanced_npc:npc\"]))\n\t\ttextures = minetest.registered_entities[\"advanced_npc:npc\"].texture_list\n\telseif age == npc.age.child then\n\t\ttextures = minetest.registered_entities[\"advanced_npc:npc\"].child_texture\n\tend\n\n\tfor i = 1, #textures do\n\t\tlocal current_texture = textures[i][1]\n\t\tif (sex == npc.MALE\n\t\t\t\tand string.find(current_texture, sex)\n\t\t\t\tand not string.find(current_texture, npc.FEMALE))\n\t\t\t\tor (sex == npc.FEMALE\n\t\t\t\tand string.find(current_texture, sex)) then\n\t\t\ttable.insert(filtered_textures, current_texture)\n\t\tend\n\tend\n\n\t-- Check if filtered textures is empty\n\tif filtered_textures == {} then\n\t\treturn textures[1][1]\n\tend\n\n\treturn filtered_textures[math.random(1,#filtered_textures)]\nend\n\nfunction npc.get_random_texture_from_array(age, sex, textures)\n\tlocal filtered_textures = {}\n\n\tfor i = 1, #textures do\n\t\tlocal current_texture = textures[i]\n\t\t-- Filter by age\n\t\tif (sex == npc.MALE\n\t\t\t\tand string.find(current_texture, sex)\n\t\t\t\tand not string.find(current_texture, npc.FEMALE)\n\t\t\t\tand ((age == npc.age.adult\n\t\t\t\tand not string.find(current_texture, npc.age.child))\n\t\t\t\tor (age == npc.age.child\n\t\t\t\tand string.find(current_texture, npc.age.child))\n\t\t)\n\t\t)\n\t\t\t\tor (sex == npc.FEMALE\n\t\t\t\tand string.find(current_texture, sex)\n\t\t\t\tand ((age == npc.age.adult\n\t\t\t\tand not string.find(current_texture, npc.age.child))\n\t\t\t\tor (age == npc.age.child\n\t\t\t\tand string.find(current_texture, npc.age.child))\n\t\t)\n\t\t) then\n\t\t\ttable.insert(filtered_textures, current_texture)\n\t\tend\n\tend\n\n\t-- Check if there are no textures\n\tif #filtered_textures == 0 then\n\t\t-- Return whole array for re-evaluation\n\t\tnpc.log(\"DEBUG\", \"No textures found, returning original array\")\n\t\treturn textures\n\tend\n\n\treturn filtered_textures[math.random(1, #filtered_textures)]\nend\n\n-- Choose whether NPC can have relationships. Only 30% of NPCs\n-- cannot have relationships\nlocal function can_have_relationships(is_child)\n\t-- Children can't have relationships\n\tif is_child then\n\t\treturn false\n\tend\n\tlocal chance = math.random(1,10)\n\treturn chance > 3\nend\n\n-- Choose a maximum of two items that the NPC will have at spawn time\n-- These items are chosen from the favorite items list.\nlocal function choose_spawn_items(self)\n\tlocal number_of_items_to_add = math.random(1, 2)\n\tlocal number_of_items = #npc.FAVORITE_ITEMS[self.sex].phase1\n\n\tfor i = 1, number_of_items_to_add do\n\t\tnpc.add_item_to_inventory(\n\t\t\tself,\n\t\t\tnpc.FAVORITE_ITEMS[self.sex].phase1[math.random(1, number_of_items)].item,\n\t\t\tmath.random(1,5)\n\t\t)\n\tend\n\t-- Add currency to the items spawned with. Will add 5-10 tier 3\n\t-- currency items\n\tlocal currency_item_count = math.random(5, 10)\n\tnpc.add_item_to_inventory(self, npc.trade.prices.currency.tier3.string, currency_item_count)\n\n\t-- For test\n\t--npc.add_item_to_inventory(self, \"default:tree\", 10)\n\t--npc.add_item_to_inventory(self, \"default:cobble\", 10)\n\t--npc.add_item_to_inventory(self, \"default:diamond\", 2)\n\t--npc.add_item_to_inventory(self, \"default:mese_crystal\", 2)\n\t--npc.add_item_to_inventory(self, \"flowers:rose\", 2)\n\t--npc.add_item_to_inventory(self, \"advanced_npc:marriage_ring\", 2)\n\t--npc.add_item_to_inventory(self, \"flowers:geranium\", 2)\n\t--npc.add_item_to_inventory(self, \"mobs:meat\", 2)\n\t--npc.add_item_to_inventory(self, \"mobs:leather\", 2)\n\t--npc.add_item_to_inventory(self, \"default:sword_stone\", 2)\n\t--npc.add_item_to_inventory(self, \"default:shovel_stone\", 2)\n\t--npc.add_item_to_inventory(self, \"default:axe_stone\", 2)\n\n\t--minetest.log(\"Initial inventory: \"..dump(self.inventory))\nend\n\n-- Spawn function. Initializes all variables that the\n-- NPC will have and choose random, starting values\nfunction npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name)\n\tnpc.log(\"INFO\", \"Initializing NPC at \"..minetest.pos_to_string(pos))\n\n\t-- Get variables\n\tlocal ent = entity\n\tif not is_lua_entity then\n\t\tent = entity:get_luaentity()\n\tend\n\n\t-- Avoid NPC to be removed by mobs_redo API\n\tent.remove_ok = false\n\n\t-- Determine sex and age\n\t-- If there's no previous NPC data, sex and age will be randomly chosen.\n\t-- - Sex: Female or male will have each 50% of spawning\n\t-- - Age: 90% chance of spawning adults, 10% chance of spawning children.\n\t-- If there is previous data then:\n\t-- - Sex: The unbalanced sex will get a 75% chance of spawning\n\t-- - Example: If there's one male, then female will have 75% spawn chance.\n\t-- - If there's male and female, then each have 50% spawn chance.\n\t-- - Age: For each two adults, the chance of spawning a child next will be 50%\n\t-- If there's a child for two adults, the chance of spawning a child goes to\n\t-- 40% and keeps decreasing unless two adults have no child.\n\t-- Use NPC stats if provided\n\tif npc_stats then\n\t\t-- Default chances\n\t\tlocal male_s, male_e = 0, 50\n\t\tlocal female_s, female_e = 51, 100\n\t\tlocal adult_s, adult_e = 0, 85\n\t\tlocal child_s, child_e = 86, 100\n\t\t-- Determine sex probabilities\n\t\tif npc_stats[npc.FEMALE].total > npc_stats[npc.MALE].total then\n\t\t\tmale_e = 75\n\t\t\tfemale_s, female_e = 76, 100\n\t\telseif npc_stats[npc.FEMALE].total < npc_stats[npc.MALE].total then\n\t\t\tmale_e = 25\n\t\t\tfemale_s, female_e = 26, 100\n\t\tend\n\t\t-- Determine age probabilities\n\t\tif npc_stats[\"adult_total\"] >= 2 then\n\t\t\tif npc_stats[\"adult_total\"] % 2 == 0\n\t\t\t\t\tand (npc_stats[\"adult_total\"] / 2 > npc_stats[\"child_total\"]) then\n\t\t\t\tchild_s,child_e = 26, 100\n\t\t\t\tadult_e = 25\n\t\t\telse\n\t\t\t\tchild_s, child_e = 61, 100\n\t\t\t\tadult_e = 60\n\t\t\tend\n\t\tend\n\t\t-- Get sex and age based on the probabilities\n\t\tlocal sex_chance = math.random(1, 100)\n\t\tlocal age_chance = math.random(1, 100)\n\t\tlocal selected_sex = \"\"\n\t\tlocal selected_age = \"\"\n\t\t-- Select sex\n\t\tif male_s <= sex_chance and sex_chance <= male_e then\n\t\t\tselected_sex = npc.MALE\n\t\telseif female_s <= sex_chance and sex_chance <= female_e then\n\t\t\tselected_sex = npc.FEMALE\n\t\tend\n\t\t-- Set sex for NPC\n\t\tent.sex = selected_sex\n\t\t-- Select age\n\t\tif adult_s <= age_chance and age_chance <= adult_e then\n\t\t\tselected_age = npc.age.adult\n\t\telseif child_s <= age_chance and age_chance <= child_e then\n\t\t\tselected_age = npc.age.child\n\t\t\tent.visual_size = {\n\t\t\t\tx = 0.65,\n\t\t\t\ty = 0.65\n\t\t\t}\n\t\t\tent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10}\n\t\t\tent.is_child = true\n\t\t\t-- For mobs_redo\n\t\t\tent.child = true\n\t\tend\n\t\t-- Store the selected age\n\t\tent.age = selected_age\n\n\t\t-- Set texture accordingly\n\t\tlocal selected_texture = get_random_texture(selected_sex, selected_age)\n\t\t--minetest.log(\"Selected texture: \"..dump(selected_texture))\n\t\t-- Store selected texture due to the need to restore it later\n\t\tent.selected_texture = selected_texture\n\t\t-- Set texture and base texture\n\t\tent.textures = {selected_texture}\n\t\tent.base_texture = {selected_texture}\n\telse\n\t\t-- Get sex based on texture. This is a 50% chance for\n\t\t-- each sex as there's same amount of textures for male and female.\n\t\t-- Do not spawn child as first NPC\n\t\tent.sex = npc.assign_sex_from_texture(ent)\n\t\tent.age = npc.age.adult\n\tend\n\n\t-- Initialize all gift data\n\tent.gift_data = {\n\t\t-- Choose favorite items. Choose phase1 per default\n\t\tfavorite_items = npc.relationships.select_random_favorite_items(ent.sex, \"phase1\"),\n\t\t-- Choose disliked items. Choose phase1 per default\n\t\tdisliked_items = npc.relationships.select_random_disliked_items(ent.sex),\n\t\t-- Enable/disable gift item hints dialogue lines\n\t\tenable_gift_items_hints = true\n\t}\n\n\t-- Flag that determines if NPC can have a relationship\n\tent.can_have_relationship = can_have_relationships(ent.is_child)\n\n\t--ent.infotext = \"Interested in relationships: \"..dump(ent.can_have_relationship)\n\n\t-- Flag to determine if NPC can receive gifts\n\tent.can_receive_gifts = ent.can_have_relationship\n\n\t-- Initialize relationships object\n\tent.relationships = {}\n\n\t-- Determines if NPC is married or not\n\tent.is_married_to = nil\n\n\t-- Initialize dialogues\n\tent.dialogues = npc.dialogue.select_random_dialogues_for_npc(ent, \"phase1\")\n\n\t-- Declare NPC inventory\n\tent.inventory = initialize_inventory()\n\n\t-- Choose items to spawn with\n\tchoose_spawn_items(ent)\n\n\t-- Flags: generic booleans or functions that help drive functionality\n\tent.flags = {}\n\n\t-- Declare trade data\n\tent.trader_data = {\n\t\t-- Type of trader\n\t\ttrader_status = npc.trade.get_random_trade_status(),\n\t\t-- Current buy offers\n\t\tbuy_offers = {},\n\t\t-- Current sell offers\n\t\tsell_offers = {},\n\t\t-- Items to buy change timer\n\t\tchange_offers_timer = 0,\n\t\t-- Items to buy change timer interval\n\t\tchange_offers_timer_interval = 60,\n\t\t-- Trading list: a list of item names the trader is expected to trade in.\n\t\t-- It is mostly related to its occupation.\n\t\t-- If empty, the NPC will revert to casual trading\n\t\t-- If not, it will try to sell those that it have, and buy the ones it not.\n\t\ttrade_list = {},\n\t\t-- Custom trade allows to specify more than one payment\n\t\t-- and a custom prompt (instead of the usual buy or sell prompts)\n\t\tcustom_trades = {}\n\t}\n\n\t-- Initialize trading offers for NPC\n\t--npc.trade.generate_trade_offers_by_status(ent)\n\t-- if ent.trader_data.trader_status == npc.trade.CASUAL then\n\t-- select_casual_trade_offers(ent)\n\t-- end\n\n\t-- Actions data\n\tent.actions = {\n\t\t-- The queue is a queue of actions to be performed on each interval\n\t\tqueue = {},\n\t\t-- Current value of the action timer\n\t\taction_timer = 0,\n\t\t-- Determines the interval for each action in the action queue\n\t\t-- Default is 1. This can be changed via actions\n\t\taction_interval = npc.actions.default_interval,\n\t\t-- Avoid the execution of the action timer\n\t\taction_timer_lock = false,\n\t\t-- Defines the state of the current action\n\t\tcurrent_action_state = npc.action_state.none,\n\t\t-- Store information about action on state before lock\n\t\tstate_before_lock = {\n\t\t\t-- State of the mobs_redo API\n\t\t\tfreeze = false,\n\t\t\t-- State of execution\n\t\t\taction_state = npc.action_state.none,\n\t\t\t-- Action executed while on lock\n\t\t\tinterrupted_action = {}\n\t\t},\n\t\t-- Walking variables -- required for implementing accurate movement code\n\t\twalking = {\n\t\t\t-- Defines whether NPC is walking to specific position or not\n\t\t\tis_walking = false,\n\t\t\t-- Path that the NPC is following\n\t\t\tpath = {},\n\t\t\t-- Target position the NPC is supposed to walk to in this step. NOTE:\n\t\t\t-- This is NOT the end of the path, but the next position in the path\n\t\t\t-- relative to the last position\n\t\t\ttarget_pos = {}\n\t\t}\n\t}\n\n\t-- This flag is checked on every step. If it is true, the rest of\n\t-- Mobs Redo API is not executed\n\tent.freeze = nil\n\n\t-- This map will hold all the places for the NPC\n\t-- Map entries should be like: \"bed\" = {x=1, y=1, z=1}\n\tent.places_map = {}\n\n\t-- Schedule data\n\tent.schedules = {\n\t\t-- Flag to enable or disable the schedules functionality\n\t\tenabled = true,\n\t\t-- Lock for when executing a schedule\n\t\tlock = false,\n\t\t-- Queue of schedules executed\n\t\t-- Used to calculate dependencies\n\t\ttemp_executed_queue = {},\n\t\t-- An array of schedules, meant to be one per day at some point\n\t\t-- when calendars are implemented. Allows for only 7 schedules,\n\t\t-- one for each day of the week\n\t\tgeneric = {},\n\t\t-- An array of schedules, meant to be for specific dates in the\n\t\t-- year. Can contain as many as possible. The keys will be strings\n\t\t-- in the format MM:DD\n\t\tdate_based = {},\n\t\t-- The following holds the check parameters provided by the\n\t\t-- current schedule\n\t\tcurrent_check_params = {}\n\t}\n\n\t-- If occupation name given, override properties with\n\t-- occupation values and initialize schedules\n\tif occupation_name and occupation_name ~= \"\" and ent.age == npc.age.adult then\n\t\t-- Set occupation name\n\t\tent.occupation_name = occupation_name\n\t\t-- Override relevant values\n\t\tnpc.occupations.initialize_occupation_values(ent, occupation_name)\n\tend\n\n\t-- Nametag is initialized to blank\n\tent.nametag = \"\"\n\n\t-- Set name\n\tent.npc_name = get_random_name(ent.sex)\n\n\t-- Set ID\n\tent.npc_id = tostring(math.random(1000, 9999))..\":\"..ent.npc_name\n\n\t-- TODO: Remove this - do inside occupation\n\t-- Dedicated trade test\n--\tent.trader_data.trade_list = {\n--\t\t[\"default:tree\"] = {},\n--\t\t[\"default:cobble\"] = {},\n--\t\t[\"default:wood\"] = {},\n--\t\t[\"default:diamond\"] = {},\n--\t\t[\"default:mese_crystal\"] = {},\n--\t\t[\"flowers:rose\"] = {},\n--\t\t[\"advanced_npc:marriage_ring\"] = {},\n--\t\t[\"flowers:geranium\"] = {},\n--\t\t[\"mobs:meat\"] = {},\n--\t\t[\"mobs:leather\"] = {},\n--\t\t[\"default:sword_stone\"] = {},\n--\t\t[\"default:shovel_stone\"] = {},\n--\t\t[\"default:axe_stone\"] = {}\n--\t}\n\n\t-- Generate trade offers\n\tnpc.trade.generate_trade_offers_by_status(ent)\n\n\t-- Add a custom trade offer\n\t-- local offer1 = npc.trade.create_custom_sell_trade_offer(\"Do you want me to fix your steel sword?\", \"Fix steel sword\", \"Fix steel sword\", \"default:sword_steel\", {\"default:sword_steel\", \"default:iron_lump 5\"})\n\t-- table.insert(ent.trader_data.custom_trades, offer1)\n\t--local offer2 = npc.trade.create_custom_sell_trade_offer(\"Do you want me to fix your mese sword?\", \"Fix mese sword\", \"Fix mese sword\", \"default:sword_mese\", {\"default:sword_mese\", \"default:copper_lump 10\"})\n\t--table.insert(ent.trader_data.custom_trades, offer2)\n\n\t-- Set initialized flag on\n\tent.initialized = true\n\t--npc.log(\"WARNING\", \"Spawned entity: \"..dump(ent))\n\tnpc.log(\"INFO\", \"Successfully initialized NPC with name \"..dump(ent.npc_name)\n\t\t\t..\", sex: \"..ent.sex..\", is child: \"..dump(ent.is_child)\n\t\t\t..\", texture: \"..dump(ent.textures))\n\t-- Refreshes entity\n\tent.object:set_properties(ent)\nend\n\n---------------------------------------------------------------------------------------\n-- Trading functions\n---------------------------------------------------------------------------------------\nfunction npc.generate_trade_list_from_inventory(self)\n\tlocal list = {}\n\tfor i = 1, #self.inventory do\n\t\tlist[npc.get_item_name(self.inventory[i])] = {}\n\tend\n\tself.trader_data.trade_list = list\nend\n\nfunction npc.set_trading_status(self, status)\n\t--minetest.log(\"Trader_data: \"..dump(self.trader_data))\n\t-- Set status\n\tself.trader_data.trader_status = status\n\t-- Re-generate trade offers\n\tnpc.trade.generate_trade_offers_by_status(self)\nend\n\n---------------------------------------------------------------------------------------\n-- Inventory functions\n---------------------------------------------------------------------------------------\n-- NPCs inventories are restrained to 16 slots.\n-- Each slot can hold one item up to 99 count.\n\n-- Utility function to get item name from a string\nfunction npc.get_item_name(item_string)\n\treturn ItemStack(item_string):get_name()\nend\n\n-- Utility function to get item count from a string\nfunction npc.get_item_count(item_string)\n\treturn ItemStack(item_string):get_count()\nend\n\n-- Add an item to inventory. Returns true if add successful\n-- These function can be used to give items to other NPCs\n-- given that the \"self\" variable can be any NPC\nfunction npc.add_item_to_inventory(self, item_name, count)\n\t-- Check if NPC already has item\n\tlocal existing_item = npc.inventory_contains(self, item_name)\n\tif existing_item ~= nil and existing_item.item_string ~= nil then\n\t\t-- NPC already has item. Get count and see\n\t\tlocal existing_count = npc.get_item_count(existing_item.item_string)\n\t\tif (existing_count + count) < npc.INVENTORY_ITEM_MAX_STACK then\n\t\t\t-- Set item here\n\t\t\tself.inventory[existing_item.slot] =\n\t\t\tnpc.get_item_name(existing_item.item_string)..\" \"..tostring(existing_count + count)\n\t\t\treturn true\n\t\telse\n\t\t\t--Find next free slot\n\t\t\tfor i = 1, #self.inventory do\n\t\t\t\tif self.inventory[i] == \"\" then\n\t\t\t\t\t-- Found slot, set item\n\t\t\t\t\tself.inventory[i] =\n\t\t\t\t\titem_name..\" \"..tostring((existing_count + count) - npc.INVENTORY_ITEM_MAX_STACK)\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\t\t-- No free slot found\n\t\t\treturn false\n\t\tend\n\telse\n\t\t-- Find a free slot\n\t\tfor i = 1, #self.inventory do\n\t\t\tif self.inventory[i] == \"\" then\n\t\t\t\t-- Found slot, set item\n\t\t\t\tself.inventory[i] = item_name..\" \"..tostring(count)\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\t\t-- No empty slot found\n\t\treturn false\n\tend\nend\n\n-- Same add method but with itemstring for convenience\nfunction npc.add_item_to_inventory_itemstring(self, item_string)\n\tlocal item_name = npc.get_item_name(item_string)\n\tlocal item_count = npc.get_item_count(item_string)\n\tnpc.add_item_to_inventory(self, item_name, item_count)\nend\n\n-- Checks if an item is contained in the inventory. Returns\n-- the item string or nil if not found\nfunction npc.inventory_contains(self, item_name)\n\tfor key,value in pairs(self.inventory) do\n\t\tif value ~= \"\" and string.find(value, item_name) then\n\t\t\treturn {slot=key, item_string=value}\n\t\tend\n\tend\n\t-- Item not found\n\treturn nil\nend\n\n-- Removes the item from an NPC inventory and returns the item\n-- with its count (as a string, e.g. \"default:apple 2\"). Returns\n-- nil if unable to get the item.\nfunction npc.take_item_from_inventory(self, item_name, count)\n\tlocal existing_item = npc.inventory_contains(self, item_name)\n\tif existing_item ~= nil then\n\t\t-- Found item\n\t\tlocal existing_count = npc.get_item_count(existing_item.item_string)\n\t\tlocal new_count = existing_count\n\t\tif existing_count - count < 0 then\n\t\t\t-- Remove item first\n\t\t\tself.inventory[existing_item.slot] = \"\"\n\t\t\t-- TODO: Support for retrieving from next stack. Too complicated\n\t\t\t-- and honestly might be unecessary.\n\t\t\treturn item_name..\" \"..tostring(new_count)\n\t\telse\n\t\t\tnew_count = existing_count - count\n\t\t\tif new_count == 0 then\n\t\t\t\tself.inventory[existing_item.slot] = \"\"\n\t\t\telse\n\t\t\t\tself.inventory[existing_item.slot] = item_name..\" \"..new_count\n\t\t\tend\n\t\t\treturn item_name..\" \"..tostring(count)\n\t\tend\n\telse\n\t\t-- Not able to take item because not found\n\t\treturn nil\n\tend\nend\n\n-- Same take method but with itemstring for convenience\nfunction npc.take_item_from_inventory_itemstring(self, item_string)\n\tlocal item_name = npc.get_item_name(item_string)\n\tlocal item_count = npc.get_item_count(item_string)\n\tnpc.take_item_from_inventory(self, item_name, item_count)\nend\n\n---------------------------------------------------------------------------------------\n-- Flag functionality\n---------------------------------------------------------------------------------------\n-- TODO: Consider removing them as they are pretty simple and straight forward.\n-- Generic variables or function that help drive some functionality for the NPC.\nfunction npc.add_flag(self, flag_name, value)\n\tself.flags[flag_name] = value\nend\n\nfunction npc.update_flag(self, flag_name, value)\n\tself.flags[flag_name] = value\nend\n\nfunction npc.get_flag(self, flag_name)\n\treturn self.flags[flag_name]\nend\n\n---------------------------------------------------------------------------------------\n-- Dialogue functionality\n---------------------------------------------------------------------------------------\nfunction npc.start_dialogue(self, clicker, show_married_dialogue)\n\n\t-- Call dialogue function as normal\n\tnpc.dialogue.start_dialogue(self, clicker, show_married_dialogue)\n\n\t-- Check and update relationship if needed\n\tnpc.relationships.dialogue_relationship_update(self, clicker)\n\nend\n\n---------------------------------------------------------------------------------------\n-- Action functionality\n---------------------------------------------------------------------------------------\n-- This function adds a function to the action queue.\n-- Actions should be added in strict order for tasks to work as expected.\nfunction npc.add_action(self, action, arguments)\n\tlocal action_entry = {action=action, args=arguments, is_task=false}\n\ttable.insert(self.actions.queue, action_entry)\nend\n\n-- This function adds task actions in-place, as opposed to\n-- at the end of the queue. This allows for continued order\nfunction npc.add_task(self, task, args)\n\tlocal action_entry = {action=task, args=args, is_task=true}\n\ttable.insert(self.actions.queue, action_entry)\nend\n\n-- This function removes the first action in the action queue\n-- and then executes it\nfunction npc.execute_action(self)\n\tnpc.log(\"DEBUG_ACTION\", \"Current actions queue: \"..dump(self.actions.queue))\n\t-- Check if an action was interrupted\n\tif self.actions.current_action_state == npc.action_state.interrupted then\n\t\tnpc.log(\"DEBUG_ACTION\", \"Re-inserting interrupted action for NPC: '\"..dump(self.npc_name)..\"': \"..dump(self.actions.state_before_lock.interrupted_action))\n\t\t-- Insert into queue the interrupted action\n\t\ttable.insert(self.actions.queue, 1, self.actions.state_before_lock.interrupted_action)\n\t\t-- Clear the action\n\t\tself.actions.state_before_lock.interrupted_action = {}\n\t\t-- Clear the position\n\t\tself.actions.state_before_lock.pos = {}\n\tend\n\tlocal result = nil\n\tif table.getn(self.actions.queue) == 0 then\n\t\t-- Set state to none\n\t\tself.actions.current_action_state = npc.action_state.none\n\t\t-- Keep state the same if there are no more actions in actions queue\n\t\treturn self.freeze\n\tend\n\tlocal action_obj = self.actions.queue[1]\n\t-- Check if action is null\n\tif action_obj.action == nil then\n\t\treturn\n\tend\n\t-- Check if action is an schedule check\n\tif action_obj.action == \"schedule_check\" then\n\t\t-- Remove table entry\n\t\ttable.remove(self.actions.queue, 1)\n\t\t-- Execute schedule check\n\t\tnpc.schedule_check(self)\n\t\t-- Return\n\t\treturn false\n\tend\n\t-- If the entry is a task, then push all this new operations in\n\t-- stack fashion\n\tif action_obj.is_task == true then\n\t\tnpc.log(\"DEBUG_ACTION\", \"Executing task for NPC '\"..dump(self.npc_name)..\"': \"..dump(action_obj))\n\t\t-- Backup current queue\n\t\tlocal backup_queue = self.actions.queue\n\t\t-- Remove this \"task\" action from queue\n\t\ttable.remove(self.actions.queue, 1)\n\t\t-- Clear queue\n\t\tself.actions.queue = {}\n\t\t-- Now, execute the task with its arguments\n\t\tresult = npc.actions.execute(self, action_obj.action, action_obj.args)\n\t\t--result = action_obj.action(self, action_obj.args)\n\t\t-- After all new actions has been added by task, add the previously\n\t\t-- queued actions back\n\t\tfor i = 1, #backup_queue do\n\t\t\ttable.insert(self.actions.queue, backup_queue[i])\n\t\tend\n\telse\n\t\tnpc.log(\"DEBUG_ACTION\", \"Executing action for NPC '\"..dump(self.npc_name)..\"': \"..dump(action_obj))\n\t\t-- Store the action that is being executed\n\t\tself.actions.state_before_lock.interrupted_action = action_obj\n\t\t-- Store current position\n\t\tself.actions.state_before_lock.pos = self.object:getpos()\n\t\t-- Execute action as normal\n\t\tresult = npc.actions.execute(self, action_obj.action, action_obj.args)\n\t\t-- Remove task\n\t\ttable.remove(self.actions.queue, 1)\n\t\t-- Set state\n\t\tself.actions.current_action_state = npc.action_state.executing\n\tend\n\treturn result\nend\n\nfunction npc.lock_actions(self)\n\n\t-- Avoid re-locking if already locked\n\tif self.actions.action_timer_lock == true then\n\t\treturn\n\tend\n\n\tlocal pos = self.object:getpos()\n\n\tif self.freeze == false then\n\t\t-- Round current pos to avoid the NPC being stopped on positions\n\t\t-- where later on can't walk to the correct positions\n\t\t-- Choose which position is to be taken as start position\n\t\tif self.actions.state_before_lock.pos ~= {} then\n\t\t\tpos = vector.round(self.actions.state_before_lock.pos)\n\t\telse\n\t\t\tpos = vector.round(self.object:getpos())\n\t\tend\n\t\tpos.y = self.object:getpos().y\n\tend\n\t-- Stop NPC\n\tnpc.actions.execute(self, npc.actions.cmd.STAND, {pos=pos})\n\t-- Avoid all timer execution\n\tself.actions.action_timer_lock = true\n\t-- Reset timer so that it has some time after interaction is done\n\tself.actions.action_timer = 0\n\t-- Check if there are is an action executing\n\tif self.actions.current_action_state == npc.action_state.executing\n\t\t\tand self.freeze == false then\n\t\t-- Store the current action state\n\t\tself.actions.state_before_lock.action_state = self.actions.current_action_state\n\t\t-- Set current action state to interrupted\n\t\tself.actions.current_action_state = npc.action_state.interrupted\n\tend\n\t-- Store the current freeze variable\n\tself.actions.state_before_lock.freeze = self.freeze\n\t-- Freeze mobs_redo API\n\tself.freeze = false\n\n\tnpc.log(\"DEBUG_ACTION\", \"Locking NPC \"..dump(self.npc_id)..\" actions\")\nend\n\nfunction npc.unlock_actions(self)\n\t-- Allow timers to execute\n\tself.actions.action_timer_lock = false\n\t-- Restore the value of self.freeze\n\tself.freeze = self.actions.state_before_lock.freeze\n\n\tif table.getn(self.actions.queue) == 0 then\n\t\t-- Allow mobs_redo API to execute since action queue is empty\n\t\tself.freeze = true\n\tend\n\n\tnpc.log(\"DEBUG_ACTION\", \"Unlocked NPC \"..dump(self.npc_id)..\" actions\")\nend\n\n---------------------------------------------------------------------------------------\n-- Schedule functionality\n---------------------------------------------------------------------------------------\n-- Schedules allow the NPC to do different things depending on the time of the day.\n-- The time of the day is in 24 hours and is consistent with the Minetest Game\n-- /time command. Hours will be written as numbers: 1 for 1:00, 13 for 13:00 or 1:00 PM\n-- The API is as following: a schedule can be created for a specific date or for a\n-- day of the week. A date is a string in the format MM:DD\nnpc.schedule_types = {\n\t[\"generic\"] = \"generic\",\n\t[\"date_based\"] = \"date_based\"\n}\n\nnpc.schedule_properties = {\n\tput_item = \"put_item\",\n\tput_multiple_items = \"put_multiple_items\",\n\ttake_item = \"take_item\",\n\ttrader_status = \"trader_status\",\n\tcan_receive_gifts = \"can_receive_gifts\",\n\tflag = \"flag\",\n\tenable_gift_items_hints = \"enable_gift_items_hints\",\n\tset_trade_list = \"set_trade_list\"\n}\n\nlocal function get_time_in_hours()\n\treturn minetest.get_timeofday() * 24\nend\n\n-- Create a schedule on a NPC.\n-- Schedule types:\n-- - Generic: Returns nil if there are already\n-- seven schedules, one for each day of the\n-- week or if the schedule attempting to add\n-- already exists. The date parameter is the\n-- day of the week it represents as follows:\n-- - 1: Monday\n-- - 2: Tuesday\n-- - 3: Wednesday\n-- - 4: Thursday\n-- - 5: Friday\n-- - 6: Saturday\n-- - 7: Sunday\n-- - Date-based: The date parameter should be a\n-- string of the format \"MM:DD\". If it already\n-- exists, function retuns nil\nfunction npc.create_schedule(self, schedule_type, date)\n\tif schedule_type == npc.schedule_types.generic then\n\t\t-- Check that there are no more than 7 schedules\n\t\tif #self.schedules.generic == 7 then\n\t\t\t-- Unable to add schedule\n\t\t\treturn nil\n\t\telseif #self.schedules.generic < 7 then\n\t\t\t-- Check schedule doesn't exists already\n\t\t\tif self.schedules.generic[date] == nil then\n\t\t\t\t-- Add schedule\n\t\t\t\tself.schedules.generic[date] = {}\n\t\t\telse\n\t\t\t\t-- Schedule already present\n\t\t\t\treturn nil\n\t\t\tend\n\t\tend\n\telseif schedule_type == npc.schedule_types.date then\n\t\t-- Check schedule doesn't exists already\n\t\tif self.schedules.date_based[date] == nil then\n\t\t\t-- Add schedule\n\t\t\tself.schedules.date_based[date] = {}\n\t\telse\n\t\t\t-- Schedule already present\n\t\t\treturn nil\n\t\tend\n\tend\nend\n\nfunction npc.delete_schedule(self, schedule_type, date)\n\t-- Delete schedule by setting entry to nil\n\tself.schedules[schedule_type][date] = nil\nend\n\n-- Schedule entries API\n-- Allows to add, get, update and delete entries from each\n-- schedule. Attempts to be as safe-fail as possible to avoid crashes.\n\n-- Actions is an array of actions and tasks that the NPC\n-- will perform at the scheduled time on the scheduled date\nfunction npc.add_schedule_entry(self, schedule_type, date, time, check, actions)\n\t-- Check that schedule for date exists\n\tif self.schedules[schedule_type][date] ~= nil then\n\t\t-- Add schedule entry\n\t\tif check == nil then\n\t\t\tself.schedules[schedule_type][date][time] = actions\n\t\telse\n\t\t\tself.schedules[schedule_type][date][time].check = check\n\t\tend\n\telse\n\t\t-- No schedule found, need to be created for date\n\t\treturn nil\n\tend\nend\n\nfunction npc.get_schedule_entry(self, schedule_type, date, time)\n\t-- Check if schedule for date exists\n\tif self.schedules[schedule_type][date] ~= nil then\n\t\t-- Return schedule\n\t\treturn self.schedules[schedule_type][date][time]\n\telse\n\t\t-- Schedule for date not found\n\t\treturn nil\n\tend\nend\n\nfunction npc.update_schedule_entry(self, schedule_type, date, time, check, actions)\n\t-- Check schedule for date exists\n\tif self.schedules[schedule_type][date] ~= nil then\n\t\t-- Check that a schedule entry for that time exists\n\t\tif self.schedules[schedule_type][date][time] ~= nil then\n\t\t\t-- Set the new actions\n\t\t\tif check == nil then\n\t\t\t\tself.schedules[schedule_type][date][time] = actions\n\t\t\telse\n\t\t\t\tself.schedules[schedule_type][date][time].check = check\n\t\t\tend\n\t\telse\n\t\t\t-- Schedule not found for specified time\n\t\t\treturn nil\n\t\tend\n\telse\n\t\t-- Schedule not found for date\n\t\treturn nil\n\tend\nend\n\nfunction npc.delete_schedule_entry(self, schedule_type, date, time)\n\t-- Check schedule for date exists\n\tif self.schedules[schedule_type][date] ~= nil then\n\t\t-- Remove schedule entry by setting to nil\n\t\tself.schedules[schedule_type][date][time] = nil\n\telse\n\t\t-- Schedule not found for date\n\t\treturn nil\n\tend\nend\n\nfunction npc.schedule_change_property(self, property, args)\n\tif property == npc.schedule_properties.trader_status then\n\t\t-- Get status from args\n\t\tlocal status = args.status\n\t\t-- Set status to NPC\n\t\tnpc.set_trading_status(self, status)\n\telseif property == npc.schedule_properties.put_item then\n\t\tlocal itemstring = args.itemstring\n\t\t-- Add item\n\t\tnpc.add_item_to_inventory_itemstring(self, itemstring)\n\telseif property == npc.schedule_properties.put_multiple_items then\n\t\tlocal itemlist = args.itemlist\n\t\tfor i = 1, #itemlist do\n\t\t\tlocal itemlist_entry = itemlist[i]\n\t\t\tlocal current_itemstring = itemlist[i].name\n\t\t\tif itemlist_entry.random == true then\n\t\t\t\tcurrent_itemstring = current_itemstring\n\t\t\t\t\t\t..\" \"..dump(math.random(itemlist_entry.min, itemlist_entry.max))\n\t\t\telse\n\t\t\t\tcurrent_itemstring = current_itemstring..\" \"..tostring(itemlist_entry.count)\n\t\t\tend\n\t\t\t-- Add item to inventory\n\t\t\tnpc.add_item_to_inventory_itemstring(self, current_itemstring)\n\t\tend\n\telseif property == npc.schedule_properties.take_item then\n\t\tlocal itemstring = args.itemstring\n\t\t-- Add item\n\t\tnpc.take_item_from_inventory_itemstring(self, itemstring)\n\telseif property == npc.schedule_properties.can_receive_gifts then\n\t\tlocal value = args.can_receive_gifts\n\t\t-- Set status\n\t\tself.can_receive_gifts = value\n\telseif property == npc.schedule_properties.flag then\n\t\tlocal action = args.action\n\t\tif action == \"set\" then\n\t\t\t-- Adds or overwrites an existing flag and sets it to the given value\n\t\t\tself.flags[args.flag_name] = args.flag_value\n\t\telseif action == \"reset\" then\n\t\t\t-- Sets value of flag to false or to 0\n\t\t\tlocal flag_type = type(self.flags[args.flag_name])\n\t\t\tif flag_type == \"number\" then\n\t\t\t\tself.flags[args.flag_name] = 0\n\t\t\telseif flag_type == \"boolean\" then\n\t\t\t\tself.flags[args.flag_name] = false\n\t\t\tend\n\t\tend\n\telseif property == npc.schedule_properties.enable_gift_item_hints then\n\t\tself.gift_data.enable_gift_items_hints = args.value\n\telseif property == npc.schedule_properties.set_trade_list then\n\t\t-- Insert items\n\t\tfor i = 1, #args.items do\n\t\t\t-- Insert entry into trade list\n\t\t\tself.trader_data.trade_list[args.items[i].name] = {\n\t\t\t\tmax_item_buy_count = args.items[i].buy,\n\t\t\t\tmax_item_sell_count = args.items[i].sell,\n\t\t\t\tamount_to_keep = args.items[i].keep\n\t\t\t}\n\n\t\tend\n \tend\nend\n\nfunction npc.add_schedule_check(self)\n\ttable.insert(self.actions.queue, {action=\"schedule_check\", args={}, is_task=false})\nend\n\nfunction npc.enqueue_schedule_action(self, entry)\n\tif entry.task ~= nil then\n\t\t-- Add task\n\t\tnpc.add_task(self, entry.task, entry.args)\n\telseif entry.action ~= nil then\n\t\t-- Add action\n\t\tnpc.add_action(self, entry.action, entry.args)\n\telseif entry.property ~= nil then\n\t\t-- Change NPC property\n\t\tnpc.schedule_change_property(self, entry.property, entry.args)\n\tend\nend\n\n-- Range: integer, radius in which nodes will be searched. Recommended radius is\n--\t\t between 1-3\n-- Nodes: array of node names\n-- Actions: map of node names to entries {action=, args={}}.\n--\t\t\tArguments can be empty - the check function will try to determine most\n--\t\t\targuments anyways (like pos and dir).\n--\t\t\tSpecial node \"any\" will execute those actions on any node except the\n--\t\t\talready specified ones.\n-- None-action: array of entries {action=, args={}}.\n--\t\t\t\tWill be executed when no node is found.\nfunction npc.schedule_check(self)\n\tnpc.log(\"DEBUG_SCHEDULE\", \"Prev Actions queue: \"..dump(self.actions.queue))\n\tlocal range = self.schedules.current_check_params.range\n\tlocal walkable_nodes = self.schedules.current_check_params.walkable_nodes\n\tlocal nodes = self.schedules.current_check_params.nodes\n\tlocal actions = self.schedules.current_check_params.actions\n\tlocal none_actions = self.schedules.current_check_params.none_actions\n\t-- Get NPC position\n\tlocal start_pos = self.object:getpos()\n\t-- Search nodes\n\tlocal found_nodes = npc.places.find_node_nearby(start_pos, nodes, range)\n\t-- Check if any node was found\n\tnpc.log(\"DEBUG_SCHEDULE\", \"Found nodes using radius: \"..dump(found_nodes))\n\tif found_nodes and #found_nodes > 0 then\n\t\tlocal node_pos\n\t\tlocal node\n\t\t-- Check if there is preference to act on nodes already acted upon\n\t\tif self.schedules.current_check_params.prefer_last_acted_upon_node == true then\n\t\t\t-- Find a node other than the acted upon - try 3 times\n\t\t\tfor i = 1, #found_nodes do\n\t\t\t\tnode_pos = found_nodes[i]\n\t\t\t\t-- Get node info\n\t\t\t\tnode = minetest.get_node(node_pos)\n\t\t\t\tif node.name == self.schedules.current_check_params.last_node_acted_upon then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- Pick a random node to act upon\n\t\t\tnode_pos = found_nodes[math.random(1, #found_nodes)]\n\t\t\t-- Get node info\n\t\t\tnode = minetest.get_node(node_pos)\n\t\tend\n\t\t-- Save this node as the last acted upon\n\t\tself.schedules.current_check_params.last_node_acted_upon = node.name\n\t\t-- Set node as a place\n\t\t-- Note: Code below isn't *adding* a node, but overwriting the\n\t\t-- place with \"schedule_target_pos\" place type\n\t\tnpc.log(\"DEBUG_SCHEDULE\", \"Found \"..dump(node.name)..\" at pos: \"..minetest.pos_to_string(node_pos))\n\t\tnpc.places.add_shared_accessible_place(\n\t\t\tself, {owner=\"\", node_pos=node_pos}, npc.places.PLACE_TYPE.SCHEDULE.TARGET, true, walkable_nodes)\n\t\t-- Get actions related to node and enqueue them\n\t\tfor i = 1, #actions[node.name] do\n\t\t\tlocal args = {}\n\t\t\tlocal action\n\t\t\t-- Calculate arguments for the following supported actions:\n\t\t\t-- - Dig\n\t\t\t-- - Place\n\t\t\t-- - Walk step\n\t\t\t-- - Walk to position\n\t\t\t-- - Use furnace\n\t\t\tif actions[node.name][i].action == npc.actions.cmd.DIG then\n\t\t\t\t-- Defaults: items will be added to inventory if not specified\n\t\t\t\t-- otherwise, and protection will be respected, if not specified\n\t\t\t\t-- otherwise\n\t\t\t\targs = {\n\t\t\t\t\tpos = node_pos,\n\t\t\t\t\tadd_to_inventory = actions[node.name][i].args.add_to_inventory or true,\n\t\t\t\t\tbypass_protection = actions[node.name][i].args.bypass_protection or false\n\t\t\t\t}\n\t\t\t\tnpc.add_action(self, actions[node.name][i].action, args)\n\t\t\telseif actions[node.name][i].action == npc.actions.cmd.PLACE then\n\t\t\t\t-- Position: providing node_pos is because the currently planned\n\t\t\t\t-- behavior for placing nodes is replacing digged nodes. A NPC farmer,\n\t\t\t\t-- for instance, might dig a plant node and plant another one on the\n\t\t\t\t-- same position.\n\t\t\t\t-- Defaults: items will be taken from inventory if existing,\n\t\t\t\t-- if not will be force-placed (item comes from thin air)\n\t\t\t\t-- Protection will be respected\n\t\t\t\targs = {\n\t\t\t\t\tpos = actions[node.name][i].args.pos or node_pos,\n\t\t\t\t\tsource = actions[node.name][i].args.source or npc.actions.take_from_inventory_forced,\n\t\t\t\t\tnode = actions[node.name][i].args.node,\n\t\t\t\t\tbypass_protection = actions[node.name][i].args.bypass_protection or false\n\t\t\t\t}\n\t\t\t\t--minetest.log(\"Enqueue dig action with args: \"..dump(args))\n\t\t\t\tnpc.add_action(self, actions[node.name][i].action, args)\n\t\t\telseif actions[node.name][i].action == npc.actions.cmd.ROTATE then\n\t\t\t\t-- Set arguments\n\t\t\t\targs = {\n\t\t\t\t\tdir = actions[node.name][i].dir,\n\t\t\t\t\tstart_pos = actions[node.name][i].start_pos\n\t\t\t\t\t\t\tor {x=start_pos.x, y=node_pos.y, z=start_pos.z},\n\t\t\t\t\tend_pos = actions[node.name][i].end_pos or node_pos\n\t\t\t\t}\n\t\t\t\t-- Enqueue action\n\t\t\t\tnpc.add_action(self, actions[node.name][i].action, args)\n\t\t\telseif actions[node.name][i].action == npc.actions.cmd.WALK_STEP then\n\t\t\t\t-- Defaults: direction is calculated from start node to node_pos.\n\t\t\t\t-- Speed is default wandering speed. Target pos is node_pos\n\t\t\t\t-- Calculate dir if dir is random\n\t\t\t\tlocal dir = npc.actions.get_direction(start_pos, node_pos)\n\t\t\t\tminetest.log(\"actions: \"..dump(actions[node.name][i]))\n\t\t\t\tif actions[node.name][i].args.dir == \"random\" then\n\t\t\t\t\tdir = math.random(0,7)\n\t\t\t\telseif type(actions[node.name][i].args.dir) == \"number\" then\n\t\t\t\t\tdir = actions[node.name][i].args.dir\n\t\t\t\tend\n\t\t\t\targs = {\n\t\t\t\t\tdir = dir,\n\t\t\t\t\tspeed = actions[node.name][i].args.speed or npc.actions.one_nps_speed,\n\t\t\t\t\ttarget_pos = actions[node.name][i].args.target_pos or node_pos\n\t\t\t\t}\n\t\t\t\tnpc.add_action(self, actions[node.name][i].action, args)\n\t\t\telseif actions[node.name][i].task == npc.actions.cmd.WALK_TO_POS then\n\t\t\t\t-- Optimize walking -- since distances can be really short,\n\t\t\t\t-- a simple walk_step() action can do most of the times. For\n\t\t\t\t-- this, however, we need to calculate direction\n\t\t\t\t-- First of all, check distance\n\t\t\t\tlocal distance = vector.distance(start_pos, node_pos)\n\t\t\t\tif distance < 3 then\n\t\t\t\t\t-- Will do walk_step based instead\n\t\t\t\t\tif distance > 1 then\n\t\t\t\t\t\targs = {\n\t\t\t\t\t\t\tdir = npc.actions.get_direction(start_pos, node_pos),\n\t\t\t\t\t\t\tspeed = npc.actions.one_nps_speed\n\t\t\t\t\t\t}\n\t\t\t\t\t\t-- Enqueue walk step\n\t\t\t\t\t\tnpc.add_action(self, npc.actions.cmd.WALK_STEP, args)\n\t\t\t\t\tend\n\t\t\t\t\t-- Add standing action to look at node\n\t\t\t\t\tnpc.add_action(self, npc.actions.cmd.STAND,\n\t\t\t\t\t\t{dir = npc.actions.get_direction(self.object:getpos(), node_pos)}\n\t\t\t\t\t)\n\t\t\t\telse\n\t\t\t\t\t-- Set end pos to be node_pos\n\t\t\t\t\targs = {\n\t\t\t\t\t\tend_pos = actions[node.name][i].args.end_pos or node_pos,\n\t\t\t\t\t\twalkable = actions[node.name][i].args.walkable or walkable_nodes or {}\n\t\t\t\t\t}\n\t\t\t\t\t-- Enqueue\n\t\t\t\t\tnpc.add_task(self, actions[node.name][i].task, args)\n\t\t\t\tend\n\t\t\telseif actions[node.name][i].task == npc.actions.cmd.USE_FURNACE then\n\t\t\t\t-- Defaults: pos is node_pos. Freeze is true\n\t\t\t\targs = {\n\t\t\t\t\tpos = actions[node.name][i].args.pos or node_pos,\n\t\t\t\t\titem = actions[node.name][i].args.item,\n\t\t\t\t\tfreeze = actions[node.name][i].args.freeze or true\n\t\t\t\t}\n\t\t\t\tnpc.add_task(self, actions[node.name][i].task, args)\n\t\t\telse\n\t\t\t\t-- Action or task that is not supported for value calculation\n\t\t\t\tnpc.enqueue_schedule_action(self, actions[node.name][i])\n\t\t\tend\n\t\tend\n\t\t-- Increase execution count\n\t\tself.schedules.current_check_params.execution_count =\n\t\t\tself.schedules.current_check_params.execution_count + 1\n\t\t-- Enqueue next schedule check\n\t\tif self.schedules.current_check_params.execution_count\n\t\t\t\t< self.schedules.current_check_params.execution_times then\n\t\t\tnpc.add_schedule_check(self)\n\t\tend\n\t\tnpc.log(\"DEBUG_SCHEDULE\", \"Actions queue: \"..dump(self.actions.queue))\n\telse\n\t\t-- No nodes found, enqueue none_actions\n\t\tfor i = 1, #none_actions do\n\t\t\t-- Add start_pos to none_actions\n\t\t\tnone_actions[i].args[\"start_pos\"] = start_pos\n\t\t\t-- Enqueue actions\n\t\t\tnpc.add_action(self, none_actions[i].action, none_actions[i].args)\n\t\tend\n\t\t-- Increase execution count\n\t\tself.schedules.current_check_params.execution_count =\n\t\t\tself.schedules.current_check_params.execution_count + 1\n\t\t-- Enqueue next schedule check\n\t\tif self.schedules.current_check_params.execution_count\n\t\t\t\t< self.schedules.current_check_params.execution_times then\n\t\t\tnpc.add_schedule_check(self)\n\t\tend\n\t\t-- No nodes found\n\t\tnpc.log(\"DEBUG_SCHEDULE\", \"Actions queue: \"..dump(self.actions.queue))\n\tend\nend\n\n---------------------------------------------------------------------------------------\n-- NPC Definition\n---------------------------------------------------------------------------------------\nmobs:register_mob(\"advanced_npc:npc\", {\n\ttype = \"npc\",\n\tpassive = false,\n\tdamage = 3,\n\tattack_type = \"dogfight\",\n\tattacks_monsters = true,\n\t-- Added group attack\n\tgroup_attack = true,\n\t-- Pathfinder = 2 to make NPCs more smart when attacking\n\tpathfinding = 2,\n\thp_min = 10,\n\thp_max = 20,\n\tarmor = 100,\n\tcollisionbox = {-0.20,0,-0.20, 0.20,1.8,0.20},\n\t--collisionbox = {-0.20,-1.0,-0.20, 0.20,0.8,0.20},\n\t--collisionbox = {-0.35,-1.0,-0.35, 0.35,0.8,0.35},\n\tvisual = \"mesh\",\n\tmesh = \"character.b3d\",\n\tdrawtype = \"front\",\n\ttextures = {\n\t\t{\"npc_male1.png\"},\n\t\t{\"npc_male2.png\"},\n\t\t{\"npc_male3.png\"},\n\t\t{\"npc_male4.png\"},\n\t\t{\"npc_male5.png\"},\n\t\t{\"npc_male6.png\"},\n\t\t{\"npc_male7.png\"},\n\t\t{\"npc_male8.png\"},\n\t\t{\"npc_male9.png\"},\n\t\t{\"npc_male10.png\"},\n\t\t{\"npc_male11.png\"},\n\t\t{\"npc_male12.png\"},\n\t\t{\"npc_male13.png\"},\n\t\t{\"npc_male14.png\"},\n\t\t{\"npc_male15.png\"},\n\t\t{\"npc_female1.png\"}, -- female by nuttmeg20\n\t\t{\"npc_female2.png\"},\n\t\t{\"npc_female3.png\"},\n\t\t{\"npc_female4.png\"},\n\t\t{\"npc_female5.png\"},\n\t\t{\"npc_female6.png\"},\n\t\t{\"npc_female7.png\"},\n\t\t{\"npc_female8.png\"},\n\t\t{\"npc_female9.png\"},\n\t\t{\"npc_female10.png\"},\n\t\t{\"npc_female11.png\"},\n\t},\n\tchild_texture = {\n\t\t{\"npc_child_male1.png\"},\n\t\t{\"npc_child_female1.png\"},\n\t},\n\tmakes_footstep_sound = true,\n\tsounds = {},\n\t-- Added walk chance\n\twalk_chance = 30,\n\t-- Added stepheight\n\tstepheight = 0.6,\n\twalk_velocity = 1,\n\trun_velocity = 3,\n\tjump = false,\n\tdrops = {\n\t\t{name = \"default:wood\", chance = 1, min = 1, max = 3},\n\t\t{name = \"default:apple\", chance = 2, min = 1, max = 2},\n\t\t{name = \"default:axe_stone\", chance = 5, min = 1, max = 1},\n\t},\n\twater_damage = 0,\n\tlava_damage = 2,\n\tlight_damage = 0,\n\t--follow = {\"farming:bread\", \"mobs:meat\", \"default:diamond\"},\n\tview_range = 15,\n\towner = \"\",\n\torder = \"follow\",\n\t--order = \"stand\",\n\tfear_height = 3,\n\tanimation = {\n\t\tspeed_normal = 30,\n\t\tspeed_run = 30,\n\t\tstand_start = 0,\n\t\tstand_end = 79,\n\t\twalk_start = 168,\n\t\twalk_end = 187,\n\t\trun_start = 168,\n\t\trun_end = 187,\n\t\tpunch_start = 200,\n\t\tpunch_end = 219,\n\t},\n\ton_rightclick = function(self, clicker)\n\n\t\t-- Rotate NPC toward its clicker\n\t\tnpc.dialogue.rotate_npc_to_player(self)\n\n\t\t-- Get information from clicker\n\t\tlocal item = clicker:get_wielded_item()\n\t\tlocal name = clicker:get_player_name()\n\n\t\tnpc.log(\"DEBUG\", \"Right-clicked NPC: \"..dump(self))\n\n\t\t-- Receive gift or start chat. If player has no item in hand\n\t\t-- then it is going to start chat directly\n\t\t--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()))\n\t\tif self.can_have_relationship\n\t\t\t\tand self.can_receive_gifts\n\t\t\t\tand item:to_table() ~= nil then\n\t\t\t-- Get item name\n\t\t\tlocal item = minetest.registered_items[item:get_name()]\n\t\t\tlocal item_name = item.description\n\n\t\t\t-- Show dialogue to confirm that player is giving item as gift\n\t\t\tnpc.dialogue.show_yes_no_dialogue(\n\t\t\t\tself,\n\t\t\t\t\"Do you want to give \"..item_name..\" to \"..self.npc_name..\"?\",\n\t\t\t\tnpc.dialogue.POSITIVE_GIFT_ANSWER_PREFIX..item_name,\n\t\t\t\tfunction()\n\t\t\t\t\tnpc.relationships.receive_gift(self, clicker)\n\t\t\t\tend,\n\t\t\t\tnpc.dialogue.NEGATIVE_ANSWER_LABEL,\n\t\t\t\tfunction()\n\t\t\t\t\tnpc.start_dialogue(self, clicker, true)\n\t\t\t\tend,\n\t\t\t\tname\n\t\t\t)\n\t\telse\n\t\t\tnpc.start_dialogue(self, clicker, true)\n\t\tend\n\tend,\n\tdo_custom = function(self, dtime)\n\t\tif self.initialized == nil then\n\t\t\t-- Initialize NPC if spawned using the spawn egg built in from\n\t\t\t-- mobs_redo. This functionality will be removed in the future in\n\t\t\t-- favor of a better manual spawning method with customization\n\t\t\tnpc.log(\"WARNING\", \"Initializing NPC from entity step. This message should only be appearing if an NPC is being spawned from inventory with egg!\")\n\t\t\tnpc.initialize(self, self.object:getpos(), true)\n\t\t\tself.tamed = false\n\t\t\tself.owner = nil\n\t\telse\n\t\t\t-- NPC is initialized, check other variables\n\t\t\t-- Check child texture issues\n\t\t\tif self.is_child then\n\t\t\t\t-- Check texture\n\t\t\t\tnpc.texture_check.timer = npc.texture_check.timer + dtime\n\t\t\t\tif npc.texture_check.timer > npc.texture_check.interval then\n\t\t\t\t\t-- Reset timer\n\t\t\t\t\tnpc.texture_check.timer = 0\n\t\t\t\t\t-- Set hornytimer to zero every 60 seconds so that children\n\t\t\t\t\t-- don't grow automatically\n\t\t\t\t\tself.hornytimer = 0\n\t\t\t\t\t-- Set correct textures\n\t\t\t\t\tself.texture = {self.selected_texture}\n\t\t\t\t\tself.base_texture = {self.selected_texture}\n\t\t\t\t\tself.object:set_properties(self)\n\t\t\t\t\tnpc.log(\"WARNING\", \"Corrected textures on NPC child \"..dump(self.npc_name))\n\t\t\t\t\t-- Set interval to large interval so this code isn't called frequently\n\t\t\t\t\tnpc.texture_check.interval = 60\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- Timer function for casual traders to reset their trade offers\n\t\tself.trader_data.change_offers_timer = self.trader_data.change_offers_timer + dtime\n\t\t-- Check if time has come to change offers\n\t\tif self.trader_data.trader_status == npc.trade.CASUAL and\n\t\t\t\tself.trader_data.change_offers_timer >= self.trader_data.change_offers_timer_interval then\n\t\t\t-- Reset timer\n\t\t\tself.trader_data.change_offers_timer = 0\n\t\t\t-- Re-select casual trade offers\n\t\t\tnpc.trade.generate_trade_offers_by_status(self)\n\t\tend\n\n\t\t-- Timer function for gifts\n\t\tfor i = 1, #self.relationships do\n\t\t\tlocal relationship = self.relationships[i]\n\t\t\t-- Gift timer check\n\t\t\tif relationship.gift_timer_value < relationship.gift_interval then\n\t\t\t\trelationship.gift_timer_value = relationship.gift_timer_value + dtime\n\t\t\telseif relationship.talk_timer_value < relationship.gift_interval then\n\t\t\t\t-- Relationship talk timer - only allows players to increase relationship\n\t\t\t\t-- by talking on the same intervals as gifts\n\t\t\t\trelationship.talk_timer_value = relationship.talk_timer_value + dtime\n\t\t\telse\n\t\t\t\t-- Relationship decrease timer\n\t\t\t\tif relationship.relationship_decrease_timer_value\n\t\t\t\t\t\t< relationship.relationship_decrease_interval then\n\t\t\t\t\trelationship.relationship_decrease_timer_value =\n\t\t\t\t\trelationship.relationship_decrease_timer_value + dtime\n\t\t\t\telse\n\t\t\t\t\t-- Check if married to decrease half\n\t\t\t\t\tif relationship.phase == \"phase6\" then\n\t\t\t\t\t\t-- Avoid going below the marriage phase limit\n\t\t\t\t\t\tif (relationship.points - 0.5) >=\n\t\t\t\t\t\t\t\tnpc.relationships.RELATIONSHIP_PHASE[\"phase5\"].limit then\n\t\t\t\t\t\t\trelationship.points = relationship.points - 0.5\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\trelationship.points = relationship.points - 1\n\t\t\t\t\tend\n\t\t\t\t\trelationship.relationship_decrease_timer_value = 0\n\t\t\t\t\t--minetest.log(dump(self))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- Action queue timer\n\t\t-- Check if actions and timers aren't locked\n\t\tif self.actions.action_timer_lock == false then\n\t\t\t-- Increment action timer\n\t\t\tself.actions.action_timer = self.actions.action_timer + dtime\n\t\t\tif self.actions.action_timer >= self.actions.action_interval then\n\t\t\t\t-- Reset action timer\n\t\t\t\tself.actions.action_timer = 0\n\t\t\t\t-- Check if NPC is walking\n\t\t\t\tif self.actions.walking.is_walking == true then\n\t\t\t\t\t-- Move NPC to expected position to ensure not getting lost\n\t\t\t\t\tlocal pos = self.actions.walking.target_pos\n\t\t\t\t\tself.object:moveto({x=pos.x, y=pos.y, z=pos.z})\n\t\t\t\tend\n\t\t\t\t-- Execute action\n\t\t\t\tself.freeze = npc.execute_action(self)\n\t\t\t\t-- Check if there are still remaining actions in the queue\n\t\t\t\tif self.freeze == nil and table.getn(self.actions.queue) > 0 then\n\t\t\t\t\tself.freeze = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- Schedule timer\n\t\t-- Check if schedules are enabled\n\t\tif self.schedules.enabled == true then\n\t\t\t-- Get time of day\n\t\t\tlocal time = get_time_in_hours()\n\t\t\t-- Check if time is an hour\n\t\t\tif ((time % 1) < dtime) and self.schedules.lock == false then\n\t\t\t\t-- Activate lock to avoid more than one entry to this code\n\t\t\t\tself.schedules.lock = true\n\t\t\t\t-- Get integer part of time\n\t\t\t\ttime = (time) - (time % 1)\n\t\t\t\t-- Check if there is a schedule entry for this time\n\t\t\t\t-- Note: Currently only one schedule is supported, for day 0\n\t\t\t\tnpc.log(\"DEBUG_SCHEDULE\", \"Time: \"..dump(time))\n\t\t\t\tlocal schedule = self.schedules.generic[0]\n\t\t\t\tif schedule ~= nil then\n\t\t\t\t\t-- Check if schedule for this time exists\n\t\t\t\t\tif schedule[time] ~= nil then\n\t\t\t\t\t\tnpc.log(\"DEBUG_SCHEDULE\", \"Adding actions to action queue\")\n\t\t\t\t\t\t-- Add to action queue all actions on schedule\n\t\t\t\t\t\tfor i = 1, #schedule[time] do\n\t\t\t\t\t\t\t-- Check if schedule has a check function\n\t\t\t\t\t\t\tif schedule[time][i].check then\n\t\t\t\t\t\t\t\t-- Add parameters for check function and run for first time\n\t\t\t\t\t\t\t\t\tnpc.log(\"DEBUG\", \"NPC \"..dump(self.npc_id)..\" is starting check on \"..minetest.pos_to_string(self.object:getpos()))\n\t\t\t\t\t\t\t\tlocal check_params = schedule[time][i]\n\t\t\t\t\t\t\t\t-- Calculates how many times check will be executed\n\t\t\t\t\t\t\t\tlocal execution_times = check_params.count\n\t\t\t\t\t\t\t\tif check_params.random_execution_times then\n\t\t\t\t\t\t\t\t\texecution_times = math.random(check_params.min_count, check_params.max_count)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t-- Set current parameters\n\t\t\t\t\t\t\t\tself.schedules.current_check_params = {\n\t\t\t\t\t\t\t\t\trange = check_params.range,\n\t\t\t\t\t\t\t\t\twalkable_nodes = check_params.walkable_nodes,\n\t\t\t\t\t\t\t\t\tnodes = check_params.nodes,\n\t\t\t\t\t\t\t\t\tactions = check_params.actions,\n\t\t\t\t\t\t\t\t\tnone_actions = check_params.none_actions,\n\t\t\t\t\t\t\t\t\tprefer_last_acted_upon_node = check_params.prefer_last_acted_upon_node or false,\n\t\t\t\t\t\t\t\t\tlast_node_acted_upon = \"\",\n\t\t\t\t\t\t\t\t\texecution_count = 0,\n\t\t\t\t\t\t\t\t\texecution_times = execution_times\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t-- Enqueue the schedule check\n\t\t\t\t\t\t\t\tnpc.add_schedule_check(self)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tnpc.log(\"DEBUG_SCHEDULE\", \"Executing schedule entry for NPC \"..dump(self.npc_id)..\": \"\n\t\t\t\t\t\t\t\t\t\t..dump(schedule[time][i]))\n\t\t\t\t\t\t\t\t-- Run usual schedule entry\n\t\t\t\t\t\t\t\t-- Check chance\n\t\t\t\t\t\t\t\tlocal execution_chance = math.random(1, 100)\n\t\t\t\t\t\t\t\tif not schedule[time][i].chance or\n\t\t\t\t\t\t\t\t\t\t(schedule[time][i].chance and execution_chance <= schedule[time][i].chance) then\n\t\t\t\t\t\t\t\t\t-- Check if entry has dependency on other entry\n\t\t\t\t\t\t\t\t\tlocal dependencies_met = nil\n\t\t\t\t\t\t\t\t\tif schedule[time][i].depends then\n\t\t\t\t\t\t\t\t\t\tdependencies_met = npc.utils.array_is_subset_of_array(\n\t\t\t\t\t\t\t\t\t\t\tself.schedules.temp_executed_queue,\n\t\t\t\t\t\t\t\t\t\t\tschedule[time][i].depends)\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t-- Check for dependencies being met\n\t\t\t\t\t\t\t\t\tif dependencies_met == nil or dependencies_met == true then\n\t\t\t\t\t\t\t\t\t\t-- Add tasks\n\t\t\t\t\t\t\t\t\t\tif schedule[time][i].task ~= nil then\n\t\t\t\t\t\t\t\t\t\t\t-- Add task\n\t\t\t\t\t\t\t\t\t\t\tnpc.add_task(self, schedule[time][i].task, schedule[time][i].args)\n\t\t\t\t\t\t\t\t\t\telseif schedule[time][i].action ~= nil then\n\t\t\t\t\t\t\t\t\t\t\t-- Add action\n\t\t\t\t\t\t\t\t\t\t\tnpc.add_action(self, schedule[time][i].action, schedule[time][i].args)\n\t\t\t\t\t\t\t\t\t\telseif schedule[time][i].property ~= nil then\n\t\t\t\t\t\t\t\t\t\t\t-- Change NPC property\n\t\t\t\t\t\t\t\t\t\t\tnpc.schedule_change_property(self, schedule[time][i].property, schedule[time][i].args)\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t-- Backward compatibility check\n\t\t\t\t\t\t\t\t\t\tif self.schedules.temp_executed_queue then\n\t\t\t\t\t\t\t\t\t\t\t-- Add into execution queue to meet dependency\n\t\t\t\t\t\t\t\t\t\t\ttable.insert(self.schedules.temp_executed_queue, i)\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t-- TODO: Change to debug\n\t\t\t\t\t\t\t\t\tnpc.log(\"DEBUG\", \"Skipping schedule entry for time \"..dump(time)..\": \"..dump(schedule[time][i]))\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\t-- Clear execution queue\n\t\t\t\t\t\tself.schedules.temp_executed_queue = {}\n\t\t\t\t\t\tnpc.log(\"DEBUG\", \"New action queue: \"..dump(self.actions.queue))\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- Check if lock can be released\n\t\t\t\tif (time % 1) > dtime + 0.1 then\n\t\t\t\t\t-- Release lock\n\t\t\t\t\tself.schedules.lock = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn self.freeze\n\tend\n})\n\n-- Spawn\n-- mobs:spawn({\n-- name = \"advanced_npc:npc\",\n-- nodes = {\"advanced_npc:plotmarker_auto_spawner\", \"mg_villages:plotmarker\"},\n-- min_light = 3,\n-- active_object_count = 1,\n-- interval = 5,\n-- chance = 1,\n-- --max_height = 0,\n-- on_spawn = npc.initialize\n-- })\n\n-------------------------------------------------------------------------\n-- Item definitions\n-------------------------------------------------------------------------\n\nmobs:register_egg(\"advanced_npc:npc\", S(\"NPC\"), \"default_brick.png\", 1)\n\n-- compatibility\nmobs:alias_mob(\"mobs:npc\", \"advanced_npc:npc\")\n\n-- Marriage ring\nminetest.register_craftitem(\"advanced_npc:marriage_ring\", {\n\tdescription = S(\"Marriage Ring\"),\n\tinventory_image = \"marriage_ring.png\",\n})\n\n-- Marriage ring craft recipe\nminetest.register_craft({\n\toutput = \"advanced_npc:marriage_ring\",\n\trecipe = { {\"\", \"\", \"\"},\n\t\t{\"\", \"default:diamond\", \"\"},\n\t\t{\"\", \"default:gold_ingot\", \"\"} },\n})\n
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+--- npc.lua (revision d95c8504ec181c2bba72a11fb7230eae284e6610)
++++ npc.lua (revision )
+@@ -454,6 +454,13 @@
+ -- Action executed while on lock
+ interrupted_action = {}
+ },
++ -- Variables that allows preserving the movement state and NPC animation
++ move_state = {
++ -- Whether a NPC is sitted or not
++ is_sitting = false,
++ -- Whether a NPC is laying or not
++ is_laying = false
++ },
+ -- Walking variables -- required for implementing accurate movement code
+ walking = {
+ -- Defines whether NPC is walking to specific position or not
+@@ -464,7 +471,7 @@
+ -- This is NOT the end of the path, but the next position in the path
+ -- relative to the last position
+ target_pos = {}
+- }
++ },
+ }
+
+ -- This flag is checked on every step. If it is true, the rest of
+@@ -806,6 +813,12 @@
+ if self.actions.action_timer_lock == true then
+ return
+ end
++ -- Check if NPC is in unmovable state
++ if self.actions.move_state
++ and (self.actions.move_state.is_sitting == true or self.actions.move_state.is_laying == true) then
++ -- Can't lock actions since NPC is in a non-movable state
++ return
++ end
+
+ local pos = self.object:getpos()
+
+@@ -843,6 +856,12 @@
+ end
+
+ function npc.unlock_actions(self)
++ -- Check if the NPC is sitting or laying states
++ if self.actions.move_state
++ and (self.actions.move_state.is_sitting == true or self.actions.move_state.is_laying == true) then
++ -- Can't unlock actions since NPC is in a non-movable state
++ return
++ end
+ -- Allow timers to execute
+ self.actions.action_timer_lock = false
+ -- Restore the value of self.freeze
+@@ -1320,7 +1339,7 @@
+ makes_footstep_sound = true,
+ sounds = {},
+ -- Added walk chance
+- walk_chance = 30,
++ walk_chance = 20,
+ -- Added stepheight
+ stepheight = 0.6,
+ walk_velocity = 1,
+@@ -1401,6 +1420,16 @@
+ self.tamed = false
+ self.owner = nil
+ else
++
++ -- Restore sit/lay state
++-- if self.actions.move_state then
++-- if self.actions.move_state.is_sitting == true then
++-- npc.actions.sit(self, {pos=self.object:getpos()})
++-- elseif self.actions.move_state.is_laying == true then
++-- npc.actions.lay(self, {pos=self.object:getpos()})
++-- end
++-- end
++
+ -- NPC is initialized, check other variables
+ -- Check child texture issues
+ if self.is_child then
diff --git a/.idea/shelf/Update_building_type_definitions_.xml b/.idea/shelf/Update_building_type_definitions_.xml
new file mode 100644
index 0000000..66908e0
--- /dev/null
+++ b/.idea/shelf/Update_building_type_definitions_.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/shelf/Update_building_type_definitions_/shelved.patch b/.idea/shelf/Update_building_type_definitions_/shelved.patch
new file mode 100644
index 0000000..6025197
--- /dev/null
+++ b/.idea/shelf/Update_building_type_definitions_/shelved.patch
@@ -0,0 +1,23 @@
+Index: data/occupations/default_priest.lua
+IDEA additional info:
+Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
+<+>----------------------------------------------------\n-- Basic priest occupation for Advanced NPC (WIP)\n-- By Zorman2000\n----------------------------------------------------\n-- The basic priest occupation is given to NPCs that spawn on houses\n-- surrounding churchs. While on the church, the priest provides\n-- universal wisdom and advice, and also heals the player a limited number of times.\n-- DISCLAIMER: The \"teachings\" in this file come from a compilation of 15 principles shared\n-- among religions around the world. Zorman2000 and other contributors are not\n-- necessarily aligned with the principles and morals in these teachings, nor affiliated\n-- to religions that promote them.\n\nlocal priest_def = {\n dialogues = {\n type = \"given\",\n data = {\n {\n text = \"Blessings be upon you, my child!\",\n tags = {\"unisex\"}\n },\n {\n text = \"The temple will always open the doors to everyone.\",\n tags = {\"unisex\"}\n },\n {\n text = \"Following the teachings is the path to a good life.\",\n tags = {\"unisex\"}\n },\n {\n text = \"Thanks for coming to greet me, I hope you have a blessed day! \",\n tags = {\"unisex\"}\n },\n {\n text = \"Welcome to the temple, how can I help you today?\",\n flag = {name=\"on_church\", value=true},\n tags = {\"unisex\"},\n responses =\n {\n [1] = {\n text = \"I'm injured. Can you heal me?\",\n action_type = \"function\",\n action = function(self, player)\n local heal_count = self.flags[\"heal_count\"]\n if heal_count then\n -- Increase heal count\n self.flags[\"heal_count\"] = self.flags[\"heal_count\"] + 1\n else\n self.flags[\"heal_count\"] = 1\n heal_count = 1\n end\n -- Check if heal count is achieved\n if heal_count > 5 then\n npc.chat(self.npc_name, player:get_player_name(), \"I cannot heal you anymore, \"\n ..\"my child.\\nTo mortals like you and me, the power of the Creator is\\n\"\n ..\" limited. Only though learning the teachings we are able to understand more\"\n ..\"...\\nBe safe my child.\")\n else\n npc.chat(self.npc_name, player:get_player_name(),\n \"Receive the blessings of the Creator!\")\n effect(self.object:getpos(), 20, \"default_coral_skeleton.png\", 0.1, 0.3, 3, 10)\n -- Heal one heart\n player:set_hp(player:get_hp() + 2)\n end\n end\n },\n [2] = {\n text = \"What are your teachings?\",\n action_type = \"function\",\n action = function(self, player)\n local teachings = {\n [1] = \"Do unto others what you would have them do unto you\",\n [2] = \"Honor your Father and Mother. Knowing them is the key to knowing ourselves\",\n [3] = \"Sincerity is the way to heaven,\\nand to think how to be sincere is the way of the man\",\n [4] = \"Generosity, charity and kindness will open an individual to an unbounded reservoir of riches\",\n [5] = \"Even as the scent dwells within the flower, so God within thine own heart forever abides\",\n [6] = \"Acts of faith, prayer and meditation provide us with the strength that allows love for our fellow man to become an abiding force. Love is unifying.\",\n [7] = \"Peacemakers are blessed.\\nPeace is the natural result of individuals and nations living in close kinship\",\n [8] = \"You reap what you sow.\\nEven if it is a mystery, we are all ruled by this inevitable law of nature\",\n [9] = \"The blessings of life are deeper than what can be appreciated by the senses\",\n [10] = \"Do no harm, as we are part of the whole, and shouldn't perceive others as foreign or separate from ownself\",\n [11] = \"The most beautiful thing a man can do is to forgive wrong\",\n [12] = \"Judge not, lest ye be judged. Mankind is nothing but a great family and we all spring from common source\",\n [13] = \"Anger clouds the mind in the very moments that clarity and objectivity are needed most.\",\n [14] = \"Nature, Being, The Absolute, Creator... whatever name man chooses, there is but one force in the universe. All people and things are of one essence\",\n [15] = \"Study the words, no doubt, but look behind them to the thought they indicate;\\nhaving fond it, throw the words away. Live the spirit of them\"\n }\n npc.chat(self.npc_name, player:get_player_name(), teachings[math.random(1, #teachings)]\n ..\". \\nThese are the teachings of our Creator.\")\n end\n }\n }\n }\n }\n },\n textures = {\n \"npc_male_priest.png\"\n },\n initial_inventory = {\n {name=\"farming:bread\", count=1}\n },\n properties = {\n initial_trader_status = npc.trade.NONE,\n enable_gift_items_hints = false\n },\n\n building_types = {\n \"hut\", \"house\", \"farm_tiny\", \"lumberjack\"\n },\n surrounding_building_types = {\n \"church\"\n },\n schedules_entries = {\n [7] = {\n -- Get out of bed\n [1] = {\n task = npc.actions.cmd.USE_BED,\n args = {\n pos = npc.places.PLACE_TYPE.BED.PRIMARY,\n action = npc.actions.const.beds.GET_UP\n }\n },\n -- Walk to home inside\n [2] = {\n task = npc.actions.cmd.WALK_TO_POS,\n chance = 95,\n args = {\n end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,\n walkable = {}\n }\n },\n -- Allow mobs_redo wandering\n [3] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}\n },\n [8] = {\n -- Walk to workplace\n [1] =\n {\n task = npc.actions.cmd.WALK_TO_POS,\n args = {\n end_pos = npc.places.PLACE_TYPE.WORKPLACE.PRIMARY,\n walkable = {},\n use_access_node = true\n }\n },\n [2] =\n {\n property = npc.schedule_properties.flag,\n args = {\n action = \"set\",\n flag_name = \"on_church\",\n flag_value = true\n }\n }\n },\n [17] = {\n [1] =\n {\n property = npc.schedule_properties.flag,\n args = {\n action = \"set\",\n flag_name = \"on_church\",\n flag_value = false\n }\n },\n [2] =\n {\n task = npc.actions.cmd.WALK_TO_POS,\n args = {\n end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,\n walkable = {}\n }\n }\n },\n [21] = {\n [1] = {\n task = npc.actions.cmd.WALK_TO_POS,\n args = {\n end_pos = {place_type=npc.places.PLACE_TYPE.BED.PRIMARY, use_access_node=true},\n walkable = {}\n }\n },\n -- Use bed\n [2] = {\n task = npc.actions.cmd.USE_BED,\n args = {\n pos = npc.places.PLACE_TYPE.BED.PRIMARY,\n action = npc.actions.const.beds.LAY\n }\n },\n -- Stay put on bed\n [3] = {action = npc.actions.cmd.FREEZE, args = {freeze = true}}\n }\n }\n}\n\n-- Register occupation\nnpc.occupations.register_occupation(\"basic_priest\", priest_def)
+Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
+<+>UTF-8
+===================================================================
+--- data/occupations/default_priest.lua (date 1505308148000)
++++ data/occupations/default_priest.lua (revision )
+@@ -103,11 +103,9 @@
+ enable_gift_items_hints = false
+ },
+
+- building_types = {
+- "hut", "house", "farm_tiny", "lumberjack"
+- },
++ building_types = {},
+ surrounding_building_types = {
+- "church"
++ {type="church", origin_building_types={"hut", "house", "farm_tiny", "lumberjack"}}
+ },
+ schedules_entries = {
+ [7] = {
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..2779b79
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,1229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ step_into_air_only
+ ItemStack
+ inventory_contains
+ enter
+ trader_data.trade_list.both
+ access
+ minetest.get_item
+ vector.
+ find_node_in_front_of_door
+ facedir
+ get_openable_node_state
+ private
+ minetest.get_meta
+ minetest.set_meta
+ Trader_data
+ set_string
+ on_rightclick
+ start_dialogue
+ timer
+ registered_entities
+ mobs:register_mob
+ mob_activate
+ rotate_
+ texture_list
+ is_female_texture
+ minetest.after
+ unlock_actions
+ return args.freeze
+ self.commands
+ actions
+
+
+ trader_data.trade_list
+ return not(args.freeze)
+ self.actions
+ commands
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/new_api.md b/doc/new_api.md
new file mode 100644
index 0000000..a42fbe7
--- /dev/null
+++ b/doc/new_api.md
@@ -0,0 +1,90 @@
+Advanced NPC 1.0 proposal
+-------------------------
+
+While Advanced NPC provides functionality and a level of intelligence that no other mob mod can, it is still limited in some features and to its ultimate purpose of creating functional towns and/or simulated communities. The following are the areas that has been identified as lacking:
+
+ - Idle/wandering
+ - When NPCs aren't executing actions, their movement is very dumb. They wander aimlessly, constantly and usually bump into obstacles and keep walking nevertheless. They get stuck at places they shouldn't.
+ - Relationships
+ - Relationships are very hardcoded, and there's no flexibility on them.
+ - Unable to add more functionality
+ - All actions are hardcoded. While the essentials are in, making a NPC operate another node that is not a furnace/chest/door is almost impossible. If a mod adds a node and wants NPC to be able to operate it, it is certainly very hard.
+ - More randomness in schedules
+ - While schedules are all about making NPCs do actions at certain times, it is not flexible enough to make it look more realistic. One morning a NPC can get up and make breakfast or not, put some music on a music player or not, go outside their home and wander around, etc.
+ - Unable to react to certain triggers
+ - When NPCs are punched, `mobs_redo` takes over and controls the NPC. Also, NPCs are unable to scan an area for certain things and perform actions continually based on it.
+
+The above are all playability issues and deficiencies. Some technical issues has to be addressed as well regarding the API. Given all these, the following is a proposal to move the mod towards the correct direction.
+
+
+##Proposed changes:
+ - Unify the actions/tasks/schedule property change/schedule query API into a `commands` API
+ - Add new commands to bring the NPC interaction level closer to that of a player
+ - Rename `flags` to `properties`
+ - Allow registering scripts, or collections of commands for external mods to provide extra functionality
+
+
+Unified Commands API
+--------------------
+
+The goal of this API is to provide consistency and extensibility to the actions a NPC can perform. First of all, rename actions/tasks/property change/query to `commands`. Each command will have the following properties that determines how it is to be executed and what it does:
+
+ - Type: specifies the type of command. The following are valid types:
+ - `instruction`: Used for fundamental, atomic operations. This type maps directly to what are called now `actions`, which are for example, walk one step, dig, place, etc.
+ - `control`: Used for specific commands that are flow control statements. Example: If-else, for loops. The conditional statements is a Lua boolean expression.
+ - `script`: Used for collections of commands, executed on a sequential structure. This type maps directly to `tasks`.
+ - Execution: specifies how the command is to be executed. The following are valid valuesf for this parameter:
+ - `immediate`: Will execute this command immediately, without any enqueing. Very little commands should be able to do this. The `control` commands should be executed immediately as they need to enqueue certain commands depending on their conditions.
+ - `default`: Command will be enqueued and executed on the global command timer call.
+ - Interruptable: specifies whether the global command timer and/or the scheduler can interrupt the command. Boolean value, can be set to false or true.
+ - _Important_: Non-interruptable commands should be able to finish by themselves. The API will execute the default command once a non-interruptable command is done and if it doesn't executes another command.
+ - Parameters: a Lua table with all the parameters that the command requires. Depending on the type, some parameters are required. Below is a list of required parameters per type:
+ - `instruction`: Requires just the parameters required by the instruction to execute.
+ - `control`: Requires different parameters depending on the type of control.
+ - Required:
+ - `condition`: The condition to be evaluated. This is a Lua boolean expression.
+ - `match_commands`: A Lua array with the commands to be executed if condition evaluates to `true`.
+ - Dependent on type:
+ - `operation`: Only required in `for-loop` command. Operation to execute on the loop variable (e.g. increase/decrease)
+ - `repetition`: Optional for `for-loop` command. Can't be used together with `max` and `min`.
+ - `max`: Optional for `for-loop` command. Can't be used together with `repetition`. Requires `min`. Randomizes a loop execution and sets the upper bound of how many times the loop will execute.
+ - `min`: Optional for `for-loop` command. Can't be used together with `repetition`. Requires `max`. Randomizes a loop execution and sets the lower bound of how many times the loop will execute.
+ - `else_commands`: Only required in `if-else` command. A Lua array with the commands to be executed if condition evaluates to `false`.
+ - `script`: A Lua array of commands to execute, in order
+
+The following `instruction` commands will be added to the default set:
+ - `do_punch`: Executes the `on_punch` function of a node, object or player
+ - `do_rightclick`: Executes the `on_rightclick` function of a node, object or player
+ - `set_property`: Sets the value of a variable in the `self.properties` object. If the variable doesn't exists, it is created. This command is executed immediately and is not enqueued.
+ - Parameters:
+ - `key`: The property key-name. This is a variable in the `self.properties` object
+ - `value`: The property value.
+ - `get_property`: Returns the value of a given property. This command is executed immediately and is not enqueued.
+ - Parameters:
+ - `key`: The property key-name.
+ - `set_internal_property`: Sets the value of a limited set of internal properties related to the NPC trading and personality variables.
+ - `get_internal_property`: Gets the value of a limited set of internal properties related to the NPC trading and personality variables.
+ - `add_item_to_npc`: Adds an item to the NPC inventory, without any specific source.
+ - `remove_item_from_npc`: Removes a specific item from the NPC inventory.
+ - `query`: Executes a query for nodes or objects. Returns a Lua table with none, single or many positions.
+
+The following `control` commands will be added to the default set:
+ - `if-else`: An if-else control statement that will execute immediately. It will evaluate the given `condition` parameter and execute commands depending on the evaluation of the `condition`.
+ - Parameters:
+ - `condition`: A Lua boolean expression to be evaluated.
+ - `true-commands`: A Lua array of commands to be executed if `condition` evaluates to `true`.
+ - `else-commands`: A Lua array of commands to be executed if `condition` evaluates to `false`.
+ - `loop`: A flexible loop command. Supports for-loop and while-loops. The amount of loops done will be available in `npc.commands.current_loop_count`. Executes immediately, it is not enqueued.
+ - Parameters:
+
+
+##Extensibility
+Once the above commands has been added, it is possible to safely build scripts which don't touch directly many of the internal NPC mechanisms. An API will be provided for external mods to register scripts that let NPCs perform actions related to those mods, e.g. operating a node provided by the mod. The API for this will be:
+
+`npc.commands.register_script(name, script)`
+
+All registered scripts have the following properties:
+ - They are interruptable by the command queue/scheduler
+ - They are not immediately executed
+
+The `script` parameter is a Lua array of commands that will be executed when the script is executed.
\ No newline at end of file
diff --git a/npc.lua b/npc.lua
index 9ac7dbf..b2428dd 100755
--- a/npc.lua
+++ b/npc.lua
@@ -893,6 +893,7 @@ end
--------------------------------------------
-- These functions manage the execution context, where variables are
-- stored, whether internal (loops) or user-created.
+-- The execution context is cleared at the end of each script.
npc.execution_context = {}
-- This function adds a value to the execution context.