523 lines
19 KiB
Markdown
523 lines
19 KiB
Markdown
# People Mod
|
|
|
|
NOTE: Not currently compatible with stock minetest. Requires at least
|
|
the current git version, plus these:
|
|
|
|
* https://github.com/CiaranG/minetest/commit/740f057253495633db4cc53c905d4ea4ff1167c8
|
|
* https://github.com/CiaranG/minetest/commit/7b7e8bd4b924b74271eb26b30fa681eb8bd780c5
|
|
* https://github.com/CiaranG/minetest/commit/8eb489ece109e56c4d0fa01a0b956f7fa9f0ab56
|
|
* https://github.com/CiaranG/minetest/commit/8772041b43b78c86bc32e5682391286bca498848
|
|
* https://github.com/CiaranG/minetest/commit/f5b73e0baabbfe68bc6a9a52c5221930e4306ed2
|
|
* https://github.com/CiaranG/minetest/commit/45dc321b3c48f449bac5f9985db587847490d3e2
|
|
* https://github.com/CiaranG/minetest/commit/830f306d0800e1e504174799c410e75a35687f1b
|
|
* https://github.com/CiaranG/minetest/commit/294d98c3783c1c1f5abe05e509982ba535f60b46
|
|
* https://github.com/CiaranG/minetest/commit/db67b29f925a477a33ebe6d784fccab332214952
|
|
* https://github.com/CiaranG/minetest/commit/e0ae57fdb44a5dd4a542677e2513793962ae5b3d
|
|
* https://github.com/CiaranG/minetest/commit/638a246de7e7709eb0d5ec5485a4782f53c10fc0
|
|
|
|
This mod provides 'people' (non player characters) that are programmable
|
|
in-game. Each NPC's Lua code runs inside a secure sandbox for that NPC only.
|
|
|
|
Author: Ciaran Gultnieks
|
|
License: LGPL
|
|
|
|
Includes lua sandbox setup code from mesecons by Jeija (also LGPL), and inspiration
|
|
from the npcf mod, and (obviously!) the mesecons lua controller design.
|
|
|
|
The character model is a modified version of the default player model from
|
|
the default minetest game.
|
|
|
|
## Requirements
|
|
|
|
Optionally, you might want these mods:
|
|
* areas (my fork of it) - https://github.com/CiaranG/areas - for being able to
|
|
specify an area as a destination
|
|
* footpath - https://gitlab.com/CiaranG/moddebug - for full route finding and
|
|
navigation support
|
|
* moddebug - https://gitlab.com/CiaranG/moddebug - for debug logging
|
|
|
|
## Usage
|
|
|
|
To create a person, issue this command:
|
|
|
|
/people create Bob
|
|
|
|
This places a person called "Bob" at your current location.
|
|
|
|
Then, right-click it at any time to program it. For now, until I think
|
|
of a better name, we'll refer to the code you enter here as "the person's
|
|
lua code"!
|
|
|
|
## The Person's Lua Code
|
|
|
|
This code, as entered in the right-click dialog for a person you own, is
|
|
really an event handler. It runs in a sandbox, and when called will always
|
|
have a variable called 'event'. This is a table, and the "type" field
|
|
determines the type of event that caused the call.
|
|
|
|
Here is a very simple example:
|
|
|
|
```lua
|
|
if event.type == "program" then
|
|
action = {"follow", name=event.programmer}
|
|
end
|
|
```
|
|
|
|
To enter this, select "Custom" from the Preset dropdown, and type in the code.
|
|
Then, when you hit the Program button, the person will start following
|
|
the player who pressed the button and keep doing that until they log off. Because
|
|
the code does nothing in response to an "act" event, at that point they will go
|
|
into a permanent wait (sleep) state.
|
|
|
|
### Sandbox
|
|
|
|
The code runs in a sandboxed Lua environment. Only a limited range of
|
|
functionality is available, and in particular things like loops and functions
|
|
are prohibited.
|
|
|
|
A range of standard string, table and math library functions are made
|
|
available, as well as the built in minetest vector helpers.
|
|
|
|
In addition:
|
|
|
|
* `log(msg)` - logs a v1 debug message
|
|
* `mem` - the person's memory - see under Persistence
|
|
* `carrying(item)` - returns the number of a particluar item the person is
|
|
carrying - e.g. carrying("default:cobble") might return 0, 10, 99 or 198.
|
|
|
|
### Persistence
|
|
|
|
The sandboxed Lua environment persists for as long as the person is active.
|
|
In addition to saving setup time on each call, this allows 'local' (actually
|
|
global to the sandboxed environment) variables persist between calls for
|
|
different events.
|
|
|
|
Information that needs to persist longer than this (i.e. across deactivations
|
|
or server restarts) can be stored in the person's "memory". The 'mem' table
|
|
(initially empty) can be used to store anything required. It will persist for
|
|
the entire lifetime of the person.
|
|
|
|
### Presets
|
|
|
|
Several 'presets' are supplied, which serve as both examples and useful
|
|
functionality. When editing, select one of the presets from the dropdown and that
|
|
preset will be used. If you subsequently select "Custom", the code from the
|
|
previously selected preset will be loaded as a base for further editing.
|
|
|
|
A brief description of these:
|
|
|
|
####FollowOwner
|
|
When programmed, follows the programmer until they log off
|
|
|
|
####SimpleCommands
|
|
Responds to simple commands. Some examples:
|
|
|
|
* `/tell Bob come here`,
|
|
* `/tell Bob go to Sugar Harbour`
|
|
* `/tell Bob follow me`
|
|
* '/tell Bob drop 23 Stone`
|
|
* '/tell Bob drop default:cobble`
|
|
Look at the code for more things it can do.
|
|
|
|
####RouteWalker
|
|
Follows a preset route (hard-coded, so you would want to edit it) indefinitely.
|
|
|
|
## Events
|
|
|
|
The following event types are received by the person's lua code:
|
|
|
|
### act
|
|
|
|
This happens when the person has no current action (see the Actions section).
|
|
The handler should select a new action, by setting the 'action' variable. If
|
|
it fails to do so, the person will do an automatic wait.
|
|
|
|
### actfail
|
|
|
|
This happens when an action fails, unless there is a 'prevaction' set. (To
|
|
get a fail, but still have a follow-on action, use 'successaction' instead!)
|
|
|
|
There is also event.failedaction available, which is the action that actually
|
|
failed, and event.failcode which is the failure code that the action returned.
|
|
That is normally just false, but some action handlers can return special
|
|
values here (anything non-true would be a fail) to indicate particular
|
|
things.
|
|
|
|
### tell
|
|
|
|
This happens when a player (who must be the owner, current) sends a message
|
|
to the person (e.g. via the 'people tell' chat command). It can be used to
|
|
respond to commands. The message is in event.message and the name of the
|
|
sender is in event.sender, and their position is in event.senderpos.
|
|
|
|
### step
|
|
|
|
This happens only when the "step" action is running. It's called every server
|
|
step, with event.dtime containing the time since the last step. The handler
|
|
should update the speed and yaw variables, or set a new action.
|
|
|
|
### program
|
|
|
|
This happens when the lua code has been edited. The name of the player that
|
|
did the programming is passed as event.programmer.
|
|
|
|
### activate
|
|
|
|
This happens when the entity is activated (either it's brand new, or is in
|
|
a block that was unloaded but is now loaded). It is called with event.dtime_s
|
|
containing the time since it was last active.
|
|
|
|
### punched
|
|
|
|
This happens when a player punches the entity. The name of the offending player
|
|
is sent as event.puncher.
|
|
|
|
## Actions
|
|
|
|
Actions are the basic building blocks of object behaviour. A person always
|
|
has a current action which they are completing, the execution of which is
|
|
handled by the mod. The job of the lua programming within the person is to
|
|
sit at a higher level of 'conciousness' and select the appropriate actions.
|
|
|
|
The person's lua code receives an "act" event when it should select an
|
|
action - usually when the current action completes. It can also set/replace
|
|
the current action in response to any other event. Setting the action is
|
|
a simple case of setting the 'action' variable within the lua code.
|
|
|
|
An action is defined by a table, where the first element [1] is the ID of
|
|
the action, and additional named fields contain parameters. For a simple
|
|
example, {"go", pos={5,0,5}} is an action to move the person to the given
|
|
position.
|
|
|
|
### Action types
|
|
|
|
The following actions are currently defined:
|
|
|
|
#### go
|
|
|
|
Go (walk) to a location. This generally decomposes into a more complex set
|
|
of actions, the ultimate aim of which is to get to the given destination
|
|
somehow.
|
|
|
|
One of the following parameters is needed:
|
|
|
|
* pos={x,y,z} - specifies an absolute position to go to
|
|
* rel={x,y,z} - to go a relative offset from the current position
|
|
* name=string - specifies a footpath node or area (see the Navigation
|
|
section
|
|
|
|
Many other parameters are handled by the go action, but these are used
|
|
internally only.
|
|
|
|
#### follow, with name="playername"
|
|
Follows the given player indefinitely (or at least until they log off).
|
|
|
|
#### face, with pos={x,y,z} or yaw=angle
|
|
Standing still, face towards the given position, or in the given direction.
|
|
|
|
#### wait, with time
|
|
Do nothing for that many seconds. This may allow the entity to be deactivated
|
|
and unloaded, in which case it will be reactivated at the appropriate time.
|
|
Short waits may not result in deactivation.
|
|
|
|
#### drop, with item="name", count=n
|
|
Drop the given item. The itemspec is either the full node name (e.g.
|
|
"default:stone_brick") or the description as seen in-game, which is
|
|
case insensitive (e.g. "stone brick").
|
|
|
|
Optionally, count can specify the number to drop. Defaults to one if
|
|
not specified.
|
|
|
|
#### dig, with pos={x,y,z}
|
|
Dig the node at the given position. Must be within range.
|
|
|
|
#### rightclicknode, with pos={x,y,z}
|
|
Right-click the node at the given position. Must be within range.
|
|
|
|
#### place
|
|
Places an item (node). Must be in the inventory, and in range.
|
|
|
|
* Required parameter: pos={x,y,z}
|
|
* Required parameter: item=itemname
|
|
* Optional parameter: param2=value (e.g. for setting facedir)
|
|
* Optional parameter: againstdir={x,y,z}
|
|
|
|
#### stash
|
|
|
|
* Required parameter: items={item1, ..., itemn"}
|
|
* Optional parameter: pos={x,y,z} - position of node to stash in
|
|
* Optional parameter: dest="node" - node name to stash in
|
|
(e.g. "default:chest")
|
|
|
|
(You need either pos or dest, or nothing will happen)
|
|
|
|
Places items into a destination node, which should be something like a chest.
|
|
Specifically, it needs to be a node with a "main" inventory list. So for a
|
|
normal chest, use "default:chest" for the nodename.
|
|
|
|
Each item in the list can be:
|
|
* Just the item name (e.g. "default:cobble") to put all held in
|
|
* Item name and count (e.g. "default:cobble 50") to put that many in
|
|
* Table ({"default:cobble", keep=5}) to keep 5 but put the rest in
|
|
|
|
#### retrieve
|
|
|
|
Gets items from a source node, which should be something like a chest.
|
|
Specifically, it needs to be a node with a "main" inventory list. So for a
|
|
normal chest, use "default:chest" for the nodename.
|
|
|
|
* Required parameter: items={item1, ..., itemn"}
|
|
* Optional parameter: pos={x,y,z} - position of node to stash in
|
|
* Optional parameter: src="node" - node name to retrieve from
|
|
(e.g. "default:chest")
|
|
|
|
(You need either pos or dest, or nothing will happen)
|
|
|
|
The items in the list are defined in the same way as for the stash action.
|
|
|
|
#### sell
|
|
|
|
* Required parameter: item="itemstring"
|
|
|
|
Need to be standing within range of a shop (barrel mod) that buys the item
|
|
in question.
|
|
|
|
The entire amount of the item carried will be sold, assuming the shop can
|
|
afford to buy it all.
|
|
|
|
#### buy
|
|
|
|
* Required parameter: item="itemname"
|
|
* Required parameter: num=amount
|
|
|
|
Need to be standing within range of a shop (barrel mod) that sells the item
|
|
in question, and have enough money. If the person doesn't have enough money
|
|
to buy the amount specified, a lesser amount may be bought instead.
|
|
|
|
#### obtain
|
|
|
|
Meta-action to obtain some of an item by whatever means possible. This could
|
|
use crafting, buying, mining, whatever, the only aim is to get it.
|
|
|
|
The specified amount is the total amount we want to end up having - so if we
|
|
already have that many, the action will not do anything. Otherwise, it could
|
|
use one or more of the available methods until the total is achieved.
|
|
|
|
* Required paramter: item="itemname"
|
|
* Required parameter: num=amount
|
|
|
|
#### craft
|
|
|
|
Craft an item.
|
|
|
|
* Required paramter: item="itemname"
|
|
* Required parameter: num=amount
|
|
|
|
#### gather
|
|
Changes current gathering settings.
|
|
|
|
All parameters are optional:
|
|
|
|
* `nodes = {}` - list of nodes to dig
|
|
* `topnodes = {}` - list of nodes to dig only if there is a) air above,
|
|
and b) the same node below - think of harvesting
|
|
cactus/papyrus while still allowing them to grow back!
|
|
* `items = {}` - list of items to pick up
|
|
* `plant = nil - seed to plant
|
|
* animal = nil - species of animal to harvest from
|
|
* animal_wield = nil - item to wield when harvesting
|
|
|
|
So calling with no parameters at all turns off gathering.
|
|
|
|
Gathering remains in effect until it is switched off, or until the person
|
|
is re-programmed. Gathering actually happens during "go" and "wait" actions
|
|
only (but this includes any action that uses "go" as a subaction).
|
|
|
|
Both planting and gathering happen in a square +/- 3m around the person.
|
|
This means a good farm arrangement is plots that are 7x7, with a (covered)
|
|
water source in the middle, where the person stands to plant and harvest.
|
|
|
|
#### attackplayer
|
|
|
|
Attack a player. Parameters:
|
|
|
|
* `name = "name"` - name of player to attack
|
|
|
|
The action will cancel when the player is out of range.
|
|
|
|
#### pickup
|
|
|
|
Picks up an item. Parameters:
|
|
|
|
* `pos = {x,y,z}` - position of the item (required)
|
|
* `item = "name"` - name of item, e.g. "default:dirt"
|
|
|
|
#### harvest
|
|
|
|
Harvest from an animal from the animals mod. Parameters:
|
|
|
|
* name = "name" - the name of the animal (e.g. "cow 1452")
|
|
|
|
The correct item should already be wielded. This is normally used in
|
|
conjunction with gathering, in which case the wielding is already
|
|
handled.
|
|
|
|
#### tunnel
|
|
|
|
Tunnel, from the current position. Also builds bridges!
|
|
|
|
Parameters:
|
|
|
|
* `dir = {x,y,z}` - direction - either x or z must be 0. and the
|
|
other +/-1, while y can be 0 or +/-1.
|
|
* 'distance = n` - distance to go for (reduces as the action
|
|
proceeds)
|
|
* `floor=item` - material to use for floor - optional
|
|
* `wall=item` - material to use for wall linings - optional
|
|
* `ceiling=item` - material to use for ceiling - optional
|
|
* `steps=item` - material to use for steps, on slopes.
|
|
* `lighting=item` - material (most likely default:torch) to use for
|
|
lighting, which is placed against walls at
|
|
4 metre intervals. Optional.
|
|
* `support_top=item` - material to use for top supports, which are placed
|
|
against the ceiling at 8 metre intervals. Optional.
|
|
* `support_ceiling=item` - like support_top, but at ceiling level, not below.
|
|
* `support_side=item` - material to use for side supports. Optional.
|
|
* `support_side_facein=? - set to true to make the side supports face in
|
|
towards the tunnel centre. (Try it with slabs, for
|
|
example!)
|
|
* `support_wall=item` - like support_side, but at wall level, not within.
|
|
|
|
#### building
|
|
|
|
Make a building, from current position.
|
|
|
|
The current position is designated as the entrance to the building.
|
|
|
|
Parameters:
|
|
|
|
* `dir = {x,y,z}` - direction - either x or z must be 0. and the
|
|
other +/-1, while y can be 0 or +/-1.
|
|
* `width = n` - the width of the building
|
|
* `depth = n` - the depth of the building
|
|
* `height = n` - the height of the building
|
|
* `wall = item` - material to use for walls
|
|
* `outside_floor = item` - material to use for the floor outside the walls.
|
|
Optional. Does not include the entrance.
|
|
This can also be a table, with two entries, in
|
|
which case the floor can be either of those, and
|
|
if it's not it's replaced with the first. (Example
|
|
use case of that is if it's dirt, in which case
|
|
it can change to dirt_with_grass, and be left
|
|
alone!)
|
|
* `door=item` - Door to use. Doorways will be left open if not
|
|
specified.
|
|
|
|
#### step
|
|
Allow's the person's lua code to handle on_step itself. It will receive
|
|
"step" events (with dtime) and should reset the action when it no longer
|
|
needs to do this. It can update 'speed' and 'yaw' and these will be used.
|
|
|
|
### Nesting
|
|
|
|
Any action can have a "prevaction" field, which should be a valid action
|
|
itself. It will be set when the action has completed. This can be used to nest
|
|
actions.
|
|
|
|
The actions themselves may make use of this functionality. For example, the
|
|
follow action is implemented by repeatedly selecting positions near the target
|
|
player, and then replacing it self with a "go" action with the original follow
|
|
nested as a "prevaction" within it. Thus, when the "go" completes, the follow
|
|
resumes once more.
|
|
|
|
## Chat Commands
|
|
|
|
People can be managed and controlled via chat commands. Most of these commands
|
|
can be issued when a person is inactive - they will be automatically woken
|
|
(the unloaded block where they are sleeping will be loaded) and then the
|
|
command executed.
|
|
|
|
The following commands are available:
|
|
|
|
### /people help
|
|
Lists all the available commands.
|
|
|
|
### /people create `name`
|
|
Creates a new person, at your current location.
|
|
|
|
### /people delete `name`
|
|
Deletes a person.
|
|
|
|
### /people where `name`
|
|
Tells you the current location of a person.
|
|
|
|
### /people summon `name`
|
|
Moves the person to your current location.
|
|
|
|
### /people setowner `name` `playername`
|
|
Changes the owner of the person.
|
|
|
|
### /people tell `name` `message`
|
|
The person receives a "tell" event with the given message.
|
|
|
|
### /tell `name` `message`
|
|
Alias for /people tell, for convenience.
|
|
|
|
### /people status
|
|
Lists all people in the word, and their current status (active/inactive).
|
|
|
|
### /people skin `name` `skin_name|list`
|
|
Sets the skin of the person to the named one. (Or use 'list' to get a
|
|
list of available skins, which come from the skins mod).
|
|
|
|
### /people track `name`
|
|
Track the given person (location and distance away) on the HUD.
|
|
|
|
Use without a parameter to switch it off.
|
|
|
|
## Autonomy and Activation
|
|
|
|
People are 'autonomous' - i.e. they will continue to act even when no player
|
|
is nearby. Indeed, they will continue to act even when no player at all is
|
|
logged in to the server.
|
|
|
|
However, they are designed to be able to not remain active unless necessary.
|
|
This is mostly done by use of the "wait" action.
|
|
|
|
If you set a person to wait for 5 minutes, then assuming no player or other
|
|
active entity is nearby, the block the person is in (and surrounding blocks)
|
|
will be unloaded, and the person will cease to exist as far as the minetest
|
|
engine is concerned. However, after 5 minutes, this mod will reactivate it
|
|
by reloading the block.
|
|
|
|
### Force Loading
|
|
This mod uses the 'force loading' API (which is silly and inadequate) to
|
|
reactive sleeping entities when it needs to. It does not require the
|
|
persistence over server restarts of force-loaded blocks, and while it ought
|
|
to clean up any force loading it does almost immediately, that cannot happen
|
|
if there is a server crash or other unclean shutdown at the wrong moment.
|
|
You could mitigate this by always removing the force loaded blocks file from
|
|
the world directory on startup.
|
|
|
|
Ultimately it won't use this 'feature' at all.
|
|
|
|
## Footpaths and Navigation
|
|
|
|
People (in particular via the 'go' action) have an in-built understanding of
|
|
footpaths, which are managed via the footpath mod. This provides people with
|
|
the ability to do sensible long-range navigation as a player would.
|
|
|
|
When following a footpath, preference will always be given to the direction
|
|
currently being travelled in. The means that, for example, a cobblestone
|
|
footpath can cross a larger area 'paved' with cobblestone (or other footpath
|
|
materials), so long as the crossing is in a straight line.
|
|
|
|
If you tell a person to go to a named place (e.g. {"go", name="My House"}) they
|
|
will first look for a footpath junction with that name, and then also find the
|
|
nearest junction to where they are now. They will then navigate along the path
|
|
network from one to the other.
|
|
|
|
If there is no footpath junction with that name, they'll also look for an area
|
|
(areas mod) and head directly for the centre of that area.
|
|
|