Code Monkey home page Code Monkey logo

advanced_npc's Introduction

advanced_npc

Introduction

Advanced NPC is a mod for Minetest using mobs_redo API. The goal of this mod is to be able to have live villages in Minetest. These NPCs are highly inspired by the typical NPCs of Harvest Moon games. The general idea is that on almost all buildings of a village there are NPCs that are kind of intelligent: they have daily tasks they perform, can speak to players, can trade with the player, can use their own items (chests and furnaces for example), know where to go around their house and village, can be lumbers, miners or any other Minetest-suitable profession and can ultimately engage into relationships with the player. And while basically only players are mentioned here, the ultimate goal is that they can do all of this also among themselves, so that villages are alive and evolving by themselves, without player intervention.

Advanced NPC is an API, and there are curently two mods that uses it:

Installation

NOTE: Advanced NPC is still under development. While the mod is largely stable, it lacks one of the most important pieces: spawning. Currently, NPCs can be spawned using eggs (found in creative inventory as 'NPC') and by themselves on villages of the mg_villages mod. NPCs will spawn automatically on mg_villages villages and over time will populate the entire village. If something goes wrong, you can reset the village by:

  • Clearing all objects (in chat, type /clearobjects quick)
  • Restore original plotmarkers (in chat, type /restore_plotmarkers radius)
    • The radius can be any number, but it is recommended you use a not so large number. 200 is suitable. So stand in the middle of the village and then run that command. This will actually restore the village and will slowly make NPCs spawn again. Currently there's no way to disable NPCs spawning on village, except by going to spawner.lua and commenting out all of minetest.register_abm() code.

Download the mod here (link always pointing to latest version)

For this mod to work correctly, you also need to install the mobs_redo mod. After installation, make sure you enable it in your world.

License

advanced_npc is Copyright (C) 2016-2017 Hector Franqui (zorman2000), licensed under the GPLv3 license. See license.txt for details.

The pathfinder.lua file contains code slighlty modified from the pathfinder mod by MarkBu, which is licensed as WTFPL. See actions/pathfinder.lua for details.

Current NPC textures are from mobs_redo mod. The following textures are by Zorman2000:

  • marriage_ring.png - CC BY-SA

Documentation and API

This mod requires a good user manual, and also is planned to have an extensive API, properly documented. Unfortunately, these still aren't ready. A very very very WIP manual can be found in the wiki

Roadmap

See it on the wiki.

advanced_npc's People

Contributors

brunomine avatar hkzorman avatar xaleth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

advanced_npc's Issues

Scheduling does not run

The method of setting the time (/time) works initially, but after a period it does not seem to work.
Branch clean_api
Minetest 0.5.0-dev
Mod test: sociedades

2018-05-22 13:44:18: [Server]: No obj found
2018-05-22 13:44:19: [Server]: No obj found
2018-05-22 13:44:22: [Server]: No obj found
2018-05-22 13:44:23: [Server]: No obj found
2018-05-22 13:44:26: [Server]: No obj found
2018-05-22 13:44:27: [Server]: No obj found
Loaded mesh: character.b3d
2018-05-22 13:44:28: ACTION[Server]: 888 sets time to 8950
2018-05-22 13:44:30: [Server]: No obj found
2018-05-22 13:44:31: [Server]: No obj found
2018-05-22 13:44:34: [Server]: No obj found

bug do agendamento

Allow other mods to register their node functionality for compatibility

While Advanced NPC provides compatibility with Sokomine's cottages mod, other mods that provide nodes usable by NPCs (like doors, beds, chairs, differently-operated furnaces, etc.) and want to support this mod should be able to do so by registering how their nodes work.

For example, if a mod has doors that do not use the default doors API, then a mod can tell advanced_npc how to detect their own doors are open/closed, by using some kind of register function like the following:

