refactor: move world concepts into engine

This commit is contained in:
Toad 2026-08-24 15:17:53 +02:00
parent eeedcb8e48
commit 2d15c7ab8d
52 changed files with 3298 additions and 1558 deletions

View file

@ -1,10 +1,8 @@
import type { Mob } from "../Mob"
import type { MobState } from "../Mob"
import { STRIDE, type Mesh } from "../../../engine/scene/Mesh"
// Shared building blocks for the per-kind mob definitions (Frog/Bee/Robin): the
// faceted geometry primitives and the deterministic wander helpers. Kept in its own
// module (no runtime import of `Mob`, only its type) so the per-kind files and the
// `Mob` registry don't form an import cycle.
// Shared building blocks for concrete mob definitions: faceted geometry primitives
// and deterministic wander helpers.
export const TAU = Math.PI * 2
@ -13,7 +11,11 @@ export const TAU = Math.PI * 2
/** A new heading: free wander when inside the leash, else biased back toward home
* so the mob never drifts off into the peaks (`jitter` = the random cone half-width
* in radians layered on top of the homeward bearing). */
export function wanderHeading(mob: Mob, leash: number, jitter: number): number {
export function wanderHeading(
mob: MobState,
leash: number,
jitter: number,
): number {
const dx = mob.home.x - mob.position.x
const dz = mob.home.z - mob.position.z
if (dx * dx + dz * dz > leash * leash) {
@ -24,7 +26,7 @@ export function wanderHeading(mob: Mob, leash: number, jitter: number): number {
/** mulberry32 step over the mob's own `seed` (mutated), so a mob's motion is
* deterministic and needs no external RNG object to clone. */
export function nextRand(mob: Mob): number {
export function nextRand(mob: MobState): number {
const a = (mob.seed + 0x6D2B79F5) | 0
mob.seed = a
let t = Math.imul(a ^ (a >>> 15), 1 | a)
@ -33,7 +35,7 @@ export function nextRand(mob: Mob): number {
}
// --- Geometry primitives --------------------------------------------------
// Mobs are drawn double-sided (see renderScene), so winding is not load-bearing --
// Mobs are drawn double-sided, so winding is not load-bearing --
// these only need to place faceted, flat-shaded surfaces.
/** A UV-rected ellipsoid (pole on Y), faceted like the boulders. */
@ -61,7 +63,13 @@ export function ellipsoid(
for (let is = 0; is <= seg; is++) {
const theta = (is / seg) * TAU
const u = u0 + (u1 - u0) * (is / seg)
mesh.verts.push(cx + crv * Math.cos(theta) * rx, cy + cyv * ry, cz + crv * Math.sin(theta) * rz, u, v)
mesh.verts.push(
cx + crv * Math.cos(theta) * rx,
cy + cyv * ry,
cz + crv * Math.sin(theta) * rz,
u,
v,
)
}
}
quadGrid(mesh, start, seg, rings)
@ -97,13 +105,36 @@ export function ovoidZ(
}
/** One flat wing quad on `side` (+1 right / -1 left), swept up and out. */
export function wing(mesh: Mesh, side: number, u0: number, u1: number, v0: number, v1: number): void {
export function wing(
mesh: Mesh,
side: number,
u0: number,
u1: number,
v0: number,
v1: number,
): void {
const base = mesh.verts.length / STRIDE
mesh.verts.push(
side * 0.06, 0.12, 0.14, u0, v0,
side * 0.42, 0.24, 0.1, u1, v0,
side * 0.42, 0.24, -0.12, u1, v1,
side * 0.06, 0.12, -0.1, u0, v1,
side * 0.06,
0.12,
0.14,
u0,
v0,
side * 0.42,
0.24,
0.1,
u1,
v0,
side * 0.42,
0.24,
-0.12,
u1,
v1,
side * 0.06,
0.12,
-0.1,
u0,
v1,
)
mesh.indices.push(base, base + 1, base + 2, base, base + 2, base + 3)
}