ludic/packages/ludic.wildlife/way.ludic
Orkuncakilkaya 81b6782c9d ludic.wildlife (18.2): a walker a crowd moves
wildlife_set_crowd(a, true) hands a walker to a crowd. Its step no longer moves it, and its want is two fields on it: goal_x / goal_z (the point it heads for or faces) and goal_speed (0 when it stands). A route reads those and hands back where the crowd put it through wildlife_moved(a, x, z, vx, vz). That sets its place, its ground, its heading from its velocity and its prints, and updates its row's columns so the spatial index finds it there. Nothing allocates per animal per frame: the want is the animal's own fields, and moved writes in place. The prints and the turn counters moved to walked.ludic (steer.ludic was at the 100-line limit). crowd_test holds it; with crowd off every old test is unchanged. wildlife 26/26.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:27:12 +03:00

58 lines
2 KiB
Text

# way.ludic - where an animal walks: by the world's path to where it is going (WildlifeWorld.way, a
# navmesh by its habitat's taste in Maroon Lake), to a target about home it can reach (goal), and
# straight, as before, where the world has no answer
# face the way toward (tx, tz): the next corner of the world's path for it, asked again every
# WL_WAY_EVERY seconds or on reaching the corner; without a path, the straight line as before
const WL_WAY_EVERY: float = 0.5
function wl_head(a: WildAnimal, tx: float, tz: float, rate: float, dt: float) -> void {
if a.crowd {
a.goal_x = tx
a.goal_z = tz
return
}
a.way_t = a.way_t - dt
let cx = a.wx - a.x
let cz = a.wz - a.z
if a.way_t <= 0.0 or cx * cx + cz * cz < 1.0 {
a.way_t = WL_WAY_EVERY
a.wx = tx
a.wz = tz
if WildlifeWorld.way(a, tx, tz) {
a.wx = WildlifeWorld.way_x()
a.wz = WildlifeWorld.way_z()
}
}
wl_face(a, a.wx, a.wz, rate, dt)
}
# somewhere about home: a spot the world says it can walk to when it knows, seeded by the same two
# draws the old target took, so the dice are the same whether it knows or not
function wl_pick_target(wildlife_st: WildlifeState, a: WildAnimal, radius: float) -> void {
let ang = wl_rnd(wildlife_st) * WL_TAU
let d = wl_rnd(wildlife_st) * radius
a.tx = a.home_x + Math.cos(ang) * d
a.tz = a.home_z + Math.sin(ang) * d
a.way_t = 0.0
if WildlifeWorld.goal(a, a.home_x, a.home_z, radius, int(ang * 100000.0) + int(d * 1000.0)) {
a.tx = WildlifeWorld.way_x()
a.tz = WildlifeWorld.way_z()
}
}
# a target d metres straight away from (fx, fz): where it runs or backs off to, by the world's way -
# round water and a cliff where there is one, and straight away as before where there is not
function wl_away(a: WildAnimal, fx: float, fz: float, d: float) -> void {
var dx = a.x - fx
var dz = a.z - fz
let l = Math.sqrt(dx * dx + dz * dz)
if l < 0.01 {
dx = -Math.sin(a.yaw)
dz = -Math.cos(a.yaw)
} else {
dx = dx / l
dz = dz / l
}
a.tx = a.x + dx * d
a.tz = a.z + dz * d
}