# way.ludic - where a person walks when the straight line will not do: the world's way (NpcWorld.way, # a navmesh by a person's taste in Maroon Lake), and without one a step round what is in the way, # and giving the target up after three tries - each of those a NPC_F_STUCK fact, so the fallback is seen export const NPC_STUCK_DETOUR: int = 0 # a step round what was in front of it export const NPC_STUCK_RETRY: int = 1 # no closer for twenty seconds, or no step round: planned again export const NPC_STUCK_GAVE_UP: int = 2 # the third of those: the target given up function np_stuck_fact(npc_st: mut NpcState, p: NpcPerson, how: int) -> void { let f = np_fact(npc_st, NPC_F_STUCK, p, -1) f.how = how f.x = p.x f.z = p.z } # the next corner toward the target, asked again every half second, on reaching it, or for a new target const NP_WAY_EVERY: float = 0.5 function np_way(p: NpcPerson, dt: float) -> void { p.way_t = p.way_t - dt let fresh = p.wtx != p.tx or p.wtz != p.tz if p.way_t > 0.0 and not fresh and npc_d(p.x, p.z, p.wx, p.wz) >= 1.0 { return } p.way_t = NP_WAY_EVERY p.wtx = p.tx p.wtz = p.tz p.wx = p.tx p.wz = p.tz if NpcWorld.way(p, p.tx, p.tz) { p.wx = NpcWorld.way_x() p.wz = NpcWorld.way_z() } } function np_detour(p: NpcPerson, want: float) -> bool { for k in 0 .. 8 { let ang = want + float((k / 2 + 1) * (1 - 2 * (k % 2))) * 0.6 let x = p.x - Math.sin(ang) * 6.0 let z = p.z - Math.cos(ang) * 6.0 if npc_line_ok(p.x, p.z, x, z) { p.via_on = true p.vx = x p.vz = z p.yaw = ang return true } } return false } function np_stuck(npc_st: mut NpcState, p: NpcPerson) -> void { p.fails += 1 p.via_on = false if p.fails >= 3 { np_stuck_fact(npc_st, p, NPC_STUCK_GAVE_UP) p.fails = 0 npc_wander(npc_st, p) } else { np_stuck_fact(npc_st, p, NPC_STUCK_RETRY) npc_plan(npc_st, p) } } # the crowd's side (phase 18): a crowd walker says where it is going and how fast, and is handed # back where the crowd put it; nothing allocates, since this runs for every walker every frame function np_crowd_want(p: NpcPerson, speed: float) -> void { p.goal_x = p.tx p.goal_z = p.tz p.goal_speed = speed } export function npc_set_crowd(p: NpcPerson, id: int) -> void { p.crowd_id = id p.crowd = id >= 0 } export function npc_moved(p: NpcPerson, x: float, z: float, vx: float, vz: float) -> void { let dx = x - p.x let dz = z - p.z p.x = x p.z = z p.y = NpcWorld.ground(x, z) if vx * vx + vz * vz > 0.01 { p.yaw = Math.atan2(-vx, -vz) } p.phase = p.phase + Math.sqrt(dx * dx + dz * dz) * 2.6 } # a fact record no drain holds any more, made fresh (and kept) only when every one is held function np_fact_record(npc_st: mut NpcState) -> NpcFact { if npc_st.np_fnext >= len(npc_st.np_ffree) { q_unheld(npc_st.np_facts, npc_st.np_fpool, npc_st.np_ffree) npc_st.np_fnext = 0 } if npc_st.np_fnext < len(npc_st.np_ffree) { let f = npc_st.np_ffree[npc_st.np_fnext] npc_st.np_fnext += 1 f.line = -1 f.choice = -1 f.how = 0 f.x = 0.0 f.z = 0.0 return f } return np_fact_more(npc_st) } # how many fact records have ever been made: the peak held at once, not the facts pushed export function npc_fact_records(npc_st: NpcState) -> int { return len(npc_st.np_fpool) }