ludic/packages/ludic.npc/ground.ludic
Orkuncakilkaya 56e4b6d688 feat(senses): walkers slide round still things, and lines of sight are the world's to answer
- ludic.wildlife: WildlifeWorld.push / pushed_x / pushed_z (defaults change nothing); a ground
  walker's step is slid clear of a still thing with a body from its species' size and scale, and
  a step that gets nowhere turns away as the water does. Birds are not pushed; no dice added.
- ludic.npc: NpcWorld.push / pushed_x / pushed_z (a person 0.35 m round) in npc_walk, and
  NpcWorld.clear (default true), which npc_line_ok asks at 1 m so a detour does not cut a trunk.
- ludic.aim: AimView.clear replaces AimView.ground and the ground march; the pick asks the line
  a body's width short of where the ray enters the thing (aim_clear_at(t, back)), so a solid
  thing does not hide itself. probe.ludic (the ground-agreement probe) and AimView.dry are gone.
- ludic.photo: PhotoLens.clear replaces PhotoLens.ground, PhotoLens.trunk and the march.

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

98 lines
3.3 KiB
Text

# ground.ludic - where a person can stand and walk: dry, gentle, allowed; a straight walk with no
# water, scramble or still thing on it; a spot in a ring, a spot toward something, the nearest shore.
# A finder that succeeds leaves its answer in npc_px / npc_pz (and the water in npc_wx / npc_wz).
export function npc_px(npc_st: NpcState) -> float { return npc_st.np_px }
export function npc_pz(npc_st: NpcState) -> float { return npc_st.np_pz }
export function npc_wx(npc_st: NpcState) -> float { return npc_st.np_wx }
export function npc_wz(npc_st: NpcState) -> float { return npc_st.np_wz }
export function npc_walkable(x: float, z: float) -> bool {
if not NpcWorld.allowed(x, z) { return false }
if NpcWorld.ground(x, z) < NpcWorld.water(x, z) + 0.6 { return false }
return NpcWorld.slope(x, z) < 0.75
}
export function npc_line_ok(ax: float, az: float, bx: float, bz: float) -> bool {
let d = npc_d(ax, az, bx, bz)
var n = int(d / 1.5)
if n < 1 { n = 1 }
var prev = NpcWorld.ground(ax, az)
if not NpcWorld.clear(ax, prev + 1.0, az, bx, NpcWorld.ground(bx, bz) + 1.0, bz) { return false } # a trunk, a tent
for i in 1 .. n + 1 {
let t = float(i) / float(n)
let x = ax + (bx - ax) * t
let z = az + (bz - az) * t
let h = NpcWorld.ground(x, z)
if h < NpcWorld.water(x, z) + 0.5 { return false }
if h - prev > d / float(n) * 1.3 { return false }
prev = h
}
return true
}
function np_found(npc_st: mut NpcState, x: float, z: float) -> bool {
npc_st.np_px = x
npc_st.np_pz = z
return true
}
# somewhere to stand d0 .. d1 from a point
export function npc_spot_near(npc_st: mut NpcState, x0: float, z0: float, d0: float, d1: float) -> bool {
for t in 0 .. 40 {
let ang = npc_rnd(npc_st) * NP_TAU
let d = d0 + npc_rnd(npc_st) * (d1 - d0)
let x = x0 + Math.cos(ang) * d
let z = z0 + Math.sin(ang) * d
if npc_walkable(x, z) { return np_found(npc_st, x, z) }
}
return false
}
# a place to stand d from (ax, az), on the side (bx, bz) is on
export function npc_toward(npc_st: mut NpcState, ax: float, az: float, bx: float, bz: float, d: float) -> bool {
let dd = Math.max(npc_d(ax, az, bx, bz), 0.5)
let x = ax + (bx - ax) / dd * d
let z = az + (bz - az) / dd * d
if npc_walkable(x, z) { return np_found(npc_st, x, z) }
for k in 0 .. 12 {
let ang = Math.atan2(bz - az, bx - ax) + float((k + 1) / 2 * (1 - 2 * (k % 2))) * 0.25
let x2 = ax + Math.cos(ang) * d
let z2 = az + Math.sin(ang) * d
if npc_walkable(x2, z2) { return np_found(npc_st, x2, z2) }
}
return npc_spot_near(npc_st, x, z, 0.0, 14.0)
}
# the nearest bit of shore within rmax: where to stand, and the water in front of it
export function npc_shore(npc_st: mut NpcState, x0: float, z0: float, rmax: float) -> bool {
var found = false
var bd = rmax
for k in 0 .. 16 {
let ang = float(k) * (NP_TAU / 16.0)
var lx = x0
var lz = z0
var ld = -100.0
var d = 2.0
while d < bd {
let x = x0 + Math.cos(ang) * d
let z = z0 + Math.sin(ang) * d
if NpcWorld.ground(x, z) < NpcWorld.water(x, z) {
if ld > 0.0 and d - ld < 12.0 {
bd = d
found = np_found(npc_st, lx, lz)
npc_st.np_wx = x
npc_st.np_wz = z
}
break
}
if npc_walkable(x, z) {
lx = x
lz = z
ld = d
}
d = d + 2.0
}
}
return found
}