# census.ludic - making one (a slot reused before a new one, a look, a name, a start on its own # ground out of the players' sight where that can be had), a post's fixed person, and leaving export function npc_spawn(npc_st: mut NpcState, kind: int) -> NpcPerson { if npc_st.np_n >= npc_st.np_max or kind < 0 or kind >= NPCK_COUNT { return null } var p: NpcPerson = null for i in 0 .. len(npc_st.np_walkers) { if not npc_st.np_walkers[i].active { p = npc_st.np_walkers[i] break } } @alloc_ok("a walker for each of np_max at most; one who left is reused") if p == null { if len(npc_st.np_walkers) >= npc_st.np_max { return null } p = new NpcPerson p.slot = len(npc_st.np_walkers) push(npc_st.np_walkers, p) } np_fresh(p, kind, npc_between(npc_st, 1, 99999)) p.name = npc_pick_name(npc_st, p.body) let k = NpcKinds[kind] p.leg = npc_between(npc_st, 0, 3) p.seed = npc_rnd(npc_st) * 40.0 p.out_h = k.leave_lo + npc_rnd(npc_st) * (k.leave_hi - k.leave_lo) p.yaw = npc_rnd(npc_st) * NP_TAU if k.start != null { let start = k.start start(p) } else { npc_start_near(npc_st, p, NpcWorld.origin_x(), NpcWorld.origin_z(), 6.0, 30.0) } if not NpcWorld.active(p.x, p.z) { return null } # nobody is born outside every zone; the slot waits p.active = true npc_st.np_n += 1 NpcWorld.born(p) npc_plan(npc_st, p) return p } function np_fresh(p: NpcPerson, kind: int, look: int) -> void { p.kind = kind p.id = NpcWorld.next_id() p.look = look p.body = NpcWorld.body_of(look) p.fixed = false p.said = -1 p.near = 0 p.goal = -1 p.subj = -1 p.note = -1 p.note_h = 0.0 p.flags = 0 p.stalk = false p.stall = 0.0 p.stall_max = 0.0 p.fails = 0 p.phase = 0.0 p.home_face = false p.heard = false p.act = NPCA_IDLE p.act_t = 0.0 p.speed = 0.0 p.via_on = false } # its own ground: a spot r0 .. r1 from (cx, cz), tried a few times for one no player is within 60 m # of, else near the place's origin; that is home, where it stands and faces export function npc_start_near(npc_st: mut NpcState, p: NpcPerson, cx: float, cz: float, r0: float, r1: float) -> void { var x = NpcWorld.origin_x() var z = NpcWorld.origin_z() var ok = false for t in 0 .. 8 { if not npc_spot_near(npc_st, cx, cz, r0, r1) { continue } x = npc_st.np_px z = npc_st.np_pz ok = true if npc_near(npc_st, x, z) > 60.0 { break } } if not ok and npc_spot_near(npc_st, x, z, 10.0, 45.0) { x = npc_st.np_px z = npc_st.np_pz } npc_set_at(p, x, z) } # put it down at (x, z) as its home, stood still export function npc_set_at(p: NpcPerson, x: float, z: float) -> void { p.x = x p.z = z p.y = NpcWorld.ground(x, z) p.hx = x p.hz = z p.tx = x p.tz = z } # somebody fixed at a post (a vendor behind the counter): never walks, never counted, never leaves, # turns to a player within its kind's notice and back to `yaw` when they go export function npc_place(npc_st: mut NpcState, kind: int, x: float, z: float, yaw: float, name: string, look: int) -> NpcPerson { let p = new NpcPerson p.slot = len(npc_st.np_posts) np_fresh(p, kind, look) p.fixed = true p.name = name p.seed = float(p.slot) * 0.9 p.stance = yaw p.yaw = yaw npc_set_at(p, x, z) p.active = true push(npc_st.np_posts, p) NpcWorld.born(p) npc_do(npc_st, p, NPCA_IDLE, 3600.0) return p } export function npc_retire(npc_st: mut NpcState, p: NpcPerson) -> void { if not p.active { return } p.active = false if not p.fixed and npc_st.np_n > 0 { npc_st.np_n -= 1 } np_left(npc_st, p.name) NpcWorld.gone(p) }