diff --git a/packages/ludic.nav/index.ludic b/packages/ludic.nav/index.ludic index 8ad555c6..d4b4936a 100644 --- a/packages/ludic.nav/index.ludic +++ b/packages/ludic.nav/index.ludic @@ -10,3 +10,4 @@ import "build.ludic" import "query.ludic" import "crowd.ludic" import "resident.ludic" +import "resident_count.ludic" diff --git a/packages/ludic.nav/resident.ludic b/packages/ludic.nav/resident.ludic index 43683a9a..1c74ae6a 100644 --- a/packages/ludic.nav/resident.ludic +++ b/packages/ludic.nav/resident.ludic @@ -51,6 +51,23 @@ export function nav_tiles_keep(nav_st: NavState, kind: int, xs: []float, zs: []f } return changed } +# the same ring by a distance the game answers: near(x, z) is how far (x, z) is from what must be +# loaded (0 inside it). A tile comes in when its centre is within pad of that, and goes only past +# pad + hyst, so a player at an edge does not read one in and out; how many changed +export function nav_tiles_keep_near(nav_st: NavState, kind: int, near: fn(float, float) -> float, pad: float, hyst: float) -> int { + let h = nv_mesh(nav_st, kind) + let f = nav_st.nv_files[kind] + if h == null or f == null { return 0 } + var changed = 0 + for i in 0 .. nvc_index_count(h) { + nvc_index_tile(h, i, nav_st.nv_tile) + let m = near(nav_st.nv_tile[0], nav_st.nv_tile[1]) - pad + let is_in = nav_st.nv_tile[2] > 0.5 + if is_in and m > hyst { changed += nvc_index_out(h, i) } + if not is_in and m < 0.0 { changed += nv_tile_read(h, f, i) } + } + return changed +} function nv_tile_read(h: pointer, f: pointer, i: int) -> int { let size = nvc_index_size(h, i) let buf = nvc_stage(size) @@ -62,24 +79,3 @@ function nv_tile_read(h: pointer, f: pointer, i: int) -> int { } return nvc_index_in(h, i, buf, size) } - -# how many of kind's tiles are in, and (nav_tiles_bytes) how many bytes those hold -export function nav_tiles_in(nav_st: NavState, kind: int) -> int { return nv_tiles_sum(nav_st, kind, false) } -export function nav_tiles_bytes(nav_st: NavState, kind: int) -> int { return nv_tiles_sum(nav_st, kind, true) } -function nv_tiles_sum(nav_st: NavState, kind: int, bytes: bool) -> int { - let h = nv_mesh(nav_st, kind) - if h == null { return 0 } - var n = 0 - for i in 0 .. nvc_index_count(h) { - nvc_index_tile(h, i, nav_st.nv_tile) - if nav_st.nv_tile[2] > 0.5 and bytes { n += int(nav_st.nv_tile[3]) } - if nav_st.nv_tile[2] > 0.5 and not bytes { n += 1 } - } - return n -} -# how many tiles kind's file holds in all -export function nav_tiles_all(nav_st: NavState, kind: int) -> int { - let h = nv_mesh(nav_st, kind) - if h == null { return 0 } - return nvc_index_count(h) -} diff --git a/packages/ludic.nav/resident_count.ludic b/packages/ludic.nav/resident_count.ludic new file mode 100644 index 00000000..a38ed00a --- /dev/null +++ b/packages/ludic.nav/resident_count.ludic @@ -0,0 +1,21 @@ +# resident_count.ludic - what a file-backed mesh holds now: its tiles in, their bytes, and how many it has +# how many of kind's tiles are in, and (nav_tiles_bytes) how many bytes those hold +export function nav_tiles_in(nav_st: NavState, kind: int) -> int { return nv_tiles_sum(nav_st, kind, false) } +export function nav_tiles_bytes(nav_st: NavState, kind: int) -> int { return nv_tiles_sum(nav_st, kind, true) } +function nv_tiles_sum(nav_st: NavState, kind: int, bytes: bool) -> int { + let h = nv_mesh(nav_st, kind) + if h == null { return 0 } + var n = 0 + for i in 0 .. nvc_index_count(h) { + nvc_index_tile(h, i, nav_st.nv_tile) + if nav_st.nv_tile[2] > 0.5 and bytes { n += int(nav_st.nv_tile[3]) } + if nav_st.nv_tile[2] > 0.5 and not bytes { n += 1 } + } + return n +} +# how many tiles kind's file holds in all +export function nav_tiles_all(nav_st: NavState, kind: int) -> int { + let h = nv_mesh(nav_st, kind) + if h == null { return 0 } + return nvc_index_count(h) +} diff --git a/packages/ludic.nav/tests/keep_near_test.ludic b/packages/ludic.nav/tests/keep_near_test.ludic new file mode 100644 index 00000000..2ab699f6 --- /dev/null +++ b/packages/ludic.nav/tests/keep_near_test.ludic @@ -0,0 +1,47 @@ +# keep_near_test.ludic - a file-backed mesh's tiles kept by a distance the game answers (its players' +# zones): a zone round one corner brings in its tile only, growing it brings in its neighbours, an +# edge nudged back and forth changes nothing, and a zone moved far away lets them all go +import "ludic.nav" +import "ludic.base" +import "fakes/meadow.ludic" +program KeepNearTest { + numbers float + + state NearState { + x: float = 0.0 + z: float = 0.0 + r: float = 0.0 + } + # metres from (x, z) to the zone's edge, 0 inside: what ludic.zones' zones_nearest answers + function near(near_st: NearState, x: float, z: float) -> float { + let d = Math.sqrt((x - near_st.x) * (x - near_st.x) + (z - near_st.z) * (z - near_st.z)) + return Math.max(d - near_st.r, 0.0) + } + function in_zone(nav_st: NavState) -> int { return nav_tiles_keep_near(nav_st, NAV_PERSON, fn near, 46.0, 200.0) } + + test "tiles come in within the zone, an edge nudged back and forth changes nothing, and a far zone lets them go" (nav_st: mut NavState, near_st: mut NearState) { + let g = ground(128, 0) + expect(nav_tiled(nav_st, NAV_PERSON, 0.0, 0.0, 64.0, 16, 4096)) + for tz in 0 .. 2 { for tx in 0 .. 2 { expect(nav_tile_build(nav_st, NAV_PERSON, tx, tz, g, new NavConfig) > 0) } } + let path = Os.temp_dir() + "/ludic_nav_near.navmesh" + expect(nav_save_file(nav_st, NAV_PERSON, path)) + nav_reset(nav_st) + expect(nav_load_index(nav_st, NAV_PERSON, path)) + near_st.x = 10.0 + near_st.z = 10.0 + near_st.r = 5.0 + expect_eq(in_zone(nav_st), 1) + expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 1) + near_st.r = 80.0 + expect_eq(in_zone(nav_st), 3) + expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 4) + for k in 0 .. 20 { + near_st.r = 5.0 + float(k % 2) * 100.0 + expect_eq(in_zone(nav_st), 0) + } + near_st.x = 2000.0 + expect_eq(in_zone(nav_st), 4) + expect_eq(nav_tiles_in(nav_st, NAV_PERSON), 0) + nav_reset(nav_st) + } +}