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 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, } } }