@bitefoundry/retrieviewer 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ASSET-PROVENANCE.md +43 -0
- package/ASSETS-LICENSE.md +58 -0
- package/ATTRIBUTION.md +54 -0
- package/EMBEDDING.md +96 -0
- package/INSTALLING.md +126 -0
- package/LICENSE +16 -0
- package/LICENSE-CODE +21 -0
- package/PLUGIN.md +110 -0
- package/PUPPY-MEMORY.md +57 -0
- package/PUPPY-Q5-COMPARISON.md +73 -0
- package/README.md +86 -2
- package/RELEASING.md +70 -0
- package/THIRD-PARTY-NOTICES.md +17 -0
- package/TRADEMARKS.md +21 -0
- package/WINDOWS-COMPANION.md +113 -0
- package/braille.mjs +45 -0
- package/examples/bridge.py +62 -0
- package/examples/embed.mjs +15 -0
- package/examples/live.mjs +40 -0
- package/examples/plugin.mjs +21 -0
- package/integrations/opencode/runtime.mjs +195 -0
- package/integrations/opencode/tui.tsx +59 -0
- package/integrations/windows-overlay/Overlay.cs +259 -0
- package/integrations/windows-overlay/desktop.mjs +87 -0
- package/integrations/windows-overlay/protocol.mjs +39 -0
- package/integrations/windows-overlay/worker.mjs +60 -0
- package/locomotion-core.mjs +21 -0
- package/locomotion-puppy.animal +0 -0
- package/package.json +131 -4
- package/pet-q5-manifest.json +118 -0
- package/puppy-clean.mjs +51 -0
- package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
- package/puppy-companion-interactions-v3.json +1 -0
- package/puppy-companion-life.mjs +182 -0
- package/puppy-companion-objects.mjs +30 -0
- package/puppy-companion-sprites.mjs +14 -0
- package/puppy-continuous.mjs +57 -0
- package/puppy-face.mjs +46 -0
- package/puppy-frame-predictive.mjs +147 -0
- package/puppy-frame-store.mjs +108 -0
- package/puppy-mouth-anchor.json +1 -0
- package/puppy-mouth-anchor.mjs +33 -0
- package/puppy-paper-actions-v9-q5.bin.gz +0 -0
- package/puppy-paper-actions-v9.json +1 -0
- package/puppy-paper-actions.mjs +48 -0
- package/puppy-paper-approach-q5.bin.gz +0 -0
- package/puppy-paper-approach.json +1 -0
- package/puppy-paper-area.mjs +15 -0
- package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
- package/puppy-paper-arrival-seat.json +1 -0
- package/puppy-paper-behaviors-q5.bin.gz +0 -0
- package/puppy-paper-behaviors.json +1 -0
- package/puppy-paper-behaviors.mjs +8 -0
- package/puppy-paper-contact.mjs +103 -0
- package/puppy-paper-director.mjs +21 -0
- package/puppy-paper-paw-transport.mjs +76 -0
- package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
- package/puppy-paper-rear-gallop.json +1 -0
- package/puppy-paper-seat-q5.bin.gz +0 -0
- package/puppy-paper-seat.json +14 -0
- package/puppy-paper-seat.mjs +13 -0
- package/puppy-paper-trick-timing.mjs +58 -0
- package/puppy-paper-world.mjs +108 -0
- package/puppy-paper.mjs +271 -0
- package/puppy-stable-meta.json +1606 -0
- package/puppy-stable.mjs +72 -0
- package/puppy-stride-meta.json +343 -0
- package/puppy-stride-tweens-q5.bin.gz +0 -0
- package/puppy-zoomies.mjs +27 -0
- package/puppy-zoomies30.mjs +12 -0
- package/retrieviewer-cli.mjs +94 -0
- package/retrieviewer-config.mjs +49 -0
- package/retrieviewer-install.mjs +157 -0
- package/retrieviewer-plugin.d.mts +44 -0
- package/retrieviewer-plugin.mjs +97 -0
- package/retrieviewer-stdio.mjs +38 -0
- package/retrieviewer.d.mts +85 -0
- package/retrieviewer.mjs +142 -0
- package/vendor/procedural-animals/LICENSE +21 -0
- package/vendor/procedural-animals/package.json +40 -0
- package/vendor/procedural-animals/src/animal.js +146 -0
- package/vendor/procedural-animals/src/bake.js +51 -0
- package/vendor/procedural-animals/src/build.js +81 -0
- package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
- package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
- package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
- package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
- package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
- package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
- package/vendor/procedural-animals/src/core/build/seams.js +183 -0
- package/vendor/procedural-animals/src/core/build/warp.js +112 -0
- package/vendor/procedural-animals/src/core/build/weights.js +393 -0
- package/vendor/procedural-animals/src/core/math/vec.js +81 -0
- package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
- package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
- package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
- package/vendor/procedural-animals/src/core/motion/common.js +158 -0
- package/vendor/procedural-animals/src/core/motion/index.js +22 -0
- package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
- package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
- package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
- package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
- package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
- package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
- package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
- package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
- package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
- package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
- package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
- package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
- package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
- package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
- package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
- package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
- package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
- package/vendor/procedural-animals/src/index.d.ts +98 -0
- package/vendor/procedural-animals/src/index.js +29 -0
- package/vendor/procedural-animals/src/registry.js +48 -0
- package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
- package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
- package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
- package/vendor/procedural-animals/src/species/bear/index.js +139 -0
- package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
- package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
- package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
- package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
- package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
- package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
- package/vendor/procedural-animals/src/species/boar/index.js +110 -0
- package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
- package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
- package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
- package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
- package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
- package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
- package/vendor/procedural-animals/src/species/cat/index.js +211 -0
- package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
- package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
- package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
- package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
- package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
- package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
- package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
- package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
- package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
- package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
- package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
- package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
- package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
- package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
- package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
- package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
- package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
- package/vendor/procedural-animals/src/species/cow/index.js +118 -0
- package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
- package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
- package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
- package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
- package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
- package/vendor/procedural-animals/src/species/crow/index.js +70 -0
- package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
- package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
- package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
- package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
- package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
- package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
- package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
- package/vendor/procedural-animals/src/species/deer/index.js +106 -0
- package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
- package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
- package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
- package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
- package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
- package/vendor/procedural-animals/src/species/dog/index.js +144 -0
- package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
- package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
- package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
- package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
- package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
- package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
- package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
- package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
- package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
- package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
- package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
- package/vendor/procedural-animals/src/species/fish/index.js +88 -0
- package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
- package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
- package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
- package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
- package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
- package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
- package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
- package/vendor/procedural-animals/src/species/fox/index.js +120 -0
- package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
- package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
- package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
- package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
- package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
- package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
- package/vendor/procedural-animals/src/species/frog/index.js +97 -0
- package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
- package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
- package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
- package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
- package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
- package/vendor/procedural-animals/src/species/goat/index.js +149 -0
- package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
- package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
- package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
- package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
- package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
- package/vendor/procedural-animals/src/species/horse/index.js +80 -0
- package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
- package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
- package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
- package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
- package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
- package/vendor/procedural-animals/src/species/lion/index.js +111 -0
- package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
- package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
- package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
- package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
- package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
- package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
- package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
- package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
- package/vendor/procedural-animals/src/species/pig/index.js +161 -0
- package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
- package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
- package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
- package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
- package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
- package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
- package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
- package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
- package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
- package/vendor/procedural-animals/src/species/rat/index.js +111 -0
- package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
- package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
- package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
- package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
- package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
- package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
- package/vendor/procedural-animals/src/species/shark/index.js +104 -0
- package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
- package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
- package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
- package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
- package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
- package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
- package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
- package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
- package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
- package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
- package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
- package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
- package/vendor/procedural-animals/src/species/snake/index.js +73 -0
- package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
- package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
- package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
- package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
- package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
- package/vendor/procedural-animals/src/species/spider/index.js +78 -0
- package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
- package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
- package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
- package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
- package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
- package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
- package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
- package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
- package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
- package/vendor/procedural-animals/src/worker.js +29 -0
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
// Action layer for the swimmer engine (core/motion/swimmer.js). Same contract as the quadruped layer:
|
|
2
|
+
// every frame it fills a parameter set P that the engine reads on top of the swimming.
|
|
3
|
+
//
|
|
4
|
+
// postures sit (hover still), lie (rest on the bed), sleep (rest, slow breathing, fins still;
|
|
5
|
+
// style 'bottom' | 'hover' | 'side'), death (loses balance, rolls onto its side, sinks and
|
|
6
|
+
// settles on the uneven bed): replace the swimming until 'stand' or a move request.
|
|
7
|
+
// one-shots burst (C-start fast start), jump (rise, leap out of the water, splash back), eat (lunge +
|
|
8
|
+
// suction bite; style 'bottom' picks food from the bed), drink (a gulp at the surface when
|
|
9
|
+
// it is close, else the same suction gulp as eat), attack (turn, burst and bite: emits
|
|
10
|
+
// attackHit at the mouth), hit (C-start away from the blow and a short dart).
|
|
11
|
+
// idle automatic: hovering, fin sculling and breathing come from the engine; the layer adds
|
|
12
|
+
// looking around, small reorientations and a slow vertical bob.
|
|
13
|
+
//
|
|
14
|
+
// play(name, opts) returns a Promise resolved when the action ends (postures: when the pose is
|
|
15
|
+
// reached). opts: target (Vector3: attack / eat / look), direction (Vector3: hit = the direction the blow
|
|
16
|
+
// travels, burst = the escape direction), heading (burst), side (+1 left / -1 right), speed (time scale).
|
|
17
|
+
// The promise resolves with how the action ended: 'done' | 'interrupted' | 'stopped' | 'refused'.
|
|
18
|
+
//
|
|
19
|
+
// Arbitration (the rule of every body plan): a new one-shot
|
|
20
|
+
// interrupts the running ones (they fade out, 'interrupted'); a leap out of the water (jump while in
|
|
21
|
+
// the air) is committed: only a hit layers over it, anything else is refused. A posture cross-fades
|
|
22
|
+
// from the current one and ends the one-shots (not a hit). stop(name) ends that action, stop() every
|
|
23
|
+
// one-shot and posture but death. Dead: only hit is taken, moving does not revive; a dead swimmer
|
|
24
|
+
// loses way (it coasts to a stop), rolls over and sinks to the bed.
|
|
25
|
+
import * as THREE from 'three';
|
|
26
|
+
import { clamp, lerp, smooth, angDiff } from './common.js';
|
|
27
|
+
|
|
28
|
+
export function createSwimParams() {
|
|
29
|
+
return {
|
|
30
|
+
speed: 0, speedW: 0, brake: 0, accelK: 0, heading: 0, headingW: 0, turnK: 0, yawRate: 0,
|
|
31
|
+
pitch: 0, pitchW: 0, pitchK: 0, breach: 0, surfaceOK: 0, vy: 0, vyW: 0, push: new THREE.Vector3(),
|
|
32
|
+
groundW: 0, conform: 0, roll: 0, rollW: 0, rollK: 0,
|
|
33
|
+
freqK: 1, freqAdd: 0, ampK: 1, pectK: 1, pectAmp: 1, fold: 0, flare: 0, pectAb: 0, pelvicAb: 0,
|
|
34
|
+
bend: 0, bendK: 0, bendHold: 0, arch: 0, droop: 0, headWave: 0,
|
|
35
|
+
breathRate: 1, breathAmp: 1, stress: 0, opFlare: 0, gape: 0, gapeFast: 0, protrude: 0,
|
|
36
|
+
look: null, lookW: 1, alive: 1, headLift: 0, headShake: 0, eyeRoll: 0, nictitate: 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resetParams(P, stress) {
|
|
41
|
+
P.speed = 0; P.speedW = 0; P.brake = 0; P.accelK = 0; P.heading = 0; P.headingW = 0; P.turnK = 0; P.yawRate = 0;
|
|
42
|
+
P.pitch = 0; P.pitchW = 0; P.pitchK = 0; P.breach = 0; P.surfaceOK = 0; P.vy = 0; P.vyW = 0; P.push.set(0, 0, 0);
|
|
43
|
+
P.groundW = 0; P.conform = 0; P.roll = 0; P.rollW = 0; P.rollK = 0;
|
|
44
|
+
P.freqK = 1; P.freqAdd = 0; P.ampK = 1; P.pectK = 1; P.pectAmp = 1; P.fold = 0; P.flare = 0; P.pectAb = 0; P.pelvicAb = 0;
|
|
45
|
+
P.bend = 0; P.bendK = 0; P.bendHold = 0; P.arch = 0; P.droop = 0; P.headWave = 0;
|
|
46
|
+
P.breathRate = 1; P.breathAmp = 1; P.stress = stress; P.opFlare = 0; P.gape = 0; P.gapeFast = 0; P.protrude = 0;
|
|
47
|
+
P.look = null; P.lookW = 1; P.alive = 1; P.headLift = 0; P.headShake = 0; P.eyeRoll = 0; P.nictitate = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Shark bite (attack / eat 'tear' / breach): the snout lifts, the lower jaw drops, the upper jaw
|
|
51
|
+
// protrudes a moment later, the eye rolls back (white shark) or the nictitating membrane closes
|
|
52
|
+
// (carcharhinids), the jaws close and the head shakes side to side to saw off a piece. t0 = start
|
|
53
|
+
// of the bite, tune = { headLift, shake, eye }; returns the time the jaws close.
|
|
54
|
+
function sharkBite(P, w, t, t0, tune, side) {
|
|
55
|
+
const lift = tune.headLift || 0;
|
|
56
|
+
const b = (a, bb, c, d) => bump(t - t0, a, bb, c, d);
|
|
57
|
+
mx(P, 'headLift', lift, w * b(-0.04, 0.1, 0.26, 0.42));
|
|
58
|
+
const open = b(0.0, 0.12, 0.22, 0.3);
|
|
59
|
+
mx(P, 'gape', 1.1 * open, w); mx(P, 'gapeFast', 1, w);
|
|
60
|
+
mx(P, 'protrude', b(0.06, 0.16, 0.3, 0.42), w);
|
|
61
|
+
const nS = tune.shake || 0, per = 0.34;
|
|
62
|
+
const eyeEnd = 0.36 + nS * per;
|
|
63
|
+
const eye = b(0.06, 0.12, eyeEnd, eyeEnd + 0.15);
|
|
64
|
+
if (tune.eye === 'roll') mx(P, 'eyeRoll', 1, w * eye);
|
|
65
|
+
else if (tune.eye === 'nictitate') mx(P, 'nictitate', 1, w * eye);
|
|
66
|
+
if (nS > 0) {
|
|
67
|
+
// hold the bite (jaws nearly shut on the prey) and shake the head
|
|
68
|
+
const ts = t - t0 - 0.3;
|
|
69
|
+
const env = smooth(0, 0.12, ts) * (1 - smooth(nS * per - 0.1, nS * per + 0.05, ts));
|
|
70
|
+
const sh = Math.sin((Math.max(0, ts) / per) * Math.PI * 2) * env * side;
|
|
71
|
+
mx(P, 'headShake', 0.3 * sh, w);
|
|
72
|
+
mx(P, 'bend', -0.25 * sh, w); mx(P, 'bendK', 2, w);
|
|
73
|
+
mx(P, 'gape', P.gape + 0.12 * env, w);
|
|
74
|
+
mx(P, 'protrude', P.protrude + 0.5 * env, w);
|
|
75
|
+
}
|
|
76
|
+
return t0 + 0.3;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
const mx = (P, k, v, w) => { P[k] += (v - P[k]) * w; };
|
|
81
|
+
// angle-aware blend of the heading target
|
|
82
|
+
const mxH = (P, v, w) => {
|
|
83
|
+
if (P.headingW <= 0) { P.heading = v; P.headingW = w; return; }
|
|
84
|
+
P.heading += angDiff(P.heading, v) * w; P.headingW += (1 - P.headingW) * w;
|
|
85
|
+
};
|
|
86
|
+
const bump = (t, a, b, c, d) => smooth(a, b, t) * (1 - smooth(c, d, t));
|
|
87
|
+
|
|
88
|
+
// height of the fish's centre when its lowest skin rests on the bed here
|
|
89
|
+
function restY(e) { return e.groundAt(e.pos.x, e.pos.z) + e.cBelow; }
|
|
90
|
+
// slope of the bed along / across the heading
|
|
91
|
+
function bedSlope(e, out) {
|
|
92
|
+
const L = e.cfg.L, h = e.heading;
|
|
93
|
+
const fx = Math.sin(h) * L * 0.4, fz = Math.cos(h) * L * 0.4, lx = Math.cos(h) * L * 0.2, lz = -Math.sin(h) * L * 0.2;
|
|
94
|
+
const x = e.pos.x, z = e.pos.z;
|
|
95
|
+
out.pitch = Math.atan2(e.groundAt(x + fx, z + fz) - e.groundAt(x - fx, z - fz), L * 0.8);
|
|
96
|
+
out.roll = Math.atan2(e.groundAt(x + lx, z + lz) - e.groundAt(x - lx, z - lz), L * 0.4);
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ------------------------------------------------------------------------------------------------
|
|
101
|
+
// Postures
|
|
102
|
+
// ------------------------------------------------------------------------------------------------
|
|
103
|
+
const POSTURES = {
|
|
104
|
+
// hover still where it is: fins keep sculling slowly, the tail barely moves
|
|
105
|
+
sit: {
|
|
106
|
+
fadeIn: 0.9, fadeOut: 0.7,
|
|
107
|
+
apply(P, w, inst, A) {
|
|
108
|
+
const e = A.engine;
|
|
109
|
+
if (e.cfg.minSpeed > 0) { mx(P, 'breathRate', 0.8, w); return; }
|
|
110
|
+
mx(P, 'speed', 0, 1); mx(P, 'speedW', 1, w); mx(P, 'brake', 0.6, w * (1 - smooth(0.4, 1, inst.t)));
|
|
111
|
+
mx(P, 'vy', 0, 1); mx(P, 'vyW', 1, w);
|
|
112
|
+
mx(P, 'ampK', 0.45, w); mx(P, 'pectAmp', 0.55, w); mx(P, 'breathRate', 0.9, w);
|
|
113
|
+
void e;
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
// settle onto the bed: sink gently, level the body on the bed, fins as props
|
|
117
|
+
lie: {
|
|
118
|
+
fadeIn: 1.6, fadeOut: 1.2,
|
|
119
|
+
apply(P, w, inst, A) {
|
|
120
|
+
const e = A.engine, L = e.cfg.L, tune = inst.tune;
|
|
121
|
+
if (e.cfg.minSpeed > 0) { inst.near = 1; mx(P, 'breathRate', 0.7, w); return; }
|
|
122
|
+
const h = e.pos.y - restY(e);
|
|
123
|
+
const near = smooth(0.25 * L, 0.02 * L, h);
|
|
124
|
+
inst.near = near;
|
|
125
|
+
mx(P, 'speed', 0, 1); mx(P, 'speedW', 1, w); mx(P, 'brake', 0.4, w);
|
|
126
|
+
mx(P, 'vy', -clamp(h * 2.5, 0.06 * L, 0.5 * L), 1); mx(P, 'vyW', 1, w);
|
|
127
|
+
mx(P, 'groundW', 1, w);
|
|
128
|
+
const sl = bedSlope(e, A._slope);
|
|
129
|
+
mx(P, 'pitch', sl.pitch, 1); mx(P, 'pitchW', near, w);
|
|
130
|
+
const side = tune.style === 'side' || inst.style === 'side' ? inst.side * 0.42 : 0;
|
|
131
|
+
mx(P, 'roll', -sl.roll + side, 1); mx(P, 'rollW', near, w);
|
|
132
|
+
mx(P, 'conform', 0, w);
|
|
133
|
+
mx(P, 'ampK', 0.3, w); mx(P, 'freqK', 0.6, w); mx(P, 'pectAmp', 0.35, w); mx(P, 'pelvicAb', 12, w * near);
|
|
134
|
+
mx(P, 'breathRate', 0.85, w);
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
sleep: {
|
|
138
|
+
fadeIn: 1.8, fadeOut: 1.3,
|
|
139
|
+
apply(P, w, inst, A) {
|
|
140
|
+
const style = inst.tune.style || 'bottom';
|
|
141
|
+
if (style === 'hover') POSTURES.sit.apply(P, w, inst, A);
|
|
142
|
+
else POSTURES.lie.apply(P, w, inst, A);
|
|
143
|
+
const c = smooth(0.8, 3, inst.t) * w;
|
|
144
|
+
mx(P, 'breathRate', 0.55, c); mx(P, 'breathAmp', 0.7, c); mx(P, 'pectAmp', 0.08, c); mx(P, 'ampK', 0.12, c);
|
|
145
|
+
mx(P, 'lookW', 0, c);
|
|
146
|
+
if (style !== 'hover') mx(P, 'pitch', P.pitch - 0.06, c);
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
// loses balance (wobbling roll, flared fins, twitching tail), rolls onto its side, sinks and settles
|
|
150
|
+
death: {
|
|
151
|
+
fadeIn: 0.3, fadeOut: 1.6,
|
|
152
|
+
apply(P, w, inst, A) {
|
|
153
|
+
const e = A.engine, L = e.cfg.L, t = inst.t, side = inst.side;
|
|
154
|
+
const h = e.pos.y - restY(e);
|
|
155
|
+
const near = smooth(0.3 * L, 0.03 * L, h);
|
|
156
|
+
const wob = smooth(0, 0.4, t) * (1 - smooth(1.0, 1.8, t));
|
|
157
|
+
const over = smooth(0.6, 2.2, t);
|
|
158
|
+
mx(P, 'alive', 0, w);
|
|
159
|
+
mx(P, 'speed', 0, 1); mx(P, 'speedW', 1, w); mx(P, 'brake', 0.3, w);
|
|
160
|
+
mx(P, 'roll', side * (0.45 * wob * Math.sin(t * 7.5) + over * (inst.tune.roll ?? Math.PI * 0.5)), 1); mx(P, 'rollW', 1, w); mx(P, 'rollK', 0.6, w);
|
|
161
|
+
// sinks once it is over, faster at first, then settles
|
|
162
|
+
const sink = -clamp(h * 1.2, 0.03 * L, 0.45 * L) * smooth(0.4, 1.4, t);
|
|
163
|
+
mx(P, 'vy', sink, 1); mx(P, 'vyW', 1, w);
|
|
164
|
+
mx(P, 'groundW', 1, w);
|
|
165
|
+
mx(P, 'pitch', -0.12 * over * (1 - near), 1); mx(P, 'pitchW', w, 1);
|
|
166
|
+
mx(P, 'conform', near * smooth(1.2, 2.6, t), w);
|
|
167
|
+
// tail twitches, then stillness; fins flare; mouth gapes, gill covers flare
|
|
168
|
+
const twitch = wob * (0.8 + 0.6 * Math.sin(t * 13));
|
|
169
|
+
mx(P, 'ampK', twitch, w); mx(P, 'freqK', 0.6, w);
|
|
170
|
+
mx(P, 'pectAmp', 0.4 * wob, w); mx(P, 'flare', 0.8 * wob + 0.2 * (1 - over), w);
|
|
171
|
+
// once over on its side the paired fins fall flat against the body
|
|
172
|
+
mx(P, 'fold', 1, w * over);
|
|
173
|
+
mx(P, 'breathAmp', 0, w); mx(P, 'breathRate', 0.1, w);
|
|
174
|
+
mx(P, 'gape', 0.55 * smooth(0.5, 1.6, t), w); mx(P, 'opFlare', 0.9 * smooth(0.3, 1.2, t), w);
|
|
175
|
+
mx(P, 'droop', -0.06 * over, w);
|
|
176
|
+
mx(P, 'lookW', 0, w);
|
|
177
|
+
mx(P, 'stress', 0, w);
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// ------------------------------------------------------------------------------------------------
|
|
183
|
+
// One-shots
|
|
184
|
+
// ------------------------------------------------------------------------------------------------
|
|
185
|
+
// fast start: stage 1 bends the body into a C (the head swings round), stage 2 is the return stroke
|
|
186
|
+
// that throws the fish forward, then a short sprint that fades back to the requested speed
|
|
187
|
+
function startCStart(inst, A, turn) {
|
|
188
|
+
const e = A.engine, L = e.cfg.L;
|
|
189
|
+
inst.h0 = e.heading;
|
|
190
|
+
inst.turn = turn;
|
|
191
|
+
const t1 = clamp(0.24 * L + 0.012, 0.035, 0.2);
|
|
192
|
+
inst.t1 = t1; inst.t2 = t1 * 1.1;
|
|
193
|
+
inst.dur = t1 + inst.t2 + (inst.tune.coast ?? 0.9);
|
|
194
|
+
inst.side = Math.sign(turn) || 1;
|
|
195
|
+
e.input.heading = e.heading + turn;
|
|
196
|
+
A.stress = 1;
|
|
197
|
+
}
|
|
198
|
+
function applyCStart(P, w, a, A, speedK = 1) {
|
|
199
|
+
const e = A.engine, cfg = e.cfg, t = a.t, t1 = a.t1, t2 = a.t2, T = t1 + t2;
|
|
200
|
+
const turnW = smooth(0, T * 0.85, t);
|
|
201
|
+
// C-bend: up during stage 1, reversed during stage 2, relaxed after
|
|
202
|
+
const s1 = Math.sin(Math.min(1, t / t1) * Math.PI * 0.5);
|
|
203
|
+
const cb = cfg.spec.cBend * clamp(Math.abs(a.turn) / 1.6, 0.45, 1) * a.side;
|
|
204
|
+
const bend = t < t1 ? cb * s1 * s1 : t < T + 0.25 ? lerp(cb, -0.3 * cb, smooth(t1, t1 + t2 * 0.8, t)) * (1 - smooth(T, T + 0.25, t)) : 0;
|
|
205
|
+
mx(P, 'bend', bend, w); mx(P, 'bendK', 5, w); mx(P, 'bendHold', 1, w * (1 - smooth(T, T + 0.4, t)));
|
|
206
|
+
mxH(P, a.h0 + a.turn * turnW, w * (1 - smooth(T + 0.2, T + 0.5, t)));
|
|
207
|
+
mx(P, 'turnK', 12, w);
|
|
208
|
+
const dEase = t < T * 0.85 ? (6 * (t / (T * 0.85)) * (1 - t / (T * 0.85))) / (T * 0.85) : 0;
|
|
209
|
+
mx(P, 'yawRate', a.turn * dEase, w);
|
|
210
|
+
// the sprint
|
|
211
|
+
const go = smooth(t1 * 0.5, T, t) * (1 - smooth(a.dur - 0.45, a.dur, t));
|
|
212
|
+
mx(P, 'speed', cfg.burstSpeed * speedK, 1); mx(P, 'speedW', go, w); mx(P, 'accelK', 7, w);
|
|
213
|
+
mx(P, 'fold', 1, w * (1 - smooth(a.dur - 0.5, a.dur, t)));
|
|
214
|
+
mx(P, 'ampK', 1.15, w);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const ONESHOTS = {
|
|
218
|
+
burst: {
|
|
219
|
+
start(inst, A) {
|
|
220
|
+
const e = A.engine, o = inst.opts, tune = inst.tune;
|
|
221
|
+
let turn;
|
|
222
|
+
if (o.direction) turn = angDiff(e.heading, Math.atan2(o.direction.x, o.direction.z));
|
|
223
|
+
else if (o.heading !== undefined) turn = angDiff(e.heading, o.heading);
|
|
224
|
+
else {
|
|
225
|
+
const side = o.side ?? (e.rand() < 0.5 ? -1 : 1);
|
|
226
|
+
turn = side * ((tune.turn ?? 90) + (e.rand() - 0.5) * 50) * Math.PI / 180;
|
|
227
|
+
}
|
|
228
|
+
turn = clamp(turn, -2.6, 2.6);
|
|
229
|
+
startCStart(inst, A, turn);
|
|
230
|
+
},
|
|
231
|
+
update(a) { return a.t >= a.dur; },
|
|
232
|
+
apply(P, w, a, A) { applyCStart(P, w, a, A, 1); },
|
|
233
|
+
},
|
|
234
|
+
hit: {
|
|
235
|
+
start(inst, A) {
|
|
236
|
+
const e = A.engine, o = inst.opts;
|
|
237
|
+
let turn;
|
|
238
|
+
if (o.direction) turn = angDiff(e.heading, Math.atan2(o.direction.x, o.direction.z));
|
|
239
|
+
else turn = (e.rand() < 0.5 ? -1 : 1) * 1.6;
|
|
240
|
+
// escape away from the blow (never more than ~150 degrees)
|
|
241
|
+
turn = clamp(turn, -2.6, 2.6);
|
|
242
|
+
if (Math.abs(turn) < 0.5) turn = Math.sign(turn || 1) * 0.5;
|
|
243
|
+
inst.tune = { coast: 0.7, ...inst.tune };
|
|
244
|
+
startCStart(inst, A, turn);
|
|
245
|
+
inst.wob = e.rand() < 0.5 ? -1 : 1;
|
|
246
|
+
},
|
|
247
|
+
update(a) { return a.t >= a.dur; },
|
|
248
|
+
apply(P, w, a, A) {
|
|
249
|
+
applyCStart(P, w, a, A, 0.85);
|
|
250
|
+
const t = a.t;
|
|
251
|
+
mx(P, 'roll', a.wob * 0.35 * Math.sin(t * 9) * bump(t, 0, 0.1, 0.4, 0.9), 1); mx(P, 'rollW', w, 1);
|
|
252
|
+
mx(P, 'opFlare', 0.6, w * bump(t, 0.05, 0.2, 0.6, 1.2));
|
|
253
|
+
// a visible jolt: the blow shoves the body sideways, every fin flares
|
|
254
|
+
const e = A.engine, L = e.cfg.L, j = bump(t, 0, 0.03, 0.12, 0.35) * w * 1.6 * L;
|
|
255
|
+
if (a.opts.direction) P.push.addScaledVector(a.opts.direction, j / Math.max(1e-6, a.opts.direction.length()));
|
|
256
|
+
mx(P, 'flare', 1, w * bump(t, 0, 0.05, 0.25, 0.6)); mx(P, 'pelvicAb', 18, w * bump(t, 0, 0.05, 0.25, 0.6));
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
attack: {
|
|
260
|
+
start(inst, A) {
|
|
261
|
+
const e = A.engine, L = e.cfg.L;
|
|
262
|
+
const tgt = inst.opts.target;
|
|
263
|
+
inst.target = tgt ? tgt.clone() : e.pos.clone().add(new THREE.Vector3(Math.sin(e.heading) * 1.4 * L, 0, Math.cos(e.heading) * 1.4 * L));
|
|
264
|
+
inst.shark = !!(inst.tune.headLift || inst.tune.shake || inst.tune.eye);
|
|
265
|
+
inst.dur = inst.shark ? 1.3 + (inst.tune.shake || 0) * 0.34 : 1.25;
|
|
266
|
+
inst.hit = false;
|
|
267
|
+
const d = inst.target.clone().sub(e.pos);
|
|
268
|
+
inst.turn = angDiff(e.heading, Math.atan2(d.x, d.z));
|
|
269
|
+
inst.pitch = clamp(Math.atan2(d.y, Math.hypot(d.x, d.z)), -0.6, 0.6);
|
|
270
|
+
inst.side = Math.sign(inst.turn) || 1;
|
|
271
|
+
e.input.heading = e.heading + inst.turn;
|
|
272
|
+
A.stress = 1;
|
|
273
|
+
},
|
|
274
|
+
update(a) { return a.t >= a.dur; },
|
|
275
|
+
apply(P, w, a, A) {
|
|
276
|
+
const e = A.engine, cfg = e.cfg, t = a.t;
|
|
277
|
+
mxH(P, e.input.heading, w); mx(P, 'turnK', 4, w);
|
|
278
|
+
mx(P, 'pitch', a.pitch, 1); mx(P, 'pitchW', w * bump(t, 0, 0.15, 0.35, 0.6), 1);
|
|
279
|
+
// S-start: a small counter-bend, then the lunge
|
|
280
|
+
mx(P, 'bend', -0.5 * a.side * bump(t, 0.0, 0.1, 0.12, 0.25), w); mx(P, 'bendK', 3, w);
|
|
281
|
+
const go = bump(t, 0.1, 0.22, 0.5, 0.9);
|
|
282
|
+
mx(P, 'speed', cfg.burstSpeed * 0.85 * (a.tune.lunge ?? 1), 1); mx(P, 'speedW', go, w); mx(P, 'accelK', 6, w);
|
|
283
|
+
mx(P, 'fold', 1, w * bump(t, 0.05, 0.15, 0.7, 1.0));
|
|
284
|
+
// the bite: open wide just before contact, snap shut
|
|
285
|
+
if (a.shark) sharkBite(P, w, t, 0.16, a.tune, a.side);
|
|
286
|
+
else {
|
|
287
|
+
const open = bump(t, 0.22, 0.34, 0.42, 0.5);
|
|
288
|
+
mx(P, 'gape', 1.1 * open, w); mx(P, 'gapeFast', 1, w); mx(P, 'protrude', open, w);
|
|
289
|
+
}
|
|
290
|
+
mx(P, 'opFlare', 0.8, w * bump(t, 0.45, 0.55, 0.8, 1.1));
|
|
291
|
+
if (!a.hit && t >= 0.44) {
|
|
292
|
+
a.hit = true;
|
|
293
|
+
const mouth = e.W[e.cfg.n + 1].clone();
|
|
294
|
+
const dir = new THREE.Vector3(Math.sin(e.heading), 0, Math.cos(e.heading));
|
|
295
|
+
e.emit('attackHit', { position: mouth, direction: dir, style: 'bite' });
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
eat: {
|
|
300
|
+
start(inst, A) {
|
|
301
|
+
const e = A.engine, L = e.cfg.L, tune = inst.tune;
|
|
302
|
+
inst.style = tune.style || 'suction';
|
|
303
|
+
if (inst.style === 'tear') { inst.dur = 1.2 + (tune.shake || 0) * 0.34; inst.side = e.rand() < 0.5 ? -1 : 1; inst.turn = undefined; return; }
|
|
304
|
+
const hBed = e.W[e.cfg.n + 1].y - e.groundAt(e.W[e.cfg.n + 1].x, e.W[e.cfg.n + 1].z);
|
|
305
|
+
if (inst.style === 'bottom' && hBed > 1.6 * L) inst.style = 'suction';
|
|
306
|
+
inst.dur = inst.style === 'bottom' ? 2.8 : 1.5;
|
|
307
|
+
if (inst.opts.target) {
|
|
308
|
+
const d = inst.opts.target.clone().sub(e.pos);
|
|
309
|
+
inst.turn = angDiff(e.heading, Math.atan2(d.x, d.z));
|
|
310
|
+
e.input.heading = e.heading + inst.turn;
|
|
311
|
+
}
|
|
312
|
+
inst.hBed = hBed;
|
|
313
|
+
},
|
|
314
|
+
update(a) { return a.t >= a.dur; },
|
|
315
|
+
apply(P, w, a, A) {
|
|
316
|
+
const e = A.engine, cfg = e.cfg, L = cfg.L, t = a.t;
|
|
317
|
+
if (a.style === 'tear') {
|
|
318
|
+
// sharks: a slow approach, a bite with the head lifted, shakes, release
|
|
319
|
+
mx(P, 'speed', Math.max(e.wantSpeed, 0.4 * L), 1); mx(P, 'speedW', bump(t, 0, 0.2, a.dur - 0.4, a.dur), w); mx(P, 'accelK', 1, w);
|
|
320
|
+
sharkBite(P, w, t, 0.1, a.tune, a.side);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (a.style === 'bottom') {
|
|
324
|
+
// head-down over the bed, two nips, back up
|
|
325
|
+
const down = bump(t, 0.1, 0.7, 2.0, 2.6);
|
|
326
|
+
mx(P, 'pitch', -0.75, 1); mx(P, 'pitchW', down, w); mx(P, 'pitchK', 1, w);
|
|
327
|
+
mx(P, 'speed', 0, 1); mx(P, 'speedW', 1, w);
|
|
328
|
+
const h = e.W[cfg.n + 1].y - e.groundAt(e.W[cfg.n + 1].x, e.W[cfg.n + 1].z);
|
|
329
|
+
mx(P, 'vy', -clamp((h - 0.06 * L) * 1.5, -0.3 * L, 0.35 * L) * bump(t, 0.2, 0.6, 1.8, 2.2), 1); mx(P, 'vyW', w * down, 1);
|
|
330
|
+
mx(P, 'groundW', 0.6, w);
|
|
331
|
+
const nip = bump(t, 0.85, 0.95, 1.05, 1.15) + bump(t, 1.35, 1.45, 1.55, 1.65);
|
|
332
|
+
mx(P, 'gape', 0.9 * nip, w); mx(P, 'gapeFast', 1, w); mx(P, 'protrude', nip, w);
|
|
333
|
+
mx(P, 'opFlare', 0.5, w * (bump(t, 1.1, 1.2, 1.3, 1.4) + bump(t, 1.6, 1.7, 1.8, 1.9)));
|
|
334
|
+
mx(P, 'pectAmp', 1.3, w); mx(P, 'flare', 0.3, w * down);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
// suction: a short lunge; the mouth snaps open (the buccal cavity expands), closes, then the
|
|
338
|
+
// gill covers flare to push the water out; two chews
|
|
339
|
+
const lunge = bump(t, 0.05, 0.15, 0.3, 0.5);
|
|
340
|
+
mx(P, 'speed', Math.max(e.wantSpeed, 2.2 * L), 1); mx(P, 'speedW', lunge, w); mx(P, 'accelK', 3, w);
|
|
341
|
+
const open = bump(t, 0.16, 0.24, 0.3, 0.4);
|
|
342
|
+
mx(P, 'gape', 1.0 * open, w); mx(P, 'gapeFast', 1, w); mx(P, 'protrude', open, w);
|
|
343
|
+
mx(P, 'opFlare', 0.9, w * bump(t, 0.38, 0.48, 0.62, 0.8));
|
|
344
|
+
const chew = 0.35 * (bump(t, 0.8, 0.88, 0.94, 1.02) + bump(t, 1.08, 1.16, 1.22, 1.3));
|
|
345
|
+
mx(P, 'gape', P.gape + chew, w);
|
|
346
|
+
if (a.turn !== undefined) { mxH(P, e.input.heading, w); mx(P, 'turnK', 2, w); }
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
drink: {
|
|
350
|
+
start(inst, A) {
|
|
351
|
+
const e = A.engine, L = e.cfg.L;
|
|
352
|
+
const surf = e.surfaceAt(e.pos.x, e.pos.z);
|
|
353
|
+
const reach = (inst.tune.reach ?? 3) * L;
|
|
354
|
+
inst.gulp = Number.isFinite(surf) && surf - e.pos.y < reach;
|
|
355
|
+
inst.surf = surf;
|
|
356
|
+
inst.dur = inst.gulp ? 3.0 : 1.5;
|
|
357
|
+
if (!inst.gulp) { inst.style = 'suction'; }
|
|
358
|
+
},
|
|
359
|
+
update(a) { return a.t >= a.dur; },
|
|
360
|
+
apply(P, w, a, A) {
|
|
361
|
+
if (!a.gulp) { ONESHOTS.eat.apply(P, w, a, A); return; }
|
|
362
|
+
// rise nose-up until the mouth touches the surface, gulp air / food, sink back
|
|
363
|
+
const e = A.engine, cfg = e.cfg, L = cfg.L, t = a.t;
|
|
364
|
+
const up = bump(t, 0.0, 0.5, 2.2, 2.9);
|
|
365
|
+
mx(P, 'speed', 0, 1); mx(P, 'speedW', 1, w);
|
|
366
|
+
mx(P, 'pitch', 0.5, 1); mx(P, 'pitchW', up, w);
|
|
367
|
+
const snout = e.W[cfg.n + 1];
|
|
368
|
+
const gap = a.surf - snout.y - 0.02 * L;
|
|
369
|
+
const vy = t < 2.1 ? clamp(gap * 3, -0.3 * L, 0.8 * L) : -0.6 * L;
|
|
370
|
+
mx(P, 'vy', vy, 1); mx(P, 'vyW', w * up, 1);
|
|
371
|
+
mx(P, 'surfaceOK', 1, w * up);
|
|
372
|
+
const g = bump(t, 1.1, 1.2, 1.35, 1.5) + bump(t, 1.6, 1.7, 1.85, 2.0);
|
|
373
|
+
mx(P, 'gape', 0.9 * g, w); mx(P, 'gapeFast', 1, w); mx(P, 'protrude', g, w);
|
|
374
|
+
mx(P, 'opFlare', 0.6, w * bump(t, 1.4, 1.5, 1.6, 1.75));
|
|
375
|
+
mx(P, 'pectAmp', 1.4, w);
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
jump: {
|
|
379
|
+
start(inst, A) {
|
|
380
|
+
const e = A.engine, L = e.cfg.L, tune = inst.tune;
|
|
381
|
+
const surf = e.surfaceAt(e.pos.x, e.pos.z);
|
|
382
|
+
const h = (tune.height ?? 1.5) * L;
|
|
383
|
+
inst.vBreach = Math.min(Math.sqrt(2 * 9.81 * h) * 1.1, e.cfg.burstSpeed * 1.4);
|
|
384
|
+
inst.surf = Number.isFinite(surf) && surf - e.pos.y < 10 * L ? surf : null;
|
|
385
|
+
inst.phase = 'rise';
|
|
386
|
+
inst.dur = 6;
|
|
387
|
+
inst.tAir = null;
|
|
388
|
+
A.stress = 1;
|
|
389
|
+
},
|
|
390
|
+
update(a, dt, A) {
|
|
391
|
+
const e = A.engine;
|
|
392
|
+
if (a.phase === 'rise' && e.air) { a.phase = 'air'; a.tAir = a.t; }
|
|
393
|
+
if (a.phase === 'air' && !e.air) { a.phase = 'dive'; a.tDive = a.t; }
|
|
394
|
+
if (a.phase === 'rise' && !a.surf && a.t > 0.9) { a.phase = 'dive'; a.tDive = a.t; }
|
|
395
|
+
if (a.phase === 'rise' && a.t > 4) { a.phase = 'dive'; a.tDive = a.t; }
|
|
396
|
+
return a.phase === 'dive' && a.t - a.tDive > 1.0;
|
|
397
|
+
},
|
|
398
|
+
apply(P, w, a, A) {
|
|
399
|
+
const e = A.engine, L = e.cfg.L, t = a.t;
|
|
400
|
+
if (a.phase === 'rise') {
|
|
401
|
+
// steep, fast ascent (from deeper water it first swims up)
|
|
402
|
+
const d = a.surf ? a.surf - e.pos.y : 2 * L;
|
|
403
|
+
const steep = smooth(0, 0.35, t);
|
|
404
|
+
mx(P, 'pitch', d > 3 * L ? 0.75 : 1.0, 1); mx(P, 'pitchW', w * steep, 1); mx(P, 'pitchK', 1.5, w);
|
|
405
|
+
mx(P, 'speed', a.vBreach, 1); mx(P, 'speedW', w * smooth(0.05, 0.3, t), 1); mx(P, 'accelK', 5, w);
|
|
406
|
+
mx(P, 'breach', 1, w); mx(P, 'surfaceOK', 1, w);
|
|
407
|
+
mx(P, 'fold', 1, w);
|
|
408
|
+
if (a.tune.bite && a.surf) {
|
|
409
|
+
// ambush from below: the jaws open on the way up
|
|
410
|
+
const near = smooth(1.4 * L, 0.3 * L, a.surf - e.pos.y);
|
|
411
|
+
mx(P, 'gape', 1.0 * near, w); mx(P, 'protrude', near, w); mx(P, 'headLift', 0.25 * near, w);
|
|
412
|
+
mx(P, 'eyeRoll', 1, w * near);
|
|
413
|
+
}
|
|
414
|
+
} else if (a.phase === 'air') {
|
|
415
|
+
const ta = t - a.tAir;
|
|
416
|
+
// arched in flight, tail still beating for a moment, fins spread
|
|
417
|
+
mx(P, 'arch', 0.5 * Math.sin(Math.min(1, ta / 0.5) * Math.PI), w);
|
|
418
|
+
mx(P, 'breach', 1, w); mx(P, 'surfaceOK', 1, w);
|
|
419
|
+
mx(P, 'ampK', 0.7 * (1 - smooth(0.1, 0.35, ta)), w);
|
|
420
|
+
mx(P, 'flare', 0.5, w); mx(P, 'fold', 0, w);
|
|
421
|
+
mx(P, 'bend', 0.25 * Math.sin(ta * 9) * (1 - smooth(0.1, 0.4, ta)), w);
|
|
422
|
+
if (a.tune.bite) {
|
|
423
|
+
// the bite on the way up, closing in the air
|
|
424
|
+
const cl = 1 - smooth(0.05, 0.35, ta);
|
|
425
|
+
mx(P, 'gape', cl, w); mx(P, 'protrude', cl, w); mx(P, 'headLift', 0.25 * cl, w); mx(P, 'eyeRoll', 1, w * (1 - smooth(0.3, 0.6, ta)));
|
|
426
|
+
}
|
|
427
|
+
if (a.tune.roll) {
|
|
428
|
+
// twists in the air and falls back on its side
|
|
429
|
+
if (a.rollSide === undefined) a.rollSide = e.rand() < 0.5 ? -1 : 1;
|
|
430
|
+
mx(P, 'roll', a.rollSide * a.tune.roll * smooth(0.0, 0.9, ta), 1); mx(P, 'rollW', w, 1); mx(P, 'rollK', 0.8, w);
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
// back under: level out and slow down to the previous depth
|
|
434
|
+
const td = t - a.tDive;
|
|
435
|
+
mx(P, 'pitch', -0.4 * (1 - smooth(0.2, 0.9, td)), 1); mx(P, 'pitchW', w, 1); mx(P, 'pitchK', 1, w);
|
|
436
|
+
mx(P, 'speed', e.wantSpeed, 1); mx(P, 'speedW', w * (1 - smooth(0.6, 1.0, td)), 1);
|
|
437
|
+
mx(P, 'brake', 1, w * (1 - smooth(0.3, 0.8, td)));
|
|
438
|
+
mx(P, 'surfaceOK', 1, w * (1 - smooth(0.0, 0.5, td)));
|
|
439
|
+
if (a.tune.roll && a.rollSide !== undefined) {
|
|
440
|
+
// rights itself under water
|
|
441
|
+
mx(P, 'roll', a.rollSide * a.tune.roll * (1 - smooth(0.1, 0.9, td)), 1); mx(P, 'rollW', w, 1); mx(P, 'rollK', 0.5, w);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// ------------------------------------------------------------------------------------------------
|
|
449
|
+
export class SwimmerActions {
|
|
450
|
+
constructor(engine) {
|
|
451
|
+
this.engine = engine;
|
|
452
|
+
this.tuning = engine.cfg.actions || {};
|
|
453
|
+
this.names = ['idle', 'burst', 'jump', 'sit', 'lie', 'sleep', 'eat', 'drink', 'attack', 'hit', 'death', 'stand'];
|
|
454
|
+
this.postures = [];
|
|
455
|
+
this.oneshots = [];
|
|
456
|
+
this.posture = 'stand';
|
|
457
|
+
this.standReq = null;
|
|
458
|
+
this.stress = 0;
|
|
459
|
+
this._slope = { pitch: 0, roll: 0 };
|
|
460
|
+
this.idle = { t: 0, lookT: 1, look: new THREE.Vector3(), hasLook: false, turnT: 4, w: 0, bob: 0 };
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// obligate ram ventilators (minSpeed > 0: sharks) keep patrolling in their rest postures
|
|
464
|
+
holdsStill() { const ram = this.engine.cfg.minSpeed > 0; return this.postures.some((p) => (p.target > 0 || p.w > 0.05) && (!ram || p.name === 'death')); }
|
|
465
|
+
dead() { return this.postures.some((p) => p.name === 'death' && p.target > 0); }
|
|
466
|
+
// automatic stand-up (asked to move): never revives a dead swimmer
|
|
467
|
+
requestStand() { if (!this.standReq && !this.dead()) this.play('stand'); }
|
|
468
|
+
// a leap out of the water cannot be cut
|
|
469
|
+
committed(a) { return !a.stopping && !!this.engine.air && (a.name === 'jump' || a.name === 'attack' || a.name === 'burst'); }
|
|
470
|
+
end(a, reason) { if (a.stopping) return; a.stopping = true; a.reason = reason; }
|
|
471
|
+
current() {
|
|
472
|
+
const o = this.oneshots.find((a) => !a.stopping);
|
|
473
|
+
if (o) return o.name;
|
|
474
|
+
const p = this.postures.find((x) => x.target > 0);
|
|
475
|
+
return p ? p.name : this.standReq ? 'stand' : null;
|
|
476
|
+
}
|
|
477
|
+
clear() {
|
|
478
|
+
for (const a of [...this.postures, ...this.oneshots]) { a.resolve?.('stopped'); a.resolveIn?.('stopped'); }
|
|
479
|
+
this.postures.length = 0; this.oneshots.length = 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
play(name, opts = {}) {
|
|
483
|
+
const e = this.engine;
|
|
484
|
+
if (name === 'idle') name = 'stand';
|
|
485
|
+
if (name === 'stand') {
|
|
486
|
+
if (!this.postures.length) return Promise.resolve('done');
|
|
487
|
+
for (const p of this.postures) if (p.target > 0) { p.target = 0; p.resolveIn?.('interrupted'); }
|
|
488
|
+
e.emit('actionStart', { name: 'stand' });
|
|
489
|
+
if (this.standReq) return this.standReq.promise;
|
|
490
|
+
let resolve;
|
|
491
|
+
const promise = new Promise((r) => { resolve = r; });
|
|
492
|
+
this.standReq = { promise, resolve };
|
|
493
|
+
return promise;
|
|
494
|
+
}
|
|
495
|
+
if (POSTURES[name]) {
|
|
496
|
+
const cur = this.postures.find((p) => p.target > 0);
|
|
497
|
+
if (cur && cur.name === 'death' && name !== 'death') return Promise.resolve('refused');
|
|
498
|
+
if (cur && cur.name === name) return cur.promiseIn;
|
|
499
|
+
if (name !== 'death' && this.oneshots.some((a) => this.committed(a))) return Promise.resolve('refused');
|
|
500
|
+
for (const p of this.postures) if (p.target > 0) { p.target = 0; p.resolveIn?.('interrupted'); }
|
|
501
|
+
if (this.standReq) { this.standReq.resolve('interrupted'); this.standReq = null; }
|
|
502
|
+
for (const a of this.oneshots) if (a.name !== 'hit' && !this.committed(a)) this.end(a, 'interrupted');
|
|
503
|
+
let side = opts.side ?? (e.rand() < 0.5 ? -1 : 1);
|
|
504
|
+
if (opts.direction && name === 'death') side = (opts.direction.x * Math.cos(e.heading) - opts.direction.z * Math.sin(e.heading)) > 0 ? -1 : 1;
|
|
505
|
+
const prev = this.postures.find((p) => p.name === 'lie' || p.name === 'sleep');
|
|
506
|
+
if (prev && opts.side === undefined) side = prev.side;
|
|
507
|
+
const def = POSTURES[name];
|
|
508
|
+
const inst = { name, t: 0, w: 0, target: 1, fadeIn: def.fadeIn / (opts.speed || 1), fadeOut: def.fadeOut, side, opts, tune: this.tuning[name] || {}, reached: false };
|
|
509
|
+
if (prev && name === 'sleep') { inst.w = prev.w; inst.t = prev.t; prev.w = 0; prev.target = 0; this.postures = this.postures.filter((p) => p !== prev); }
|
|
510
|
+
inst.promiseIn = new Promise((r) => { inst.resolveIn = r; });
|
|
511
|
+
this.postures.push(inst);
|
|
512
|
+
e.emit('actionStart', { name });
|
|
513
|
+
if (name === 'death') e.emit('death', { position: e.pos.clone() });
|
|
514
|
+
return inst.promiseIn;
|
|
515
|
+
}
|
|
516
|
+
const def = ONESHOTS[name];
|
|
517
|
+
if (!def) return Promise.reject(new Error(`procedural-animals: unknown action "${name}"`));
|
|
518
|
+
if (this.dead() && name !== 'hit') return Promise.resolve('refused');
|
|
519
|
+
if (this.holdsStill() && name !== 'hit') return this.play('stand').then((r) => (r === 'refused' ? r : this.play(name, opts)));
|
|
520
|
+
if (e.air && name !== 'hit') return Promise.resolve('refused');
|
|
521
|
+
// arbitration: a new one-shot interrupts the running ones (a leap in the air only takes a hit)
|
|
522
|
+
if (this.oneshots.some((a) => this.committed(a))) { for (const a of this.oneshots) if (a.name === name) this.end(a, 'interrupted'); }
|
|
523
|
+
else for (const a of this.oneshots) this.end(a, 'interrupted');
|
|
524
|
+
const inst = { name, t: 0, w: 0, dur: 1, stopping: false, opts, tune: { ...(this.tuning[name] || {}) }, fade: name === 'burst' || name === 'hit' ? 0.03 : 0.12, speed: opts.speed || 1 };
|
|
525
|
+
def.start(inst, this);
|
|
526
|
+
inst.promise = new Promise((r) => { inst.resolve = r; });
|
|
527
|
+
this.oneshots.push(inst);
|
|
528
|
+
e.emit('actionStart', { name });
|
|
529
|
+
return inst.promise;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
stop(name) {
|
|
533
|
+
for (const a of this.oneshots) if ((!name || a.name === name) && !this.committed(a)) this.end(a, 'stopped');
|
|
534
|
+
if (!name || POSTURES[name]) if (this.postures.some((p) => p.target > 0 && (name ? p.name === name : p.name !== 'death'))) this.play('stand');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
onLand() {}
|
|
538
|
+
|
|
539
|
+
update(dt) {
|
|
540
|
+
const e = this.engine, P = e.P;
|
|
541
|
+
this.stress = Math.max(0, this.stress - dt * 0.15);
|
|
542
|
+
resetParams(P, this.stress);
|
|
543
|
+
// ---- postures
|
|
544
|
+
for (const p of this.postures) {
|
|
545
|
+
p.t += dt;
|
|
546
|
+
const rate = p.target > p.w ? 1 / p.fadeIn : 1 / p.fadeOut;
|
|
547
|
+
p.w = clamp(p.w + clamp(p.target - p.w, -rate * dt, rate * dt), 0, 1);
|
|
548
|
+
const settled = p.name === 'death' ? p.t > 2.6 : p.name === 'lie' || p.name === 'sleep' ? (p.near ?? 1) > 0.9 || p.t > 6 : true;
|
|
549
|
+
if (!p.reached && p.target > 0 && p.w >= 1 && settled) { p.reached = true; p.resolveIn('done'); e.emit('actionEnd', { name: p.name }); }
|
|
550
|
+
}
|
|
551
|
+
this.postures = this.postures.filter((p) => p.target > 0 || p.w > 0);
|
|
552
|
+
const active = this.postures.find((p) => p.target > 0);
|
|
553
|
+
this.posture = active ? (active.name === 'death' ? 'dead' : active.name) : this.postures.length ? 'standing-up' : 'stand';
|
|
554
|
+
if (this.standReq && !this.postures.length) { this.standReq.resolve('done'); this.standReq = null; e.emit('actionEnd', { name: 'stand' }); }
|
|
555
|
+
// ---- idle
|
|
556
|
+
const L = e.cfg.L;
|
|
557
|
+
const still = e.speed < 0.08 * L && !e.input.follow && !e.input.target;
|
|
558
|
+
const idleTarget = still && !this.postures.length && !this.oneshots.some((a) => !a.stopping) ? 1 : 0;
|
|
559
|
+
const id = this.idle;
|
|
560
|
+
id.w += clamp(idleTarget - id.w, -dt * 1.5, dt * 0.7);
|
|
561
|
+
this.applyIdle(P, id.w, dt);
|
|
562
|
+
// ---- postures
|
|
563
|
+
for (const p of this.postures) {
|
|
564
|
+
const w = p.w * p.w * (3 - 2 * p.w);
|
|
565
|
+
POSTURES[p.name].apply(P, w, p, this);
|
|
566
|
+
p.near = p.near ?? 1;
|
|
567
|
+
}
|
|
568
|
+
// ---- one-shots
|
|
569
|
+
// (a ram ventilator's minimum patrol speed is not a request to move)
|
|
570
|
+
const wantsToMove = e.wantSpeed > Math.max(0.1 * L, e.cfg.minSpeed * 1.05) || !!e.input.follow;
|
|
571
|
+
for (const a of this.oneshots) {
|
|
572
|
+
if (wantsToMove && (a.name === 'eat' || a.name === 'drink') && a.t > 0.3) this.end(a, 'interrupted');
|
|
573
|
+
a.t += dt * a.speed;
|
|
574
|
+
const def = ONESHOTS[a.name];
|
|
575
|
+
if (!a.stopping && def.update(a, dt * a.speed, this)) this.end(a, 'done');
|
|
576
|
+
if (a.stopping) a.w = Math.max(0, a.w - dt / Math.max(0.05, a.fade * 2));
|
|
577
|
+
else a.w = Math.min(1, a.w + dt / a.fade);
|
|
578
|
+
const w = a.w * a.w * (3 - 2 * a.w);
|
|
579
|
+
if (w > 0) def.apply(P, w, a, this);
|
|
580
|
+
}
|
|
581
|
+
for (const a of this.oneshots) if (a.stopping && a.w <= 0) { a.resolve(a.reason || 'done'); e.emit('actionEnd', { name: a.name, reason: a.reason || 'done' }); }
|
|
582
|
+
this.oneshots = this.oneshots.filter((a) => !(a.stopping && a.w <= 0));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
applyIdle(P, w, dt) {
|
|
586
|
+
const e = this.engine, id = this.idle, L = e.cfg.L;
|
|
587
|
+
if (w <= 0.001) { id.hasLook = false; return; }
|
|
588
|
+
id.t += dt;
|
|
589
|
+
// slow vertical bob and a little pectoral asymmetry
|
|
590
|
+
id.bob += dt;
|
|
591
|
+
mx(P, 'vy', 0.03 * L * Math.sin(id.bob * 0.9) + 0.015 * L * Math.sin(id.bob * 2.3 + 1), 1); mx(P, 'vyW', w * 0.6, 1);
|
|
592
|
+
// occasional small reorientation (no target, no look request)
|
|
593
|
+
id.turnT -= dt;
|
|
594
|
+
if (id.turnT <= 0 && w > 0.9 && !e.input.look) {
|
|
595
|
+
id.turnT = 5 + e.rand() * 8;
|
|
596
|
+
if (e.rand() < 0.6) e.input.heading = e.heading + (e.rand() - 0.5) * 0.8;
|
|
597
|
+
}
|
|
598
|
+
if (!e.input.look) {
|
|
599
|
+
id.lookT -= dt;
|
|
600
|
+
if (id.lookT <= 0) {
|
|
601
|
+
id.lookT = 1.2 + e.rand() * 2.6;
|
|
602
|
+
if (e.rand() < 0.25) id.hasLook = false;
|
|
603
|
+
else {
|
|
604
|
+
const a = e.heading + (e.rand() - 0.5) * 2.2;
|
|
605
|
+
const d = (3 + e.rand() * 6) * L;
|
|
606
|
+
id.look.set(e.pos.x + Math.sin(a) * d, e.pos.y + (e.rand() - 0.5) * 2 * L, e.pos.z + Math.cos(a) * d);
|
|
607
|
+
id.hasLook = true;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (id.hasLook) { P.look = id.look; P.lookW = w; }
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|