npc.actions.register_door({
  name_prefix: "other_mod:door",
  is_door_open = function(pos) {
    local node_meta = minetest.get_meta(pos)
    return meta:get_string("door_state") == "open"
  }
}

API Documentation

I am trying to compile a more complete documentation on how the API works for developers. I still need to understand some API dynamics and I'd like some help with it.

My API documentation

Is it correct now?

Spawner: Spawning sequence is run more than once

Spawning sequence is currently being run two or more times, when it is supposed to only run once.
Cause:

  • A violation protection on mg_villages mod will cause replacement of the plotmarker.

Possible solution:

  • Make an alias for the auto-spawner node?

Needs more investigation.

NPCs can not find objects on another floor of the house

They remain locked on the same floor in node_query check (This needs to be made clear in the documentation). This is pretty bad, but you can work around this problem by using programs that are conditioned to NPC locations. How is this possible?

Unify actions/tasks API

Unified Commands API

Description:

The Commands API is a Bash Script-like execution environment for Minetest entities.
There are the following fundamental constructs:

  • commands: execute a specific action (e.g. dig a node, search for nodes, etc.)
  • variables: stores information temporarily (e.g. results of node search)
  • control statements: if/else and loops

All of these is done using the Lua programming language, and a few custom
expressions where Lua can't help.

The fundamental concept is to use the three constructs together in the form of
a script to allow NPCs (and any Minetest entity) to perform complex actions,
like walking from one place to the other, operating furnaces, etc. In this sense,
this "commands" API can be considered a domain-specific language (DSL), that is
defined using Lua language structures.

Basic definitions:

A variable is any value that can be accessed using a specific key, or name.
In the context of the commands API, there is an execution context where
variables can be stored into, read and deleted from. The execution context
is nothing but a map of key-value pairs, with the key being the variable name.
Some rules regarding variables:

  • A variable can be read-write or read-only. Read-only variables cannot be
    updated, but can be deleted.
  • A variable cannot be overwritten by another variable of the same name.
  • The scope of variables is global within a script. The execution context
    is cleared after a script finishes executing. For more info about scripts,
    see below.
  • The Lua entity variables, referring to any self.* value isn't available
    as a variable. This is to keep the NPC integrity from a security perspective.
    However, as some values are very useful and often needed (such as
    self.object:getpos()), some variables are exposed. These are referred to
    as internal variables. They are read-only.

A 'command' is a Lua function, with only two parameters: self, the Lua entity,
and args, a Lua table containing all arguments required for the command. The
control statements (if/else, loop) and variable set/read are defined as commands
as well. The arguments are not strictly controlled, with the following exceptions:

  • A variable expression string is special string that allows a variable
    to be passed as an argument to a command. The reason why a function can't
    be used for this is because the execution context, where variables are
    stored, lives in the entity itself (the self object), to which there's no
    access when a script is defined as a Lua array.
    The special string has a specific format: "<var_type>:<var_key>" where the
    accepted values for <var_type> are:
    • var, referring to a variable from the execution context, and,
    • ivar, referring to an internal variable (an exposed self.* variable)
  • A function expression table is a Lua table that contains a executable
    Lua function and the arguments to be executed. The function is executed
    at the proper moment when passed as an argument to a command, instead of
    executing immediately while defining a script.
    The function expression table has the following format:
     {
        func = <Lua function>,
        args = <Lua table of arguments for the function>
     }
  • A boolean expression table is a Lua table that is reconstructed into a
    Lua boolean expression. The reason for this to exist is similar to the
    above explanation, and is that, at the moment a script is defined as a
    Lua array, any function or variable passed as a boolean expression will
    evaluate, making the value effectively a constant. That would render
    loops and if/else statements useless.
    The boolean expression table has the following format:
     {
        left_side: <any_value|function expression table|variable expression string>,
        operator: <string>,
        right_side: <any value|function expression table|variable expression string>,
     }

operator and right_side are optional: a single function in left_side is
enough as long as it evaluates to a boolean value. The operator argument
accepts the following values:
- equals
- not_equals
- greater_than
- greater_than_equals
- less_than
- less_than_equals
right_side is required if operator is defined.

A script is an ordered sequence of commands to be executed, and is defined
using a Lua array, where each element is a Lua function corresponding to a
command. Scripts are intended to be implemented by users of the API, and as
such it is possible to register a script for other mods to use. For example,
a script can be used by a mod that creates a music player node so that NPCs
can also be able to use it.
Scripts can also be executed at certain times during a Minetest day thanks
to the schedules functionality.

Execution:

The execution of commands is performed on a timer basis, per NPC, with a default
interval value of one second. This interval can be changed by a command itself,
however it is the recommended interval is one second to avoid lag caused by many NPCs
executing commands.
Commands has to be enqueued in order to execute. Enqueuing commands directly isn't
recommended, and instead it should be done through a script. Nonetheless, the API
for enqueuing commands and scripts is the following:

  • npc.enqueue_command(command_name, args)
  • npc.enqueue_script(script_name, args)

The control statement commands (if/else, loops) and variable set/read commands
will execute the next command in queue immediately after finishing instead of
waiting for the timer interval.

There is an execution context which contains all the variables that are defined
using the variable set commands. Also, it contains values specific to the loops,
like the number of times it has executed. The execution context lives in the NPC
self object, and therefore, has to be carefully used, or otherwise it can create
huge memory usage. In order to avoid this, variables can be deleted from the execution
context using a specific command (npc.commands.del_var(key)). Also, as basic
memory management routine, the execution context is cleared after the end of
executing a script.
To keep global variables, use the npc.command.get/set_flag() API which is not
deleted after execution.

New commands

The following 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.

If/else:

This command allows the conditional execution of two array of commands
depending on the evaluation of a certain condition. This is the typical
if-else statement of a programming language. If-else can be nested.
Arguments:

  • condition: accepts two values:
    • boolean: Lua boolean expression, any expression that evaluates to true or false.
    • table: A boolean expression table.
  • true_commands: an array of commands to be executed when the condition
    evaluates to true
  • false_commands: an array of commands to be executed when the condition
    evaluates to false

Loops:

This command works as the types of loops, depending on the arguments
given. It can work as a while, a for and a for-each loop. Loops can
be nested.
While-loop arguments:

  • name: string, a key-name for this loop. Default is nil. If given,
    it gives access to the number of times the loop has executed.
  • condition: boolean, the loop will be executed as long as this condition
    evaluates to true
  • commands: array, array of commands to be executed during the loop

For-loop arguments:

  • name: string, a key-name for this loop. Default is nil. If given, it
    gives access to the number of times this loop has executed.
  • initial_value: integer, the starting value of the for-loop. If left
    blank, default value is 1.
  • condition: boolean, the loop will be executed as long as this condition
    evaluates to true.
  • modifier: function, the loop will execute this modifier at the end of
    every iteration. If left blank, default is: initial_value + 1
  • commands: array, array of commands to be executed during the loop

Both of these loops store how many times they have been executed. To
access it, it is required to give pass the argument name. Then the
value will be stored on the execution context and the value retrievable
with npc.commands.get_context(key), where key is the name argument.

For-each-loop arguments:

  • name: string, a key-name for this loop. Default is nil. If given, it
    gives access to the number of times this loop has executed and the current
    value of the array/table being evaluated.
  • iterable: array or table of key-value pairs, this is an iterable array
    or table for which the loop will execute commands at every element in the
    iterable array/table.
  • commands: array, array of commands to be executed during the loop
    To get the current element being iterated in a for-each loop, you need to define
    the name argument. Then, the value will be stored in the execution context and
    will be retrievable with npc.commands.get_context(key). It will return a table
    like this: {loop_count = x, current_value = y}

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.

Program registration

I'm trying to create a program and get this for now.

npc.programs.register("sunos:interagir", function(self, args)
	local tempo = args.time or 5
	local pos = npc.programs.helper.get_pos_argument(self, args.pos, true)
	
	-- Rotate
	npc.exec.proc.enqueue(self, "advanced_npc:rotate", {
		start_pos = self.object:getpos(),
		end_pos = pos,
	})
	
	-- Wait
	npc.exec.proc.enqueue(self, "advanced_npc:wait", {
		time = time,
	})
end)

It looks like the NPC does not rotate to the right place.

I did not understand correctly where the temporary data is and how to reference future places.

Action and task programs

I am already working on updating the documentation with the new tasks and actions that will work in program format.
I would like to use this issue for explanations on some programs.
Can you explain more details about the IDLE ("advanced_npc:idle") program? To better understand ways it can be used.

Add NPC spawner

  • Create node registration
  • Add code to replace suitable mg_villages:plotmarker nodes
  • Add code to scan useful nodes inside a building
  • Determine how many NPCs will be spawned depending on building
  • Spawn NPCs
  • Let NPC know the building's entrance
  • Assign nodes to NPC - In progress
  • Assign basic schedule to all NPCs

The second version of spawners should:

  • Assign schedules to NPCs based on the building type they are spawning
  • Determine nearby buildings
  • Add formspec to show this information
  • Allow changeable textures/nodetype

Separate API from specific NPCs

The proposal here is to make the mod advanced_npc completely as an API, with zero specific implementations of NPCs. Below is a comprehensive list of files that will be removed from the project and will be re-used in a separate, brand-new mod:

  • textures
    • npc_child_female1.png
    • npc_child_male1.png
    • npc_female[1-11].png
    • npc_male[1-14].png
    • npc_male_priest
  • random_data.lua
  • data
    • occupations
      • default.lua
      • default_farmer.lua
      • default_miner.lua
      • default_priest.lua
    • dialogues_data.lua
    • gift_items_data.lua
    • names_data.lua

All these files will be re-used in another mod.

Also, minor changes in npc.lua are required as currently it depends on random_data.lua for names.
When this is implemented, advanced_npc will be a pure API for implementing NPCs.

NPC models are displaced out of their hitbox

Testing on latest stable minetest (0.4.16), archlinux, pure configuration, no addons (except for mobs_redo, mg_villages, handle_schematics (dependency of mg_villages) all mods are latest git versions).
NPC appears to be displaced vertically.
screenshot_20170925_212528
You can see that their hitbox is still displayed correctly and they walk properly, though their model appears to be submerged.

index local state_program_on_finished (a nil value)

I allowed to see the arguments to help.

2018-04-23 13:08:35: [Server]: [advanced_npc] INFO: Hi, executing state process "advanced_npc:node_query"
2018-04-23 13:08:35: [Server]: [advanced_npc] INFO: Executing program '"advanced_npc:node_query"' with args:
{
	prefer_last_acted_upon_node = false,
	count = 1,
	nodes = {
		"sunos:bau_casa",
		"default:furnace",
		"sunos:wood_barrel_nodrop",
		"sunos:tear_palha_nodrop",
		"sunos:bancada_de_trabalho_nodrop",
		"sunos:kit_culinario_nodrop"
	},
	range = 6,
	on_found_executables = {
		["sunos:wood_barrel_nodrop"] = {
			{
				program_name = "advanced_npc:walk_to_pos",
				arguments = {
					end_pos = "compostagem"
				},
				interrupt_options = {
					
				}
			},
			{
				program_name = "advanced_npc:wait",
				arguments = {
					time = 3
				}
			}
		},
		["sunos:bau_casa"] = {
			{
				program_name = "advanced_npc:walk_to_pos",
				arguments = {
					end_pos = "bau"
				},
				interrupt_options = {
					
				}
			},
			{
				program_name = "advanced_npc:wait",
				arguments = {
					time = 3
				}
			}
		},
		["sunos:tear_palha_nodrop"] = {
			{
				program_name = "advanced_npc:walk_to_pos",
				arguments = {
					end_pos = "tear"
				},
				interrupt_options = {
					
				}
			},
			{
				program_name = "advanced_npc:wait",
				arguments = {
					time = 3
				}
			}
		},
		["sunos:kit_culinario_nodrop"] = {
			{
				program_name = "advanced_npc:walk_to_pos",
				arguments = {
					end_pos = "kit_culinario"
				},
				interrupt_options = {
					
				}
			},
			{
				program_name = "advanced_npc:wait",
				arguments = {
					time = 2
				}
			}
		},
		["sunos:bancada_de_trabalho_nodrop"] = {
			{
				program_name = "advanced_npc:walk_to_pos",
				arguments = {
					end_pos = "bancada_de_trabalho"
				},
				interrupt_options = {
					
				}
			},
			{
				program_name = "advanced_npc:wait",
				arguments = {
					time = 1
				}
			}
		},
		["default:furnace"] = {
			{
				program_name = "advanced_npc:walk_to_pos",
				arguments = {
					end_pos = "furnace_primary"
				},
				interrupt_options = {
					
				}
			},
			{
				program_name = "advanced_npc:wait",
				arguments = {
					time = 5
				}
			}
		}
	},
	on_not_found_executables = {
		{
			program_name = "advanced_npc:walk_to_pos",
			arguments = {
				end_pos = "bau"
			},
			interrupt_options = {
				
			}
		},
		{
			program_name = "advanced_npc:wait",
			arguments = {
				time = 3
			}
		}
	}
}
2018-04-23 13:08:35: ERROR[Main]: ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod 'sunos' in callback luaentity_Step(): ...anced_npc/executable/programs/builtin/node_query.lua:220: attempt to index local 'state_program_on_finished' (a nil value)
2018-04-23 13:08:35: ERROR[Main]: stack traceback:
2018-04-23 13:08:35: ERROR[Main]: 	...anced_npc/executable/programs/builtin/node_query.lua:220: in function <...anced_npc/executable/programs/builtin/node_query.lua:8>
2018-04-23 13:08:35: ERROR[Main]: 	(tail call): ?
2018-04-23 13:08:35: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:911: in function 'execute_process'
2018-04-23 13:08:35: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1168: in function 'process_scheduler'
2018-04-23 13:08:35: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1324: in function 'execution_routine'
2018-04-23 13:08:35: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1910: in function <...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1833>
2018-04-23 13:08:35: ERROR[Main]: 	(tail call): ?
2018-04-23 13:08:35: ERROR[Main]: 	...de Mods/minetest 0-5-0/bin/../mods/mobs_redo/api.lua:2579: in function <...de Mods/minetest 0-5-0/bin/../mods/mobs_redo/api.lua:2522>
2018-04-23 13:08:35: ACTION[Server]: 888 leaves game. List of players:

How put a program in queue

And this way does not allow you to queue multiple programs, apparently it runs all at once.

npc.programs.execute(self, "advanced_npc:walk_to_pos", {
	end_pos = {
		place_type="bed_primary", 
		use_access_node=true
	}
})
npc.programs.execute(self, "advanced_npc:use_bed", {
	pos = "bed_primary",
	action = npc.programs.const.node_ops.beds.LAY
})
npc.programs.execute(self, "advanced_npc:idle", {
	acknowledge_nearby_objs = false,
	wander_chance = 0
})

Function references for actions get unloaded frequently causing crashes

While I'm not 100% this is the cause of the bug, I'm quite certain that function references inside the Lua table of a Lua Entity get removed on unload/load of entities and are completely unreliable. Every time this function references get nil, Minetest crashes with a nil exception.

The proposed fix for this bug is to implement a execute function in actions.lua that takes a constant value and executes the corresponding function. These constants will be called "commands" and they are going to be defined in actions.lua. These commands are going to be stored in the queue of the Lua Entity instead of the direct function reference.

1.0 Version Roadmap

This issue is to keep track of what is still lacking in the mod in order to have a 1.0 release.

  • Technical:
    • Spawners ( #6 )
    • Jobs ( #7 )
    • Places ( #1 )
    • Actions:
      • Add actions to place and dig nodes
    • Schedules:
      • Add ability to override some of the NPC's attributes (like trader status)
      • Add check function
      • Enqueue actions related to finding specific nodes
      • Automatic calculation of certain parameters
  • Content:
    • Textures: add at least 3-5 more textures for males and females, including children.
    • Dialogues: add at least a 100 dialogue lines that are generic enough
    • Prices: add prices for all default items
    • Names: add at least 50 more names for both males and females
    • Relationships: add at least 10 more unique liked items and disliked item for each phase

NPC lies down repeatedly.

The npc lies down repeatedly while the hour 22 does not pass.

npc.add_schedule_entry(self, "generic", day, 7, nil, {
	-- Get out of bed
	[1] = {
		task = npc.actions.cmd.USE_BED, args = {
			pos = npc.places.PLACE_TYPE.BED.PRIMARY,
			action = npc.actions.const.beds.GET_UP
		}
	},
	-- Allow mobs_redo wandering
	[2] = {
		action = npc.actions.cmd.FREEZE, args = {freeze = false}
	}
})
	
npc.add_schedule_entry(self, "generic", day, 22, nil, {
	-- Use bed
	[2] = {
		task = npc.actions.cmd.USE_BED, args = {
			pos = "bed_primary",
			action = npc.actions.const.beds.LAY
		}
	},
	-- Stay put on bed
	[3] = {
		action = npc.actions.cmd.FREEZE, args = {freeze = true}
	}
})

Crash on function.

2017-10-24 18:54:14: ERROR[Main]: ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod 'advanced_npc' in callback luaentity_Step(): ...ll30/.minetest/mods/advanced_npc/actions/actions.lua:967: attempt to index field 'move_state' (a nil value)
2017-10-24 18:54:14: ERROR[Main]: stack traceback:
2017-10-24 18:54:14: ERROR[Main]: ...ll30/.minetest/mods/advanced_npc/actions/actions.lua:967: in function <...ll30/.minetest/mods/advanced_npc/actions/actions.lua:890>
2017-10-24 18:54:14: ERROR[Main]: (tail call): ?
2017-10-24 18:54:14: ERROR[Main]: /home/dell30/.minetest/mods/advanced_npc/npc.lua:791: in function 'execute_action'
2017-10-24 18:54:14: ERROR[Main]: /home/dell30/.minetest/mods/advanced_npc/npc.lua:1457: in function </home/dell30/.minetest/mods/advanced_npc/npc.lua:1366>
2017-10-24 18:54:14: ERROR[Main]: (tail call): ?
2017-10-24 18:54:14: ERROR[Main]: /home/dell30/.minetest/mods/mobs_redo-master/api.lua:2573: in function </home/dell30/.minetest/mods/mobs_redo-master/api.lua:2515>

Add npc name to initialize

I want to choose names from a specific list for an npc. And do not be based simply on gender.
It is possible? If it is not I want to suggest.
I am now using:
self.npc_name = names[math.random(1, #names)]
But I believe this may be unsafe in the future. If initialization needs.

Walking to a position is not reliable

NPCs usually get lost on their way to a specific position. This isn't due to pathfinding; this is probably due to lag and the unreliability of using speed to walk one exactly from one node to the other.

There is need to add either:

  1. A position correction mechanism, where the NPC is moved to the expected position; this causes the NPC to teleport small distances, would provide correct positions, but doesn't looks good, or,
  2. Position based moving, where instead of changing direction every interval and expect the NPC to be at the right position, wait until the NPC is at the correct position to move it to the next.

Talking about programs

I'd like to talk about how programs work.

Programs

The API follows an OS like model where "programs" are registered. Programs execute
"instructions" and when a program is run it is put into a process queue
(which is handled by a process scheduler that runs every 1 second).
There is also the ability to interrupt processes in which the process state is stored.
Also, processes have a variable storage if they need them.

Methods

  • npc.programs.register(program_name, func): Register a program
  • npc.programs.is_registered(program_name): Check if a program exists
  • npc.programs.execute(luaentity, program_name, {program arguments}): Execute a program for a NPC

Program definition

{
    program_name = "modname:program1", -- Programs name
    
    arguments = {program arguments}, -- Arguments table for the program
    
    is_state_program = true, --[[
        ^ [OPTIONAL]
        ^ If this is true, then this program will be 
          repeated while there is no next program]]
    
    interrupt_options = {} -- [OPTIONAL] ???
    
    depends = {}, -- [OPTIONAL] ???
    
    chance = 75, -- [OPTIONAL] ???
}

This is part of the documentation that deals with programs. I would like @hkzorman to do a review and answer some questions.

The method npc.programs.execute does not especify some program definitions like depends and chance. That's true?

How do ??? definitions work?

Error with nil on_rightclick

I do not understand why this happened.
Minetest 0.5.0
Branch clean_api

8-06-21 00:01:03: [Main]: [advanced_npc] INFO: Successfully registered program '"sunos:interagir_mobilia"'
2018-06-21 00:01:03: [Main]: [advanced_npc] INFO: Successfully registered program '"sunos:walk_to_checkin"'
2018-06-21 00:01:03: [Main]: [advanced_npc] INFO: Successfully registered occupation with name: "sunos_npc_caseiro_lojista"
2018-06-21 00:01:03: [Main]: [advanced_npc] WARNING: Attempt to register an existing texture with filename: "sunos_npc_male.png"
2018-06-21 00:01:03: [Main]: [advanced_npc] WARNING: Attempt to register an existing texture with filename: "sunos_npc_female.png"
2018-06-21 00:01:03: [Main]: [advanced_npc] INFO: Successfully registered occupation with name: "sunos_npc_caseiro"
2018-06-21 00:01:04: ACTION[Main]:         .__               __                   __   
2018-06-21 00:01:04: ACTION[Main]:   _____ |__| ____   _____/  |_  ____   _______/  |_ 
2018-06-21 00:01:04: ACTION[Main]:  /     \|  |/    \_/ __ \   __\/ __ \ /  ___/\   __\
2018-06-21 00:01:04: ACTION[Main]: |  Y Y  \  |   |  \  ___/|  | \  ___/ \___ \  |  |  
2018-06-21 00:01:04: ACTION[Main]: |__|_|  /__|___|  /\___  >__|  \___  >____  > |__|  
2018-06-21 00:01:04: ACTION[Main]:       \/        \/     \/          \/     \/        
2018-06-21 00:01:04: ACTION[Main]: World at [[censored]/minetest 0-5-0/bin/../worlds/soc 01]
2018-06-21 00:01:04: ACTION[Main]: Server for gameid="minetest" listening on 0.0.0.0:30000.
2018-06-21 00:01:07: ACTION[Server]: 888 [127.0.0.1] joins game. 
2018-06-21 00:01:07: ACTION[Server]: 888 joins game. List of players: 888
2018-06-21 00:01:08: [Server]: [advanced_npc] INFO: Corrected position for walking NPC "Trunko"
2018-06-21 00:01:09: [Server]: [advanced_npc] INFO: Corrected position for walking NPC "Trunko"
2018-06-21 00:01:09: ERROR[Main]: ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod 'sunos' in callback luaentity_Step(): ...npc/executable/instructions/builtin_instructions.lua:675: attempt to call field 'on_rightclick' (a nil value)
2018-06-21 00:01:09: ERROR[Main]: stack traceback:
2018-06-21 00:01:09: ERROR[Main]: 	...npc/executable/instructions/builtin_instructions.lua:675: in function <...npc/executable/instructions/builtin_instructions.lua:663>
2018-06-21 00:01:09: ERROR[Main]: 	(tail call): ?
2018-06-21 00:01:09: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1376: in function 'execute'
2018-06-21 00:01:09: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1408: in function 'execute'
2018-06-21 00:01:09: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1461: in function 'execution_routine'
2018-06-21 00:01:09: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:2189: in function <...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:2121>
2018-06-21 00:01:09: ERROR[Main]: 	(tail call): ?
2018-06-21 00:01:09: ERROR[Main]: 	...de Mods/minetest 0-5-0/bin/../mods/mobs_redo/api.lua:2579: in function <...de Mods/minetest 0-5-0/bin/../mods/mobs_redo/api.lua:2522>
2018-06-21 00:01:09: ACTION[Server]: 888 leaves game. List of players: 
Loaded texture: [censored]/minetest 0-5-0/textures/base/pack/menu_header.png
Loaded texture: [censored]/minetest 0-5-0/games/minetest_game/menu/header.png

Fix code debugger

You need to reduce tons of messages in the debugger.
You can add a control for development or just remove permanently.

walk_to_pos does not walk to an exact position

walk_to_pos program does not walk to an exact position, even using the optimize_one_node_distance=false parameter.
This only happens when the NPC needs to go to the next coordinate. When that happens the NPC does not walk, it just rotates.
Branch clean_api
Minetest 0.5.0-dev
Mod test: sociedades
In the picture, the NPC should be sideways, but it just rotated to interact.
bug de rotacao

NPCs do not schedule after first spawn

After an NPC is spawned for the first time, it remains stopped, and after restarting the world it starts functioning normally.

terminal

2018-04-27 12:49:57: ACTION[Server]: Object clearing done.
2018-04-27 12:49:57: [Server]: [advanced_npc] INFO: Initializing NPC at (8574,9.5,9274)
2018-04-27 12:49:57: [Server]: [advanced_npc] INFO: Successfully initialized NPC with name "Luis", sex: male, is child: nil, texture: {
	"sunos_npc.png"
}
2018-04-27 12:49:57: [Server]: [advanced_npc] INFO: Initializing NPC at (8592,9.5,9269)
2018-04-27 12:49:57: [Server]: [advanced_npc] INFO: Successfully initialized NPC with name "Alexis", sex: male, is child: nil, texture: {
	"sunos_npc.png"
}
2018-04-27 12:49:58: [Server]: Executing scheduler for NPC "Luis"
2018-04-27 12:49:58: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:49:58: [Server]: Executing scheduler for NPC "Alexis"
2018-04-27 12:49:58: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:49:59: [Server]: Executing scheduler for NPC "Luis"
2018-04-27 12:49:59: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:49:59: [Server]: Executing scheduler for NPC "Alexis"
2018-04-27 12:49:59: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:50:00: [Server]: Executing scheduler for NPC "Luis"
2018-04-27 12:50:00: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:50:00: [Server]: Executing scheduler for NPC "Alexis"
2018-04-27 12:50:00: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:50:01: [Server]: Executing scheduler for NPC "Luis"
2018-04-27 12:50:01: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:50:01: [Server]: Executing scheduler for NPC "Alexis"
2018-04-27 12:50:01: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-27 12:50:02: [Server]: Executing scheduler for NPC "Luis"

go_to_checkin program

This programs send NPC for a location of (maybe non-loaded) of the world.
This can be useful to NPC walk in a city.

The program consists of repeating this instruction over and over again.
I hope the API does not stop the server if the NPC goes to an inactive chunk of the world.
instrucao go_to_check 2

Mods using this API

I noticed that the latest version of this mod doesn't include any entities. What mods use this API?

Execution Diagram

I am trying to draw a diagram that exemplifies the whole operation of the NPC.
sketchboard
Can you explain how to insert a program into the instruction flow of another program? Show an example.

Findpath not work

This simply never works, in any setting.
Minetest version: 0.5.0-DEV

Extend pathfinding to be able to go up and down inside houses

Currently, pathfinding and NPC movements are restricted to a 2D plane. This is due to:

  1. The pathfinding algorithm in the Jumper library uses only 2D planes.
  2. The villages in Sokomine's mg_villages mod have a flat terrain

However, the houses spanwed by the mg_villages mod usually have a second floor and sometimes even a third. While the first floor of the houses has usually many things that are useful and the NPCs will be able to do well only using the first floor, the second floor usually has more living space, so using it can increase the NPC population or make a more logical use of the house.

NPC not initialized when created from inventory, leads to crash

To reproduce:

  • Type: /giveme advanced_npc:npc
  • Select it in the inventory and right click somewhere, to place it in the world.
  • Right click the NPC.

It immediately crashes, complaining about a nil relationships field.

Apparently, that's because the spawn_npc function isn't executed in that code path. It would be nice if it was, allowing creators to place NPCs where desired.

Problem with actions argument of npc.add_schedule_entry method

I believe the syntax is not correct, or maybe the API uses the same term actions to refer to two different things.

function npc.add_schedule_entry(self, schedule_type, date, time, check, actions)

1º case - actions is a table with some action aguments. In this case, the table must contain some values such as min_count and max_count for example.
2º case - actions is one of the arguments of the first case.

After all, what is the correct argument for actions? actions or actions?

character model compatibility

I've noticed that the API applies settings in the character.b3d model to work with the model provided by the player_api mod (minetest.0.5.0). Is it possible to create a compatibility to work with minetest 0.4.16?

Erro: Index field 'actions' (a nil value)

I do not know if I'm doing this correctly, I just tried to follow what the documentation says.

My npc do_custom:

do_custom = function(self, dtime)
		if not self.testou and minetest.find_node_near(self.object:getpos(), 3, {"beds:bed_bottom"}) then
			self.testou = true
			npc.add_task(self, npc.actions.cmd.USE_BED, {
				pos = minetest.find_node_near(self.object:getpos(), 3, {"beds:bed_bottom"}),
				action = npc.actions.cmd.LAY,
			})
		end
	end,

This is what happens when I put a bed next to it.

2017-10-07 19:23:32: ERROR[Main]: ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod 'mobs_npc' in callback luaentity_Step(): ...ods/minetest_0-4-16/bin/../mods/advanced_npc/npc.lua:731: attempt to index field 'actions' (a nil value)
2017-10-07 19:23:32: ERROR[Main]: stack traceback:
2017-10-07 19:23:32: ERROR[Main]: 	...ods/minetest_0-4-16/bin/../mods/advanced_npc/npc.lua:731: in function 'add_task'
2017-10-07 19:23:32: ERROR[Main]: 	...e Mods/minetest_0-4-16/bin/../mods/mobs_npc/igor.lua:115: in function 'do_custom'
2017-10-07 19:23:32: ERROR[Main]: 	...e Mods/minetest_0-4-16/bin/../mods/mobs_redo/api.lua:2573: in function <...e Mods/minetest_0-4-16/bin/../mods/mobs_redo/api.lua:2515>

NPC does not stand still

I am only try maintain a NPC sleeping (by 10 seconds). He just sits on the bed and gets up. (Apparently)

do_custom = function(self, dtime)	

	-- Inicialize NPC
	if self.initialized == nil then
		npc.initialize(self, self.object:getpos(), true)
		self.tamed = false
		self.owner = nil
	end

	-- Action queue timer
	-- Check if actions and timers aren't locked
	if self.actions.action_timer_lock == false then
		-- Increment action timer
		self.actions.action_timer = self.actions.action_timer + dtime
		if self.actions.action_timer >= self.actions.action_interval then
			-- Reset action timer
			self.actions.action_timer = 0
			-- Check if NPC is walking
			if self.actions.walking.is_walking == true then
				-- Move NPC to expected position to ensure not getting lost
				local pos = self.actions.walking.target_pos
				self.object:moveto({x=pos.x, y=pos.y, z=pos.z})
			end
			-- Execute action
			self.freeze = npc.execute_action(self)
			-- Check if there are still remaining actions in the queue
			if self.freeze == nil and table.getn(self.actions.queue) > 0 then
				self.freeze = false
			end
		end
	end

	-- Lay in the bed
	if not self.tested and minetest.find_node_near(self.object:getpos(), 3, {"beds:bed_bottom"}) then
		self.tested = true
		npc.add_task(self, npc.actions.cmd.USE_BED, {
			pos = minetest.find_node_near(self.object:getpos(), 3, {"beds:bed_bottom"}),
			action = npc.actions.const.beds.LAY,
		})
		npc.add_action(self, npc.actions.cmd.SET_INTERVAL, {
			interval = 10,
			freeze=true,
		})
	end
end,

Problem with walk_to_pos (node_pos var)

2018-06-11 17:10:39: ERROR[Main]: ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod 'sunos' in callback luaentity_Step(): ...Mods/minetest 0-5-0/bin/../builtin/common/vector.lua:66: attempt to index local 'pos2' (a nil value)
2018-06-11 17:10:39: ERROR[Main]: stack traceback:
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../builtin/common/vector.lua:66: in function 'direction'
2018-06-11 17:10:39: ERROR[Main]: 	...nced_npc/executable/programs/builtin/walk_to_pos.lua:48: in function <...nced_npc/executable/programs/builtin/walk_to_pos.lua:12>
2018-06-11 17:10:39: ERROR[Main]: 	(tail call): ?
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:965: in function 'execute_process'
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1094: in function 'interrupt'
2018-06-11 17:10:39: ERROR[Main]: 	...npc/executable/instructions/builtin_instructions.lua:81: in function <...npc/executable/instructions/builtin_instructions.lua:76>
2018-06-11 17:10:39: ERROR[Main]: 	(tail call): ?
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1376: in function 'execute'
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1408: in function 'execute'
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:1461: in function 'execution_routine'
2018-06-11 17:10:39: ERROR[Main]: 	...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:2189: in function <...Mods/minetest 0-5-0/bin/../mods/advanced_npc/npc.lua:2121>
2018-06-11 17:10:39: ERROR[Main]: 	(tail call): ?
2018-06-11 17:10:39: ERROR[Main]: 	...de Mods/minetest 0-5-0/bin/../mods/mobs_redo/api.lua:2579: in function <...de Mods/minetest 0-5-0/bin/../mods/mobs_redo/api.lua:2522>

This variable node_pos appears to return null when end_pos argument is a table.
I immediately tried that include line

if not node_pos then node_pos = end_pos end

The error has stopped, but the NPC always looks in a steady direction (in this case, to the back.) after walking to a position, I do not know if this is normal.

Add method for Action queue timer

This control is required for the API to control the NPC (in on_step callback).

-- Action queue timer
-- Check if actions and timers aren't locked
if self.actions.action_timer_lock == false then
	-- Increment action timer
	self.actions.action_timer = self.actions.action_timer + dtime
	if self.actions.action_timer >= self.actions.action_interval then
		-- Reset action timer
		self.actions.action_timer = 0
		-- Check if NPC is walking
		if self.actions.walking.is_walking == true then
			-- Move NPC to expected position to ensure not getting lost
			local pos = self.actions.walking.target_pos
			self.object:moveto({x=pos.x, y=pos.y, z=pos.z})
		end
		-- Execute action
		self.freeze = npc.execute_action(self)
		-- Check if there are still remaining actions in the queue
		if self.freeze == nil and table.getn(self.actions.queue) > 0 then
			self.freeze = false
		end
	end
end

Could this be simplified for final developers with a simple method? (eg npc.action_queue_timer(luaentity, dtime))

You may need to process other things in the future, so you can use a more comprehensive syntax method (eg npc.on_step(luaentity, dtime))

Problem with paralyzed NPCs

I'm just doing initial testing to see the changes. I place a bed and a npc next door, but it seems he does not want to sleep. It gets paralyzed only. After a few tries I just get this message in the debugger.

Debug

2018-04-17 11:55:36: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:55:37: [Server]: [advanced_npc] INFO: Initializing NPC at (16,10,-43)
2018-04-17 11:55:37: [Server]: [advanced_npc] INFO: Successfully initialized NPC with name "Kiko", sex: male, is child: nil, texture: {
	"mobs_igor5.png"
}
2018-04-17 11:55:37: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:55:37: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:55:37: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:55:37: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:55:38: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:55:38: [Server]: [advanced_npc] INFO: Current process queue size: 0
2018-04-17 11:55:38: [Server]: [advanced_npc] INFO: NPC "Kiko" is executing: nil
2018-04-17 11:55:38: [Server]: [advanced_npc] INFO: NPC "Kiko" is executing: nil
2018-04-17 11:55:38: [Server]: [advanced_npc] ERROR: Attempted to execute program with name nil.
Program doesn't exists.
2018-04-17 11:55:59: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:00: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:56:00: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:00: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:56:00: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:00: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:56:00: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:00: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:56:01: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:01: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:56:01: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:01: [Server]: [advanced_npc] INFO: Current process queue size: 1
2018-04-17 11:56:01: [Server]: Executing scheduler for NPC "Kiko"
2018-04-17 11:56:01: [Server]: [advanced_npc] INFO: Current process queue size: 1

register npc mobs redo

on_spawn = function(self)
		if self.initialized == nil then
			
			-- Initialize
			npc.initialize(self, self.object:getpos(), true)
			self.tamed = false
	
			-- Location
			local bed = minetest.find_node_near(self.object:getpos(), 6, {"beds:bed_bottom"})
			if bed then
				local v = minetest.facedir_to_dir(minetest.get_node(bed).param2)
				local acess = vector.subtract(bed, v)
				npc.locations.add_owned(self, "cama_1", "bed_primary", bed, acess)
			else
				self.object:remove()
				minetest.chat_send_all("bed not found")
			end
			
			minetest.chat_send_all("set schedule")
			
			npc.schedule.create(self, "generic", 0)
			
			npc.schedule.entry.set(self, "generic", 0, 22, nil, {
				[1] = {
					program_name = "advanced_npc:walk_to_pos",
					arguments = {
						end_pos = {place_type="bed_primary", use_access_node=true},
						walkable = {}
					},
					interrupt_options = {}
				},
				[2] = {
					program_name = "advanced_npc:use_bed",
					arguments = {
						pos = "bed_primary",
						action = npc.programs.const.node_ops.beds.LAY
					},
					interrupt_options = {
						allow_rightclick = false
					}
				},
				[3] = {
					program_name = "advanced_npc:idle",
					arguments = {
						acknowledge_nearby_objs = false,
						wander_chance = 0
					},
					interrupt_options = {
						allow_rightclick = false
					},
					is_state_program = true
				}
			})
		end
	end,
	
	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,

captura de tela de 2018-04-17 12-08-37

Add basic jobs

Whether simulated or implemented for real, the following basic jobs should be added and assigned to NPC depending on the type of building the live on or is close by.

  • Farmer
  • Woodcutter
  • Miner
  • Cooker
  • Priest
  • Bartender/server
  • Smith

Elaborating on "simulated" jobs, means that the NPC will get random number of items that would get if actually doing the job. Example: A farmer NPC may wander the field for 4-6 Minetest hours and be assigned randomly 10-15 seeds, and 10-15 crops, as opposed to the NPC actually harvesting, getting seeds, etc.

This is the job/occupation implementation check list:

  • Add spawner functionality to detect nearby building types
  • NPC: Allow choosing random textures from a given texture list
  • Add spawner functionality to choose number of NPC for occupations for building
  • Implement occupation initialization function
  • Implement schedule "check" or "query" function

Jumper library causes issues with Minetest's mod security

Basically, Jumper library's structure uses require() function which is blocked by Minetest when mod security is enabled.

An alternative would be to replace Jumper library with a simple A* star implementation that can be used by Lua code without calling require().

Crash finding "ignore" nodes on actions.use_bed()

This bug may also affect other somilar functions that do minetest.get_node().
The issue apparently happens when mapblocks are unloaded and NPCs are performing actions near them.
A possible fix is to just return when finding ignore nodes as there's not much else that can be done.

However all locations where this might happen has to be identified.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.