advanced_npc/doc/api.md
BrunoMine 2dc2400b38 Api doc update (#31)
* Update api.md

* Add schedules
* Add occupation
* Update places map
* Fix `step` and `initialize`

* Add schedule commands

* Add WALK_TO_POS task

* orthographic correction
2017-12-20 14:40:36 -05:00

15 KiB

Advanced_NPC API Reference Alpha-2 (DEV)

IMPORTANT: This WIP & unfinished file contains the definitions of current advanced_npc functions (Some documentation is lacking, so please bear in mind that this WIP file is just to enhance it)

Introduction

You can consult this document for help on API of behaviors for the NPCs. The goal is to be able to have NPCs that have the same functionality as normal players. The NPCs make Sokomine's mg_villages in Minetest alive although they can be manually spawned outside the village and work as good as new. Here is some information about the API methods and systems.

Initialize NPC

The API works with some variables into Lua Entity that represent a NPC, then you should initialize the Lua Entity before that it really assume a controled behavior.

Methods

  • npc.initialize(entity, pos, is_lua_entity, npc_stats, occupation_name) : Initialize a NPC

The simplest way to start a mob (of mobs_redo API) is by using the on_spawn function Note: currently this call is unduly repeated (mobs_redo problem), so you should check if npc has already been initialized.

on_spawn = function(self)
    if self.initialized == nil then
        npc.initialize(self, self.object:getpos(), true)
        self.tamed = false
    end
end

Or after add in the world

local obj = minetest.add_entity({x=0, y=10, z=0}, "mobs:sheep", {naked = true})
local luaentity = get_luaentity(obj)
npc.initialize(luaentity, luaentity.object:getpos(), true)
luaentity.tamed = false

NPC Steps

The API works with NPC steps, then on_step callback need run the npc.on_step(luaentity). This function process the NPC actions and return the freeze state, which is used for stop mobs_redo behavior.

Example:

on_step = function(self, dtime)
    npc.step(self, dtime)
end

Mobs of Mobs_Redo API uses do_custom function instead of on_step callback and it needs return the freeze state to stop mobs_redo behavior. Here is a recommended code.

do_custom = function(self, dtime)
	
    -- Here is my "do_custom" code
	
    -- Process the NPC action and return freeze state
    return npc.step(self, dtime)
end

Actions and Tasks Queue

Actions are "atomic" executable actions the NPC can perform. Tasks are sequences of actions that are common enough to be supported by default. Each action or task is wrapped on a Lua table which tells the action/task to be executed and the arguments to be used. However, this is encapsulated to the user in the following two methods for a NPCs:

Methods

  • npc.add_action(luaentity, action, {action definition}): Add action into NPC actions queue
  • npc.add_task(luaentity, task, {task definition}): Add task into NPC actions queue

For both of the above, action/task is a constant defined in npc.actions.cmd, and {task/action definition} is a Lua table specific arguments to each action/task.

Example

npc.add_task(self, npc.actions.cmd.USE_BED, {
    pos = {x=0,y=0,z=0},
    action = npc.actions.const.beds.LAY
})
npc.add_action(self, npc.actions.cmd.SET_INTERVAL, {
    interval = 10,
    freeze = true,
})
npc.add_task(self, npc.actions.cmd.USE_BED, {
    pos = {x=0,y=0,z=0},
    action = npc.actions.const.beds.GET_UP
})

See more in actions_and_tasks.md documentation.

Schedules

The interesting part of Advanced NPC is its ability to simulate realistic behavior in NPCs. Realistic behavior is defined simply as being able to perform tasks at a certain time of the day, like usually people do. This allow the NPC to go to bed, sleep, get up from it, sit in benches, etc. All of this is simulated through a structured code using action and tasks.

The implementation resembles a rough OS process scheduling algorithm where only one process is allowed at a time. The processes or tasks are held in a queue, where they are executed one at a time in queue fashion. Interruptions are allowed, and the interrupted action is re-started once the interruption is finished.

Schedule commands

Schedule commands are an array of actions and tasks that the NPC. There are 4 possible commands:

  • action
    {
        action = action, -- Is a constant defined in `npc.actions.cmd`
        args = {} -- action arguments
    }
  • task
    {
        task = task, -- Is a constant defined in `npc.actions.cmd`
        args = {} -- task arguments
    }
  • Property change
    {
        ???
    }
  • Schedule query/check
    {
        schedule query/check definition
    }

Schedule time

Only integer value 0 until 23

  • 0: 0/24000 - 999
  • 1: 1000 - 1999
  • 2: 2000 - 2999
  • ...
  • 22: 22000 - 22999
  • 23: 23000 - 23999

Schedule Type

  • "generic" : Returns nil if there are already seven schedules, one for each day of the week or if the schedule attempting to add already exists. The date parameter is the day of the week it represents as follows: Note: Currently only one schedule is supported, for day 0 1: Monday 2: Tuesday 3: Wednesday 4: Thursday 5: Friday 6: Saturday 7: Sunday
  • "date_based" : The date parameter should be a string of the format "MM:DD". If it already exists, function retuns nil

Methods

  • npc.create_schedule(luaentity, schedule_type, day) : Create a schedule for a NPC
  • npc.delete_schedule(luaentity, schedule_type, date) : Delete a schedule for a NPC
  • npc.add_schedule_entry(luaentity, schedule_type, date, time, check, commands) : Add a schedule entry for a time
  • npc.get_schedule_entry(luaentity, schedule_type, date, time) : Get a schedule entry
  • npc.update_schedule_entry(luaentity, schedule_type, date, time, check, commands) : Update a schedule entry

Examples

-- Schedule entry for 7 in the morning
npc.add_schedule_entry(self, "generic", 0, 7, nil, {
    -- Get out of bed
    [1] = {
        task = npc.actions.cmd.USE_BED, 
        args = {
            pos = "bed_primary",
            action = npc.actions.const.beds.GET_UP
       }
    },
    -- Allow mobs_redo wandering
    [2] = {
        action = npc.actions.cmd.FREEZE, 
        args = {
        	freeze = false
        }
    }
})

Occupations

NPCs need an occupation or job in order to simulate being alive. This functionality is built on top of the schedules functionality. Occupations are essentially specific schedules, that can have slight random variations to provide diversity and make specific occupations less predictable. Occupations are associated with textures, dialogues, specific initial items, type of building (and surroundings) where NPC lives, etc.

Methods

  • npc.occupations.register_occupation(occupation_name, {occupation definition}) : Register an occupation
  • npc.occupations.initialize_occupation_values(luaentity, occupation_name) : Initialize an occupation for a NPC

Places Map

Places map define which NPCs can access which places. Places are separated into different types.

Place types

Current place types

  • bed_primary : the bed of a NPC
  • sit_primary
  • sit_shared
  • furnace_primary
  • furnace_shared
  • storage_primary
  • storage_shared
  • home_entrance_door
  • schedule_target_pos : used in the schedule actions
  • calculated_target_pos
  • workplace_primary
  • workplace_tool
  • home_plotmarker
  • home_inside
  • home_outside

Methods

  • npc.places.add_owned(luaentity, place_name, place_type, pos, access_pos) : Add owned place. luaentity npc owner. place_name a specific place name. place_type place typing. pos is a position of a node to be owned. access_pos is the coordinate where npc must be to initiate the access. Place is added for the NPC.
  • npc.places.add_shared(luaentity, place_name, place_type, pos, access_node) : Add shared place

Dialogues

Dialogs can be registered to be spoken by NPCs.

Tags

The flags or marks of the dialogue text. Tags can be used for ....

  • "unisex" : Both male and female NPCs can say the defined text.
  • "phase1" : NPCs in phase 1 of a relationship can say the defined text.

Methods

  • set_response_ids_recursively() : A local function that assigns unique key IDs to dialogue responses.
  • npc.dialogue.register_dialogue({dialogue definition}) : Defines and registers dialogues.
  • npc.dialogue.search_dialogue_by_tags({search_tags}) : A method returning a table of dialogues if called.

Definition tables

Occupation definition (register_occupation)

{
    dialogues = {
        enable_gift_item_dialogues = true, --[[
            ^ This flag enables/disables gift item dialogues.
            ^ If not set, it defaults to true. ]]
        type = "", -- The type can be "given", "mix" or "tags"
        data = {}, --[[
            ^ Array of dialogue definitions. This will have dialogue
              if the type is either "mix" or "given" ]]
        tags = {}, --[[
            ^ Array of tags to search for. This will have tags
              if the type is either "mix" or "tags" ]]
    },
    
    textures = {}, --[[
        ^ Textures are an array of textures, as usually given on
          an entity definition. If given, the NPC will be guaranteed
          to have one of the given textures. Also, ensure they have sex
          as well in the filename so they can be chosen appropriately.
        ^ If left empty, it can spawn with any texture. ]]
        
    walkable_nodes = {}, -- Walkable nodes
    
    building_types = {}, --[[
        ^ An array of string where each string is the type of building
          where the NPC can spawn with this occupation.
        ^ Example: building_type = {"farm", "house"}
        ^ If left empty or nil, NPC can spawn in any building ]]
    
    surrounding_building_types = {}, --[[
        ^ An array of string where each string is the type of building
          that is an immediate neighbor of the NPC's home which can also
          be suitable for this occupation. Example, if NPC is farmer and
          spawns on house, then it has to be because there is a field
          nearby. 
        ^ If left empty or nil, surrounding buildings doesn't matter. ]]
    
    workplace_nodes = {}, --[[
        ^ An array of string where each string is a node the NPC works with. 
        ^ These are useful for assigning workplaces and work work nodes. ]]
    
    initial_inventory = {}, --[[
        ^ An array of entries like the following:
          {name="", count=1} -- or
          {name="", random=true, min=1, max=10}
        ^ This will initialize the inventory for the NPC with the given
          items and the specified count, or, a count between min and max
          when the entry contains random=true
        ^ If left empty, it will initialize with random items. ]]
    
    initial_trader_status = "", --[[
        ^ String that specifies initial trader value. 
        ^ Valid values are: "casual", "trader", "none" ]]
    
    schedules_entries = {},
        ^ This is a table of tables in the following format:
          {
              [<time number>]  = {
                  [<command number>] = {
                      command
                  }
              }
          }
        ^ Example:
        {
            [1] = {
                [1] = schedule command
            },
            [13] = {
                [1] = schedule command,
                [2] = schedule command
            },
            [23] = {
                [1] = schedule command
            }
        }
          The numbers, [1], [13] and [23] are the times when the entries
          corresponding to each are supposed to happen. The tables with
          [1], [1],[2] and [1] actions respectively are the entries that
          will happen at time 1, 13 and 23. ]]
}

Dialogue definition (register_dialogue)

{
    text = "Hello.", --[[ 
    ^ The dialogue text itself. 
    ^ It must be included in the method.]]
    
    tags = {"tag1", "tag2"} --[[ 
    ^ The flags or marks of the dialogue text. 
    ^ The object can be excluded. ]]
}

Schedule query/check definition (schedule command)

{
    check = true, -- Indicates that this is a schedule query/check
    
    range = 2, -- Range of checked area in blocks.
    
    count = 20, -- How many checks will be performed.
    
    random_execution_times = true, --[[
        ^ Randomizes the number of checks that will be performed.
        ^ min_count and max_count is required ]]
    
    min_count = 20, -- minimum of checks
    max_count = 25, -- maximum of checks
    
    nodes = {"itemstring1", "itemstring2"}, --[[ 
        ^ Nodes to be found for the actions.
        ^ When a node is found, it is add in the npc place map 
          with the place name "schedule_target_pos"
    
    prefer_last_acted_upon_node = true, -- If prefer to act on nodes already acted upon
    
    walkable_nodes = {"itemstring1", "itemstring2"}, -- Walkable nodes
    
    actions = { --[[
        ^ Table where index is a itemstring of the node to be found, 
          and value is an array of actions and tasks to be performed 
          when found the node. ]]
   	
        ["itemstring1"] = {            
           [1] = action or task in schedule command format,
           [2] = action or task in schedule command format,
           [3] = action or task in schedule command format
        },
        ["itemstring2"] = {            
           [1] = action or task in schedule command format,
           [2] = action or task in schedule command format
        }
    },
    
}

Examples:

Syntax example 1:

npc.dialogue.register_dialogue({
    text = "Hello.", -- "Hello." will be said by the NPC upon rightclick and displayed in the messages section.
    tags = {"unisex", "phase1"} -- The flags that define the conditions of who and what can say the text.
})

Syntax example 2:

npc.dialogue.register_dialogue({
    text = "Hello again."
    -- The tags object is excluded, meaning that any NPC can say "Hello again." upon rightclick under no condition.
})