# Animal behavior (animal.behavior) ## What it is for Individual animal decisions for a game: each animal is a named instance that keeps bounded needs (hunger, thirst, fatigue) and fear, and answers each step with a mode and, when it needs something from the world, an action to confirm. The engine keeps physics, navigation and every real effect. It does not move anything itself and invents no world effect: food, water, damage and paths are supplied by the game, and a bite or a sip counts only when the game confirms it. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "animal.behavior" } gripforge_gamekit_configure { project_id: "gkp_…", id: "animal.behavior", config: { "diet": "carnivore", "temperament": "defensive", "seed": 7 } } ``` Only the keys that differ from the defaults need to be written. Each animal is one named instance, created by the game; a fresh game already has one instance called `default`. ## First example A hungry herbivore finds food: the kit asks to eat, the game consumes the food, then confirms with the token and the amount really eaten (hunger drops from 0.8 to 0.7). ```text create ["deer", { "animalId": "e1", "hungerRate": 0 }] request ["deer", { "op": "setNeeds", "hunger": 0.8, "thirst": 0, "fatigue": 0 }] request ["deer", { "op": "step", "dt": 0.1, "position": [0,0,0], "velocity": [0,0,0], "alive": true, "resources": [{ "id": "e2", "kind": "plant", "position": [0,0,0], "accessible": true }] }] -> { "mode": "eat", "action": { "kind": "eat", "token": 1, ... }, "needs": { "hunger": 0.8, ... } } request ["deer", { "op": "resolve", "token": 1, "success": true, "amount": 0.1 }] -> hunger 0.7 ``` ## Settings and defaults | Setting | Default | Meaning | |---|---|---| | `animalId` | `"animal"` | Stable identity, excluded from its own perceptions | | `species` | `"generic"` | Game-defined label; behavior comes from the other settings | | `diet` | `"herbivore"` | `herbivore`, `carnivore` or `omnivore`: which food it seeks and whether it hunts prey | | `temperament` | `"timid"` | `timid` flees; `defensive` defends when close or cornered; `aggressive` approaches and defends | | `space` | `"ground"` | `ground` keeps the up-axis velocity; `free` steers in three dimensions (the engine owns gravity and collisions) | | `walkSpeed` | 2 | Top speed for ordinary movement | | `runSpeed` | 6 | Top speed when fleeing or pursuing (at least `walkSpeed`) | | `acceleration` | 8 | Maximum steering acceleration on a valid path | | `senseRadius` | 25 | Distance within which supplied observations count | | `dangerRadius` | 12 | Distance at which a visible threat starts danger behavior | | `safeRadius` | 20 | Fleeing keeps reacting out to this distance | | `defendDistance` | 2 | Defensive animals defend inside this distance, or when unable to flee | | `attackReach` | 1.5 | Maximum attack distance | | `hungerRate` | 0.005 | Hunger gained per living simulation second | | `thirstRate` | 0.008 | Thirst gained per second; 0 disables natural thirst | | `fatigueRate` | 0.003 | Fatigue gained at `runSpeed`, scaled by observed speed | | `needThreshold` | 0.6 | Need value (0 to 1) that starts seeking resources or resting | | `satisfiedThreshold` | 0.2 | An ongoing need behavior continues until this value (below `needThreshold`) | | `restRecovery` | 0.08 | Fatigue recovered per second while resting in a safe place the engine approves | | `fearDecay` | 0.3 | Fear lost per second without refreshed danger | | `threatMemory` | 3 | Seconds a vanished threat position is remembered | | `herdRadius` | 12 | Range for supplied compatible herd members (≤ `senseRadius`) | | `separationDistance` | 1 | Minimum desired spacing from herd members | | `followDistance` | 3 | Desired distance from the herd centroid | | `wanderRadius` | 8 | Radius of wander goals around home; 0 disables wandering | | `wanderInterval` | 5 | Seconds before a new wander goal is chosen | | `arrivalRadius` | 0.4 | Arrival radius at a resource, and tolerance for matching a navigation request | | `actionInterval` | 1 | Minimum seconds between new food, water or attack requests | | `attackDamage` | 10 | Maximum damage requested per attack (the combat system confirms the real effect) | | `biteAmount` | 0.2 | Maximum hunger relief requested per confirmed bite | | `sipAmount` | 0.3 | Maximum thirst relief requested per confirmed sip | | `seed` | 1 | Nonzero deterministic seed; give each animal its own | Constraints are checked at creation: `dangerRadius` ≤ `safeRadius` ≤ `senseRadius`, `walkSpeed` ≤ `runSpeed`, `attackReach` ≤ `defendDistance` ≤ `dangerRadius`, `separationDistance` ≤ `followDistance` ≤ `herdRadius` ≤ `senseRadius`, `satisfiedThreshold` < `needThreshold`. ## Commands `step` carries the observation: `dt` (at most a few tenths of a second), `position`, `velocity`, `alive`, and the lists the game can see: `resources` (food and water, each with `id`, `kind`, `position`, `accessible`), `prey` (with `lineClear`), `threats`, `herd` and `navigation`. The answer holds the `mode` (`idle`, `wander`, `search_food`, `search_water`, `rest`, `flee`, `defend` or `hunt`), the needs, and an `action` (`eat`, `drink` or `attack`) with a `token` when the world must be touched. `resolve { token, success, amount }` confirms or fails it; a stale token is refused. `setNeeds { hunger, thirst, fatigue }` sets the needs, `preview { command }` runs a command on a copy, and `status`, `config`, `save` and `load` read and restore. ## Services The kit is a driver service: it decides only when the game calls it, never on its own tick. - `create(id, config)`: creates a named instance (config is checked; at most 128 instances; an existing id is refused); returns the id. - `request(id, command)`: sends one command object to an instance and returns its answer (unknown instance or invalid command: an error). - `remove(id)`: removes an instance; returns whether it existed. - `ids()`: lists the instance ids. `status`, `config`, `save`, `preview` and `load` are reads and state operations; `step` is the fixed-step command the game sends every tick. Every other command is a decision and goes through the request hook below. ## What it announces | Name | Detail | |---|---| | `animal.behavior.created` | An instance was created; payload `{ instance, preset }`. | | `animal.behavior.removed` | An instance was removed; payload `{ instance }`. | | `animal.behavior.changed` | A decision command was applied; payload `{ instance, op, result }`. | | `animal.behavior.refused` | A tap of the request hook cancelled a decision; payload `{ instance, op, by, reason }`. | ## Hooks and modding A game or a mod taps a hook with a document of its `hooks` list, without touching the kit: - `animal.behavior.create`: before an instance is created; payload `{ instance, preset, config }`; a tap may replace `config`, or cancel (creation then fails with `creation refused`). - `animal.behavior.request`: before a decision command is applied; payload `{ instance, op, command }`; a tap may replace `command`, or cancel: the call then answers `{ ok: false, cancelled: true, by, reason }` and the refused event is emitted. ```json { "id": "no_attacks", "hook": "animal.behavior.request", "match": { "op": "resolve" }, "cancel": true, "reason": "peaceful" } ``` ## Presets and scripts Presets. A document `{ "id": "", "config": { … } }` in the project data collection `animal_behavior_presets` names a ready-made tuning. `create(id, { "preset": "", … })` starts from it; the keys given next to `preset` win (shallow merge). An unknown preset is an error; the created event carries the preset name. Scripts and rules can drive an instance with the action `animal_behavior_request { instance?, command }` (instance defaults to `default`) and test it with the condition `animal_behavior_exists { instance? }`. ## When it does not do what you expect - A command is refused with `stale action token`: the token is not the one of the pending action (or the action already ended); use the token of the last `step` answer. - `setNeeds` or `save` fails while an action is pending: confirm it with `resolve` first (a failed confirmation also ends it). ## Limits - No movement, pathfinding, gravity or collision: the game supplies valid navigation paths and applies the confirmed effects. - No hidden tick: nothing happens between two `step` commands, and time passes only by the `dt` the game sends. - Instances are saved and restored with the game (including the random generator, so a reload replays the same choices); a pending action must be resolved first. - Targets: the native kit host and the web player, from the same Rust core. The kit has no display module. ## Versions 1.1.0 (2026-10-19): `game.engine` hooks, presets, outcome events and script verbs; 1.0.0 (2026-10-16): first release. Requires no other kit.