# routine.ludic - going somewhere to do something, doing it here, and deciding what is next: the # kind's own plan first, then a weighted draw among its steps for this hour, falling through to the # next step whose spot can be found, and standing a while when none can export function npc_go(npc_st: NpcState, p: NpcPerson, x: float, z: float, act: int, dur: float) -> void { p.tx = x p.tz = z p.next_act = act p.next_t = dur p.act = NPCA_WALK p.via_on = false p.detours = 0 p.prog_t = 0.0 p.best = npc_d(p.x, p.z, x, z) p.fx = x + (x - p.x) p.fz = z + (z - p.z) p.speed = 1.1 + npc_rnd(npc_st) * 0.4 } # whatever it is, it is not done where the place says nobody stops: walk out of there first export function npc_do(npc_st: mut NpcState, p: NpcPerson, act: int, dur: float) -> void { if act != NPCA_WALK and not p.fixed and not NpcWorld.allowed(p.x, p.z) { if npc_spot_near(npc_st, p.x, p.z, 20.0, 60.0) { let fx = p.fx let fz = p.fz npc_go(npc_st, p, npc_st.np_px, npc_st.np_pz, act, dur) if act != NPCA_IDLE { p.fx = fx p.fz = fz } return } } p.act = act p.act_t = dur p.speed = 0.0 } export function npc_face(p: NpcPerson, x: float, z: float) -> void { p.fx = x p.fz = z } export function npc_face_ahead(p: NpcPerson) -> void { p.fx = p.x - Math.sin(p.yaw) * 10.0 p.fz = p.z - Math.cos(p.yaw) * 10.0 } # where it faces sitting at home: its own fire when it has one, else out ahead from home export function npc_set_home_face(p: NpcPerson, x: float, z: float) -> void { p.hfx = x p.hfz = z p.home_face = true } function np_in_hours(s: NpcStep, h: float) -> bool { if s.from <= s.to { return h >= s.from and h < s.to } return h >= s.from or h < s.to } # what next: the kind's plan, then its steps for this hour export function npc_plan(npc_st: mut NpcState, p: NpcPerson) -> void { p.stalk = false if p.fixed { npc_do(npc_st, p, NPCA_IDLE, 3600.0) return } let k = NpcKinds[p.kind] if k.plan != null { let plan = k.plan if plan(p) { return } } if k.steps != null and npc_steps(npc_st, p, k.steps) { return } npc_face_ahead(p) npc_do(npc_st, p, NPCA_IDLE, 10.0) } # a weighted draw among the steps whose hours hold now; a step whose spot cannot be found # gives way to the next one in the table, round to the start export function npc_steps(npc_st: NpcState, p: NpcPerson, steps: []NpcStep) -> bool { let h = NpcWorld.hour() var total = 0 for i in 0 .. len(steps) { if np_in_hours(steps[i], h) { total += np_pos(steps[i].weight) } } if total <= 0 { return false } var r = npc_between(npc_st, 0, total - 1) var first = -1 for i in 0 .. len(steps) { if not np_in_hours(steps[i], h) { continue } r -= np_pos(steps[i].weight) if r < 0 { first = i break } } for k in 0 .. len(steps) { let s = steps[(first + k) % len(steps)] if not np_in_hours(s, h) or s.spot < 0 or s.spot >= NPCS_COUNT { continue } let find = NpcSpots[s.spot].find if find != null and find(p, s) { return true } } return false } function np_pos(n: int) -> int { if n < 0 { return 0 } return n }