Animal behavior
Usage guide of animal.behavior: one named instance per animal that decides needs, fleeing, defense, hunting, herding and wandering, with confirmed effects, settings, commands, events, hooks and presets.
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 #
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 #
<!-- test: kits/game_engine/host/tests/animal_behavior.rs::feeding_is_confirmed_by_game_owned_resources_not_the_behavior_kit --> 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).
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 replaceconfig, or cancel (creation then fails withcreation refused).animal.behavior.request: before a decision command is applied; payload{ instance, op, command }; a tap may replacecommand, or cancel: the call then answers{ ok: false, cancelled: true, by, reason }and the refused event is emitted.
{ "id": "no_attacks", "hook": "animal.behavior.request", "match": { "op": "resolve" }, "cancel": true, "reason": "peaceful" }
Presets and scripts #
Presets. A document { "id": "<name>", "config": { … } } in the project data collection animal_behavior_presets names a ready-made tuning. create(id, { "preset": "<name>", … }) 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 laststepanswer. setNeedsorsavefails while an action is pending: confirm it withresolvefirst (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
stepcommands, and time passes only by thedtthe 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.