Go to file
Ciaran Gultnieks eda0d51861 More wip 2015-05-24 10:25:51 +01:00
actions More wip 2015-05-24 10:25:51 +01:00
models Initial commit 2014-04-03 08:32:23 +01:00
presets More wip 2015-05-24 10:25:51 +01:00
textures Initial commit 2014-04-03 08:32:23 +01:00
README.md More wip 2015-05-24 10:25:51 +01:00
commands.lua More wip 2015-05-24 10:25:51 +01:00
depends.txt Switch to using footpath mod 2015-05-09 12:32:33 +01:00
form.lua A ton of stuff 2015-05-19 23:29:08 +01:00
init.lua More wip 2015-05-24 10:25:51 +01:00
tracking.lua More tweaks and fixes 2014-11-26 18:18:30 +00:00

README.md

People Mod

NOTE: Not currently compatible with stock minetest. Requires at least the current git version, plus these:

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:

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:

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.

The presets can be reloaded from disk while the server is running, using the chat command /people reload_presets. The new versions will start being used immediately by any active person set to that preset (but obviously where a preset has been used as a base for a "Custom" setting, that will not be affected).

A brief description of some of the presets currently available:

####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 fixed route indefinitely. After programming, the person has no route defined. To give it the first point on its route, you /tell it the command "here". The place you're standing when you say that becomes the first point on its route.

You can then follow that with any additional number of "then here" commands, which add further points to the route.

The route is followed from start to finish, and then back the other way.

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} - this specifies a direction to the node against which the placing will be done. If it's not specified, a direction will automatically be figured out, if possible. Normally it's fine to let this be figured out - you would specify it if it's essential the node is placed against a specific one, for example, when placing a torch against a wall.

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.
  • sdist=n - special things (lighting, supports) are placed in a repeating 8 node pattern that begins where the tunnelling starts. This can be used to shift the start point of that. Generally 1-7 would be specified here, if anything.
  • 'openside={}` - if present, can have either left or right=true, or both. The end (last three nodes) of the tunnel will be left open on that/those sides.
  • 'endface={} - as for openside, but after the tunneling is complete, the person will fininsh positioned and facing correctly to being a tunnel through that opening. If both are specified, one is picked at random.

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.