feat: bees and frogs
This commit is contained in:
parent
46e7070b6b
commit
16f205babe
13 changed files with 540 additions and 31 deletions
52
app/level.ts
52
app/level.ts
|
|
@ -4,6 +4,7 @@ import { STRIDE, type Mesh } from "../engine/scene/Mesh"
|
|||
import { Boulder } from "../engine/scene/Boulder"
|
||||
import { Bush } from "../engine/scene/Bush"
|
||||
import { Flower, type FlowerColor } from "../engine/scene/Flower"
|
||||
import type { Mob, MobKind } from "../engine/scene/Mob"
|
||||
import { Terrain } from "../engine/scene/Terrain"
|
||||
import { Tree } from "../engine/scene/Tree"
|
||||
|
||||
|
|
@ -58,6 +59,9 @@ export type Level = {
|
|||
chunks: Chunk[]
|
||||
colliders: Aabb[]
|
||||
npcPosition: { x: number; y: number; z: number }
|
||||
/** Roaming mobs -- simulated on the main thread each frame (see main.ts), not
|
||||
* baked into the static culled chunks. */
|
||||
mobs: Mob[]
|
||||
terrain: Terrain
|
||||
sky: SkyConfig
|
||||
}
|
||||
|
|
@ -112,6 +116,14 @@ const FLOWER_SEED = 0xF10E
|
|||
const FLOWER_REACH = 0.3
|
||||
const FLOWER_COLORS: FlowerColor[] = ["white", "red", "yellow"]
|
||||
|
||||
/** Roaming mobs: how many frogs/bees to scatter, their seed, and how far out they
|
||||
* reach (fraction of the world). Kept modest -- roaming meshes are drawn every
|
||||
* frame (frustum-culled), not baked into the static chunks. */
|
||||
const FROG_COUNT = 40
|
||||
const BEE_COUNT = 30
|
||||
const MOB_SEED = 0x30B
|
||||
const MOB_REACH = 0.5
|
||||
|
||||
/** Spatial partition of the world for frustum culling: `CHUNK_GRID` x
|
||||
* `CHUNK_GRID` square cells over [-outer, outer]. Smaller cells cull tighter
|
||||
* (less drawn off-screen) but cost more per-cell tests + bounds; this is the
|
||||
|
|
@ -199,8 +211,9 @@ export function buildLevel(): Level {
|
|||
const bushes = placeBushes()
|
||||
const flowers = placeFlowers()
|
||||
const chunks = buildChunks(trees, boulders, bushes, flowers)
|
||||
const mobs = placeMobs()
|
||||
|
||||
return { floor, walls, crate, chunks, colliders, npcPosition, terrain: TERRAIN, sky }
|
||||
return { floor, walls, crate, chunks, colliders, npcPosition, mobs, terrain: TERRAIN, sky }
|
||||
}
|
||||
|
||||
/** Bake the terrain + props into a `CHUNK_GRID` x `CHUNK_GRID` set of spatial
|
||||
|
|
@ -383,6 +396,43 @@ function placeFlowers(): Flower[] {
|
|||
return flowers
|
||||
}
|
||||
|
||||
/** Scatter frogs + bees across the grass (like the boulders), each at its home
|
||||
* anchor with a random heading and size. No colliders here -- mobs move, so their
|
||||
* block/stand-on AABBs are rebuilt per frame in `main`. */
|
||||
function placeMobs(): Mob[] {
|
||||
const rand = mulberry(MOB_SEED)
|
||||
const maxDist = TERRAIN.outer * MOB_REACH
|
||||
const mobs: Mob[] = []
|
||||
const total = FROG_COUNT + BEE_COUNT
|
||||
for (let guard = 0; mobs.length < total && guard < total * 20; guard++) {
|
||||
const angle = rand() * Math.PI * 2
|
||||
const dist = ARENA + 3 + rand() * (maxDist - ARENA - 3)
|
||||
const x = Math.cos(angle) * dist
|
||||
const z = Math.sin(angle) * dist
|
||||
if (Math.max(Math.abs(x), Math.abs(z)) < TERRAIN.inner + 2) {
|
||||
continue
|
||||
}
|
||||
const kind: MobKind = mobs.length < FROG_COUNT ? "frog" : "bee"
|
||||
const y = Terrain.height(TERRAIN, x, z)
|
||||
const scale = kind === "frog" ? 0.5 + rand() * 0.35 : 0.5 + rand() * 0.3
|
||||
mobs.push({
|
||||
kind,
|
||||
home: { x, y, z },
|
||||
position: { x, y, z },
|
||||
heading: rand() * Math.PI * 2,
|
||||
scale,
|
||||
seed: (rand() * 0xFFFFFFFF) | 0,
|
||||
vx: 0,
|
||||
vz: 0,
|
||||
vy: 0,
|
||||
timer: rand() * 1.5,
|
||||
phase: rand() * 10,
|
||||
grounded: true,
|
||||
})
|
||||
}
|
||||
return mobs
|
||||
}
|
||||
|
||||
/** Deterministic 0..1 generator (mulberry32) for tree placement. */
|
||||
function mulberry(seed: number): () => number {
|
||||
let a = seed >>> 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue