# Companions that follow and obey (companion.control) ## What it is for `companion.control` runs a travelling ally tied to an owner (the player by default). The ally follows at a distance, can wait where it stands or walk to a point, asks for help when it is lost or too far, and the player can take control of it. It decides how the companion should move; your engine (or, on the Rust host, the NPC motor with `ai.navigation`) does the walking, collision, camera and animation. It creates and destroys no actors. ## Turn it on Install the kit in a project and set its settings with the tools below; `gripforge kit api companion.control` lists every setting, and `gripforge docs companion-control` prints this guide offline. ```text gripforge_gamekit_install { project_id: "gkp_…", id: "companion.control" } gripforge_gamekit_configure { project_id: "gkp_…", id: "companion.control", config: { "followDistance": 2.5, "followOffset": [1.2, 0, -1], "walkSpeed": 2.8 } } ``` ## First example An explicit instance reads the observed positions of owner and companion on each `step` and answers a steering intent. A `hold` order stops it; a `move` order walks it to a target, and it holds again on arrival. ```text companion.control.request("default", { op: "command", mode: "hold" }) companion.control.request("default", { op: "step", dt: 0.1, ownerPosition: [0,0,0], position: [10,0,0], … }) // desiredVelocity [0,0,0], state.mode "hold" companion.control.request("default", { op: "command", mode: "move", target: [0,0,8] }) companion.control.request("default", { op: "step", dt: 0.1, position: [0,0,7.8], … }) // arrived: true, state.mode "hold", state.target null ``` ## Settings and defaults | Setting | Default | Meaning | | --- | --- | --- | | `owner` | `player` | stable id of the owner, different from the companion | | `companion` | `companion` | stable id of the companion | | `maxSpeed` | 6 | {{s_maxSpeed}} | | `acceleration` | 15 | steering acceleration and braking when approaching (0.1 to 500) | | `followDistance` | 2 | {{s_followDistance}} | | `followOffset` | `[0, 0, 0]` | {{s_followOffset}} | | `followFrame` | `owner` | {{s_followFrame}} | | `stopRadius` | 0.2 | {{s_stopRadius}} | | `recoveryDistance` | 30 | {{s_recoveryDistance}} | | `followSlack` | 0 | {{s_followSlack}} | | `followDelay` | 0 | {{s_followDelay}} | | `walkSpeed` | 0 | {{s_walkSpeed}} | | `catchUpDistance` | 8 | {{s_catchUpDistance}} | ## Services - `companion.control` (service): `create(id, config)`, `request(id, command)`, `remove(id)`, `ids()`; the instance `default` uses the installed settings. Commands of `request`: `command` (`mode` of `follow`, `hold` or `move`, with a `target` for `move`), `step`, `possess`, `release`, plus `status`, `config`, `preview`, `save`, `load`. - With the Rust host and a `companions` document, the kit binds an existing NPC to its owner and moves it by itself: `order(id, mode, target?)` gives an order and `bound()` lists the bound companions. A `companions` document has `id`, `npc` (an existing `npcs` document), `ownerTag` (default `player`; exactly one living owner must carry it), `mode` (`follow` or `hold`), `damageable` (`false` cancels damage to an essential follower) and an optional `config` with the movement settings above. ## Events - companion.control.created and companion.control.removed (an instance appeared or went), companion.control.changed (a decided command applied, with its `op` and `result`; never a read or a `step`), companion.control.refused (a hook cancelled a command, with `by` and `reason`). - companion.bound (a live NPC took the exclusive motor driver), companion.ordered (a follow, hold or move order was accepted, with `companion`, `mode`, `target`) and companion.recovery (separation exceeded the safe distance; nothing was teleported). The kit listens to no event. ## Hooks and modding - `companion.control.create`: an instance is about to be created; a tap may change its `config` or cancel, and `create` then fails with the reason. - `companion.control.request`: a decided command (every `op` except the reads and `step`) is about to apply; a tap may change the `command` or cancel, and `request` then answers `{ ok: false, cancelled: true, by, reason }` with the instance unchanged. - `companion.order`: a bound companion is about to accept an order; a tap may change its `mode` or `target`, or cancel (Rust host only). ## Using it from content Every setting is moddable. A `companion_control_presets` document `{ id, extends?, config }` names a ready-made set of settings, and `create(id, { preset: "" })` takes it (own keys win). A hook document of a game or a mod looks like this: ```json { "id": "no_move_orders", "hook": "companion.control.request", "match": { "op": "command" }, "cancel": true, "reason": "stay_close" } ``` Script verbs: the action `companion_control_request` sends a `command` to an instance (default `default`) through the request hook, and the condition `companion_control_exists` tells whether an instance exists. The action `companion_order` (with `companion`, `mode` and, for `move`, a world-space `target`) orders a live managed companion. The inputs `companion_hold` and `companion_follow` are unbound by default: bind them to keys or a menu. ## Driving it from your engine Each frame send `step` with `dt`, `ownerPosition`, `position`, `velocity`, `ownerAlive`, `companionAlive` and `pathAvailable` (optionally `blocked`, `waypoint`, `attentionPosition`). The answer holds `desiredVelocity`, `arrived`, `applySteering`, `navigationTarget`, `needsPath`, `needsRecovery` and the `state` (`mode`, `target`, `possessed`). With no path the companion asks for one and stays put; beyond `recoveryDistance` it asks for recovery and never teleports; an `attentionPosition` pauses motion and gives a look direction (a dialogue partner). ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | A command is refused with "follow, hold or move" | `mode` must be one of the three, and `move` needs a `target` of three numbers. | | The companion does not move | It is in `hold`, has arrived, is waiting out `followDelay`, or has no path (`needsPath`). Check `state.mode` and provide `pathAvailable` or a `waypoint`. | | `possess` is refused | Both owner and companion must have been observed alive by a `step` first. | ## Limits - Pathfinding, collision, floor, animation, camera and input ownership stay with your engine; the kit gives intents. - The managed motor (binding an NPC from `companions`) runs on the Rust host only; the web wrapper and the C library offer the explicit driver. - Dialogue content, combat behaviour and models belong to your game; installing the kit does not make a companion. ## Versions Kit `companion.control` 1.6.0. 1.6.0 added the dialogue attention pause, 1.5.0 the natural following settings (`followSlack`, `followDelay`, `walkSpeed`, `catchUpDistance`), 1.1.0 the hooks, presets, events and verbs. It needs `npc.locomotion` ^1.3 and `ai.navigation` ^1 for the managed motor.