64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import type { Vec3 } from "engine/math/Vec3"
|
|
import type { Actor } from "engine/scene/Actor"
|
|
import type { RenderTransform } from "engine/render/RenderScene"
|
|
import type { BoxCollider } from "engine/world/Collider"
|
|
import type { Terrain } from "engine/world/Terrain"
|
|
|
|
/** Runtime actor using shared roaming-creature state. Concrete definitions are
|
|
* direct object references supplied by Frog, Bee, Robin, or future content. */
|
|
export type Mob = Actor<Terrain>
|
|
|
|
export type MobState = {
|
|
/** Leash anchor (where it was scattered); wandering is pulled back toward it. */
|
|
home: Vec3
|
|
/** Live feet-center (frog/robin) / body-center (bee), advanced each frame. */
|
|
position: Vec3
|
|
/** Facing yaw; the mesh's front is local +Z, so world dir = (sin h, 0, cos h). */
|
|
heading: number
|
|
/** Per-instance size multiplier. */
|
|
scale: number
|
|
/** Evolving RNG state (mutated by `update`) -- keeps the sim deterministic. */
|
|
seed: number
|
|
/** Horizontal velocity (frog/robin: mid-hop or -flight; bee: cruise). */
|
|
vx: number
|
|
vz: number
|
|
/** Vertical velocity (frog/robin ballistic hop/flight; bee stays 0, uses a bob). */
|
|
vy: number
|
|
/** Countdown to the next decision (frog/robin: next hop; bee: next heading change). */
|
|
timer: number
|
|
/** Behavior scratch clock: the bee's hover-bob phase; the robin's remaining
|
|
* powered-flight cruise time (>0 while gliding between perches). */
|
|
phase: number
|
|
/** Frog/robin: resting on the ground vs airborne (a hop or a flight). */
|
|
grounded: boolean
|
|
}
|
|
|
|
export namespace Mob {
|
|
export function transform(state: MobState): RenderTransform {
|
|
return {
|
|
x: state.position.x,
|
|
y: state.position.y,
|
|
z: state.position.z,
|
|
heading: state.heading,
|
|
scale: state.scale,
|
|
}
|
|
}
|
|
|
|
export function collider(
|
|
state: MobState,
|
|
radius: number,
|
|
height: number,
|
|
standable: boolean,
|
|
): BoxCollider {
|
|
const half = radius * state.scale * 0.7
|
|
return {
|
|
shape: "box",
|
|
minX: state.position.x - half,
|
|
maxX: state.position.x + half,
|
|
minZ: state.position.z - half,
|
|
maxZ: state.position.z + half,
|
|
top: state.position.y + height * state.scale,
|
|
standable,
|
|
}
|
|
}
|
|
}
|