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>
87 lines
2.5 KiB
Text
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) }
|
|
}
|