diff --git a/packages/ludic.npc/defs.ludic b/packages/ludic.npc/defs.ludic index 2352a657..f9da8e17 100644 --- a/packages/ludic.npc/defs.ludic +++ b/packages/ludic.npc/defs.ludic @@ -52,6 +52,10 @@ export property NpcPerson { wtx: float = 0.0 # the target that corner was asked for: a new target asks again wtz: float = 0.0 way_t: float = 0.0 # seconds until it asks the way again + crowd: bool = false # walked by the world's crowd (phase 18): it decides, the crowd moves it + goal_x: float = 0.0 # where it walks this tick and how fast (0 standing): what a crowd is handed + goal_z: float = 0.0 + goal_speed: float = 0.0 detours: int = 0 fails: int = 0 stall: float = 0.0 # seconds walking without moving diff --git a/packages/ludic.npc/tests/way_test.ludic b/packages/ludic.npc/tests/way_test.ludic index b76fda18..256c153b 100644 --- a/packages/ludic.npc/tests/way_test.ludic +++ b/packages/ludic.npc/tests/way_test.ludic @@ -81,4 +81,24 @@ program NpcWayTest { let fs = q_drain(npc_facts(npc_st)) for i in 0 .. len(fs) { expect(fs[i].what != NPC_F_STUCK) } } + + test "a crowd walker says where and how fast, is moved by hand to its target, and begins there" (npc_st: mut NpcState, npc_tests_fake_st: mut NpcTestsFakeState, way_test_st: WayTestState) { + fk_setup(npc_st, npc_tests_fake_st) + npc_tests_fake_st.fk_want = 1 + let p = npc_spawn(npc_st, NPCK_HIKER) + npc_set_at(p, 0.0, 0.0) + npc_set_crowd(p, true) + npc_go(npc_st, p, 38.0, -25.0, NPCA_SIT, 30.0) + for k in 0 .. 60 { npc_tick(npc_st, 0.05) } + expect_eq(p.x, 0.0) + expect_eq(p.z, 0.0) + expect_eq(p.goal_x, 38.0) + expect_eq(p.goal_z, -25.0) + expect(p.goal_speed > 0.5) + expect_eq(way_test_st.asked, 0) + for k in 1 .. 101 { npc_moved(p, 0.38 * float(k), -0.25 * float(k), 1.0, -0.66) } + npc_tick(npc_st, 0.05) + expect_eq(p.act, NPCA_SIT) + expect(p.phase > 100.0) + } } diff --git a/packages/ludic.npc/tick.ludic b/packages/ludic.npc/tick.ludic index 72619bbe..1e9da316 100644 --- a/packages/ludic.npc/tick.ludic +++ b/packages/ludic.npc/tick.ludic @@ -7,6 +7,7 @@ export function npc_tick(npc_st: mut NpcState, dt: float) -> void { for i in 0 .. len(npc_st.np_walkers) { let p = npc_st.np_walkers[i] if not p.active or np_leaves(npc_st, p) { continue } + p.goal_speed = 0.0 if p.act == NPCA_WALK { npc_walk(npc_st, p, dt) } else { npc_act(npc_st, p, dt) } npc_notice(npc_st, p) } diff --git a/packages/ludic.npc/walk.ludic b/packages/ludic.npc/walk.ludic index 6f416cc4..847627fa 100644 --- a/packages/ludic.npc/walk.ludic +++ b/packages/ludic.npc/walk.ludic @@ -14,7 +14,7 @@ export function npc_walk(npc_st: mut NpcState, p: NpcPerson, dt: float) -> void np_arrive(npc_st, p) return } - if not p.via_on { + if not p.via_on and not p.crowd { np_way(p, dt) gx = p.wx gz = p.wz @@ -33,6 +33,10 @@ export function npc_walk(npc_st: mut NpcState, p: NpcPerson, dt: float) -> void var sp = p.speed if sp < 0.1 { sp = 1.2 } if p.stalk { sp = Math.min(sp, 0.7) } + if p.crowd { + np_crowd_want(p, sp) + return + } let want = Math.atan2(-dx, -dz) let r = 3.0 * dt p.yaw = p.yaw + Math.clamp(npc_wrap(want - p.yaw), -r, r) diff --git a/packages/ludic.npc/way.ludic b/packages/ludic.npc/way.ludic index d0ffa8ad..e819cdd4 100644 --- a/packages/ludic.npc/way.ludic +++ b/packages/ludic.npc/way.ludic @@ -56,3 +56,21 @@ function np_stuck(npc_st: mut NpcState, p: NpcPerson) -> void { 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, on: bool) -> void { p.crowd = on } +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 +}