ludic/packages/ludic.npc/walk.ludic
Orkuncakilkaya ab84a5f936 ludic.npc (17.7): a person's fallback is a fact
A step round what is in the way, a target planned again after twenty seconds without progress, and a target given up are each pushed as NPC_F_STUCK (how, x, z), so the game can see where the way failed a person. The lake test expects one when a walker turns back from the water; the way test expects none when the world gives the way.

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

87 lines
2.5 KiB
Text

# walk.ludic - walking to the target by the world's way where it has one (way.ludic): turn toward the
# next corner, step, slid round a trunk or a tent, never into the water and never up a scramble - step
# round it instead - and give up on a target that gets no closer for twenty seconds
const NP_BODY_R: float = 0.35 # a person's width, for the still things
export function npc_walk(npc_st: mut NpcState, p: NpcPerson, dt: float) -> void {
var gx = p.tx
var gz = p.tz
if p.via_on {
gx = p.vx
gz = p.vz
}
if npc_d(p.x, p.z, gx, gz) < 1.2 {
np_arrive(npc_st, p)
return
}
if not p.via_on {
np_way(p, dt)
gx = p.wx
gz = p.wz
}
let dx = gx - p.x
let dz = gz - p.z
let dg = npc_d(p.x, p.z, p.tx, p.tz)
if dg < p.best - 1.0 {
p.best = dg
p.prog_t = 0.0
} else { p.prog_t = p.prog_t + dt }
if p.prog_t > 20.0 {
np_stuck(npc_st, p)
return
}
var sp = p.speed
if sp < 0.1 { sp = 1.2 }
if p.stalk { sp = Math.min(sp, 0.7) }
let want = Math.atan2(-dx, -dz)
let r = 3.0 * dt
p.yaw = p.yaw + Math.clamp(npc_wrap(want - p.yaw), -r, r)
let step = sp * dt
np_slide(npc_st, p, p.x - Math.sin(p.yaw) * step, p.z - Math.cos(p.yaw) * step)
let nx = npc_st.np_ask_x
let nz = npc_st.np_ask_z
let nh = NpcWorld.ground(nx, nz)
let slid = Math.abs(nx - p.x) + Math.abs(nz - p.z)
if nh < NpcWorld.water(nx, nz) + 0.4 or nh - p.y > step * 1.4 or slid < step * 0.2 {
p.stall = p.stall + dt
if p.stall > p.stall_max { p.stall_max = p.stall }
if p.detours < 10 and np_detour(p, want) {
np_stuck_fact(npc_st, p, NPC_STUCK_DETOUR)
p.detours += 1
return
}
np_stuck(npc_st, p)
return
}
p.x = nx
p.z = nz
p.y = nh
p.stall = 0.0
p.phase = p.phase + step * 2.6
}
# (x, z) slid clear of every still thing for a person; np_ask_x / _z hold where it came out
function np_slide(npc_st: mut NpcState, p: NpcPerson, x: float, z: float) -> void {
npc_st.np_ask_x = x
npc_st.np_ask_z = z
if not NpcWorld.push(x, z, NP_BODY_R, p.y + 0.1, p.y + 1.8) { return }
let ox = NpcWorld.pushed_x()
let oz = NpcWorld.pushed_z()
npc_st.np_ask_x = ox
npc_st.np_ask_z = oz
}
# there: a step round something ends, or the errand begins, facing what it came for
function np_arrive(npc_st: mut NpcState, p: NpcPerson) -> void {
if p.via_on {
p.via_on = false
return
}
let fx = p.fx
let fz = p.fz
npc_do(npc_st, p, p.next_act, p.next_t)
p.fx = fx
p.fz = fz
p.stall = 0.0
if p.act != NPCA_WALK { np_fact(npc_st, NPC_F_ARRIVED, p, -1) }
}