Go to file
Ciaran Gultnieks bcdc792990 More tweaks and fixes 2014-11-26 18:18:30 +00:00
actions More tweaks and fixes 2014-11-26 18:18:30 +00:00
models Initial commit 2014-04-03 08:32:23 +01:00
presets More tweaks and fixes 2014-11-26 18:18:30 +00:00
textures Initial commit 2014-04-03 08:32:23 +01:00
README.md More stuff, including pickup implementation 2014-11-15 14:12:21 +00:00
commands.lua More tweaks and fixes 2014-11-26 18:18:30 +00:00
depends.txt Initial commit 2014-04-03 08:32:23 +01:00
footpath.lua More tweaks and fixes 2014-11-26 18:18:30 +00:00
form.lua More stuff, including pickup implementation 2014-11-15 14:12:21 +00:00
init.lua More tweaks and fixes 2014-11-26 18:18:30 +00: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

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.

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, with pos={x,y,z}

Walks to the given position.

go, with name="string"

Walks to a named position - names are looked up using the areas module, if present This converts to go, pos={x,y,z}, with name retained for reference.

Names are most frequently used in conjunction with footpaths.

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.

place, with pos={x,y,z} and item=name

Places the given item. Must be in the inventory, and in range.

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 one item in
  • Item name and count (e.g. "default:cobble 50") to put that many in
  • Item name preceded by an asterisk (e.g. "*default:cobble") , to put all carried in

retrieve, with src="nodename" and items={"item1", "itemn"}

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.

Each item in the list can be:

  • Just the item name (e.g. "default:cobble") to get one item out
  • Item name and count (e.g. "default:cobble 50") to get that many out
  • Item name preceded by an asterisk (e.g. "*default:cobble") , to get all of that kind of item out

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 = "" - seed to plant

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).

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. Paramaters:

  • pos = {x,y,z} - position of the item (required)
  • item = "name" - name of item, e.g. "default:dirt"

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.

/people footpath_update

Update the footpath network.

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 laid out across the map by players, usually using cobble and cobble steps (but wood can also be used, e.g. for bridges). In addition, special areas (via the areas mod) are used to 'mark up' routes along these footpaths. This provides people with the ability to do sensible long-range navigation as a player would.

Path junctions are marked up by creating a 1x1x1 area above the junction node. They are always named path_xxxxx, where xxxxx is the unique name for that junction - e.g. path_My House.

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.

Additionally, above each node leading away from the junction, a 1x1x1 area with the name to_xxxxx is used. In this case, xxxxx is the name of the path node this direction leads to - e.g. to_Your House.

Changes to the footpath network only take effect when the /people footpath_update command is used. At this point, if anything is wrong with the network, this will be indicated, and the previous network will remain in effect. The current network is saved across server restarts.

If you tell a person to go to a named place (e.g. {"go", name="My House"}) they will first look for path_My House, and then also find the nearest path junction to where they are now. They will then navigate along the path network from one to the other.

If there is no path_My House they will also look for just My House, and if that's found, head directly for the centre of that area.