creatures/behaviour.js
67 lines · in natura-src-1.0.0.zip as natura/creatures/behaviour.js · a module · its entry in the module reference
// creatures/behaviour.js -- CreatureBehaviour: the DECISION layer.
//
// What an animal DOES over time, kept apart from how it is drawn (creatures/render.js) and from
// what it is (creatures/species.js). A behaviour is a loop of segments; each segment holds a slice
// of the loop (f, a fraction) and the posture + movement to hold during it: adv (cross-ground move,
// 0..1, or >1 for a dash), neck/ear/stand posture, an optional peck (bird strike), and an optional
// gait override (a bolting deer swaps to the gallop mid-loop). targetOf() picks the current segment
// for a creature at loop time e; the scene eases the real pose toward it so heads raise and legs
// plant smoothly. The renderer never sees a behaviour, only the resulting pose -- so the same loop
// drives the meadow piece or a Natura-world agent identically.
//
// UMD (node test / window.CreatureBehaviour). No dependencies.
(function (root, factory) {
if (typeof module !== 'undefined' && module.exports) module.exports = factory();
else root.CreatureBehaviour = factory();
})(typeof self !== 'undefined' ? self : this, function () {
'use strict';
// a gallop gait: long stride, brief ground contact so there is an airborne suspension phase, a big loft
const GALLOP = { stride:44, lift:18, duty:0.34, speed:2.2, gallop:true };
const TROT = { stride:30, lift:11, duty:0.5, speed:1.5, trot:true }; // the diagonal 2-beat between a walk and a run
const BEH = {
graze: { period:16, seg:[
{f:0.55, adv:0.0, neck:1.78, ear:-0.18, stand:1}, // head down, cropping the grass
{f:0.13, adv:0.0, neck:-0.35, ear:0.55, stand:1}, // head snaps up, ears forward -- a check
{f:0.32, adv:1.0, neck:0.05, ear:0.15, stand:0} ]}, // ambles on to fresh grass
flee: { period:19, seg:[
{f:0.58, adv:0.0, neck:1.78, ear:-0.15, stand:1}, // grazing, calm and unaware
{f:0.08, adv:0.0, neck:-0.5, ear:0.75, stand:1}, // ALARM -- head jerks up, ears pinned forward
{f:0.22, adv:1.9, neck:0.5, ear:-0.1, stand:0, gait:GALLOP}, // BOLT -- gallops away, neck thrust forward
{f:0.12, adv:0.5, neck:-0.15, ear:0.5, stand:0} ]}, // eases up, still trotting, glancing back
stalk: { period:22, seg:[
{f:0.78, adv:1.0, neck:0.30, ear:0.20, stand:0}, // steady low-headed padding
{f:0.22, adv:0.0, neck:-0.10, ear:0.55, stand:1} ]}, // halts, head up, scenting the air
freeze:{ period:11, seg:[
{f:0.68, adv:0.0, neck:-0.18, ear:0.5, stand:1}, // frozen bolt-still, ears up, listening
{f:0.32, adv:1.0, neck:0.10, ear:0.1, stand:0} ]}, // then a sudden bounding dash
wade: { period:15, seg:[
{f:0.52, adv:0.0, neck:0, stand:1, peck:0}, // stock-still over the shallows, neck coiled
{f:0.10, adv:0.0, neck:0, stand:1, peck:1}, // the STRIKE -- neck stabs down at a fish
{f:0.38, adv:0.4, neck:0, stand:0, peck:0} ]}, // a few slow, deliberate wading steps
forage:{ period:1, seg:[ {f:1.0, adv:1.0, neck:0} ]}, // continuous walk/hop + the gait's own auto-peck
sitforage:{ period:12, seg:[ // a SQUIRREL: scamper to a spot, sit up and eat, move on
{f:0.30, adv:1.2, neck:0, ear:0.1, stand:0, siteat:0}, // bounds to a fresh patch
{f:0.55, adv:0.0, neck:0, ear:0.2, stand:1, siteat:1}, // SITS UP on its haunches, food to the mouth, nibbling
{f:0.15, adv:0.0, neck:-0.1, ear:0.6, stand:1, siteat:1} ]},// still sitting -- a quick look-round mid-meal
dig: { period:14, seg:[ // a BADGER's nature: dig, look, trundle on, dig again
{f:0.60, adv:0.0, neck:1.95, ear:-0.2, stand:1, dig:1}, // head down, front paws scraping earth, soil flung back
{f:0.14, adv:0.0, neck:-0.2, ear:0.6, stand:1, dig:0}, // pauses -- head up, a quick look round, snout testing the air
{f:0.26, adv:0.9, neck:0.85, ear:0.1, stand:0, dig:0} ]}, // trundles low to a fresh patch, head still carried low
roost: { period:18, seg:[ // a perched OWL: sits upright and watches, never walks (the head-swivel carries the life)
{f:0.50, adv:0.0, neck:0.0, ear:0.15, stand:1}, // sits bolt-upright, watching the meadow
{f:0.14, adv:0.0, neck:-0.12, ear:0.5, stand:1}, // a small rouse -- lifts, alert
{f:0.36, adv:0.0, neck:0.04, ear:0.1, stand:1} ]}, // settles again, still perched
};
// Pick the active segment for creature `c` (needs c.beh + c.seed) at loop time `e` (seconds).
function targetOf(c, e) {
const B = c.beh; if (!B) return { adv: 1 };
let t = ((e / B.period + c.seed) % 1 + 1) % 1, acc = 0;
for (const s of B.seg) { acc += s.f; if (t < acc) return s; }
return B.seg[B.seg.length - 1];
}
return { GALLOP, TROT, BEH, targetOf };
});