From b053bf625c6b43872271dcb0bfee23ccfe2fc3bb Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 21:14:42 +0300 Subject: [PATCH] ludic.things: a Thing's dawn (the last day a restock reached it, saved); things_restock takes the day; things_catch_up restocks a chunk's Things that missed a dawn once a morning would reach them, as that morning would have Co-Authored-By: Claude Opus 5.5 --- packages/ludic.things/README.md | 4 ++- packages/ludic.things/chunks.ludic | 14 +++++++++ packages/ludic.things/dispatch.ludic | 14 +++++---- packages/ludic.things/kinds.ludic | 1 + packages/ludic.things/spare.ludic | 1 + packages/ludic.things/system.ludic | 2 ++ packages/ludic.things/tests/things_test.ludic | 29 +++++++++++++++++-- 7 files changed, 57 insertions(+), 8 deletions(-) diff --git a/packages/ludic.things/README.md b/packages/ludic.things/README.md index 56e723a1..cd9f3d43 100644 --- a/packages/ludic.things/README.md +++ b/packages/ludic.things/README.md @@ -22,6 +22,7 @@ property Thing { timer, used, n # state the kind decides the meaning of sx, sz, syaw # a heading or a point the kind decides the meaning of seat, seats, taken # its seats: the first of the game's seat rows, how many, a bit per one sat in + dawn # the last day a restock reached it (saved), for the catch-up shared # the game copies it to others (a party) } ``` @@ -89,7 +90,8 @@ the actor the moment the Thing exists (a caller often scales or tints it on the | `things_all()`, `things_count()`, `things_ready()`, `thing_by_uid(uid)` | every Thing, in the order placed; whether a world was ever set out | | `thing_find(kind, id)`, `thing_nearest(kind, x, z)`, `things_within(x, z, r)`, `things_each(kind)`, `things_each_all(kind)`, `thing_dist2(t, x, z)` | where they are: only active ones answer except the `_all`; a kind below 0 is any; `within` is nearest first | | `thing_name(t)`, `thing_prompt(t)`, `thing_use(t)`, `thing_can_use(t)`, `thing_reach(t)`, `thing_kind(t)`, `thing_is_personal(t)`, `thing_is_put_down(t)` | the dispatcher | -| `things_tick(hours)`, `things_restock()` | every active kind that ticks; every Thing the world grew back and unused | +| `things_tick(hours)`, `things_restock(day)` | every active kind that ticks; every Thing the world grew back and unused where a morning reaches (`ThingsWorld.restocks`), its `dawn` set to the day | +| `things_catch_up(chunk, day, out)` | a chunk's Things that missed a dawn (`dawn < day`) and a morning now reaches, restocked as that morning would have (no dice), into the caller's list | | `things_facts() -> Queue` | `{ what, uid, kind, id, owner, x, z }`: `THING_PLACED`, `THING_REMOVED`, `THING_USED` | | `thing_kind_ok(k)`, `thing_kind_by_key(key)` | the kinds | | `things_system() -> System` | `"things"`, `PH_COMMIT`: its save section - every active Thing the port keeps, its kind by KEY, so the game may reorder its kinds - and a reset that removes the kept ones for the load to put back | diff --git a/packages/ludic.things/chunks.ludic b/packages/ludic.things/chunks.ludic index 67a45fd2..0baad7f1 100644 --- a/packages/ludic.things/chunks.ludic +++ b/packages/ludic.things/chunks.ludic @@ -16,6 +16,20 @@ export function things_drop_chunk(things_st: mut ThingsState, chunk: int) -> int return n } +# the chunk's Things that missed a dawn and a morning would now reach, restocked as that morning +# would have (no dice: a restock rolls none) and filed into the caller's list; how many +export function things_catch_up(things_st: ThingsState, chunk: int, day: int, out: []Thing) -> int { + List.clear(out) + let rows = ix_rows(things_st.th.chunks, chunk) + for i in 0 .. len(rows) { + let t = things_st.th.tab.rec[rows[i]] + if t.dawn >= day or thing_is_put_down(t) or not ThingsWorld.restocks(t.x, t.z) { continue } + th_restock_one(t, day) + kept_push(out, t) + } + return len(out) +} + # change detection: the tick now, the table moving on to the next - a consumer keeps what this # returned and asks things_changed(since: it) for what was written after export function things_now(things_st: mut ThingsState) -> int { diff --git a/packages/ludic.things/dispatch.ludic b/packages/ludic.things/dispatch.ludic index 7b18648e..b8965882 100644 --- a/packages/ludic.things/dispatch.ludic +++ b/packages/ludic.things/dispatch.ludic @@ -64,14 +64,18 @@ export function things_tick(things_st: mut ThingsState, hours: float) -> void { } } -# a restock: every Thing the world grew is back, and loses its `used` unless its kind keeps it; -# what was put down is left as it is -export function things_restock(things_st: ThingsState) -> void { +# a restock on `day`: every Thing the world grew where a morning reaches is back, and loses its +# `used` unless its kind keeps it; what was put down is left as it is +export function things_restock(things_st: ThingsState, day: int) -> void { let all = things_all(things_st) for i in 0 .. len(all) { let t = all[i] if thing_is_put_down(t) or not ThingsWorld.restocks(t.x, t.z) { continue } - if not ThingKinds[t.kind].keeps { t.used = 0 } - if not t.active { thing_show(t) } + th_restock_one(t, day) } } +function th_restock_one(t: Thing, day: int) -> void { + if not ThingKinds[t.kind].keeps { t.used = 0 } + if not t.active { thing_show(t) } + t.dawn = day +} diff --git a/packages/ludic.things/kinds.ludic b/packages/ludic.things/kinds.ludic index 026dac5e..077c9d65 100644 --- a/packages/ludic.things/kinds.ludic +++ b/packages/ludic.things/kinds.ludic @@ -21,6 +21,7 @@ export property Thing { seat: int = -1 # the first of its model's seat rows in the game's table; -1 none seats: int = 0 # how many seat rows follow it taken: int = 0 # a bit per seat someone is sitting in; never saved + dawn: int = 0 # the last day a restock reached it (a morning, or a catch-up) shared: bool = false # the game copies it to others (a party) ent: int = -1 # its handle in the table, -1 once removed tab: Table = null # the table holding it, so a verb on the Thing alone keeps the indexes diff --git a/packages/ludic.things/spare.ludic b/packages/ludic.things/spare.ludic index 765980aa..195d2b15 100644 --- a/packages/ludic.things/spare.ludic +++ b/packages/ludic.things/spare.ludic @@ -50,6 +50,7 @@ function th_clean(t: Thing) -> Thing { t.seat = -1 t.seats = 0 t.taken = 0 + t.dawn = 0 t.shared = false t.ent = -1 t.tab = null diff --git a/packages/ludic.things/system.ludic b/packages/ludic.things/system.ludic index c8c88d29..c33f2f63 100644 --- a/packages/ludic.things/system.ludic +++ b/packages/ludic.things/system.ludic @@ -31,6 +31,7 @@ function th_to_json(t: Thing) -> Val { sv_put_float(o, "syaw", t.syaw) sv_put_int(o, "seat", t.seat) sv_put_int(o, "seats", t.seats) + sv_put_int(o, "dawn", t.dawn) return o } @@ -61,6 +62,7 @@ function th_from_json(things_st: mut ThingsState, o: Val) -> void { t.syaw = sv_float(o, "syaw", 0.0) t.seat = sv_int(o, "seat", -1) t.seats = sv_int(o, "seats", 0) + t.dawn = sv_int(o, "dawn", 0) let x = sv_float(o, "x", 0.0) let z = sv_float(o, "z", 0.0) let y = sv_float(o, "y", ThingsWorld.ground(x, z)) diff --git a/packages/ludic.things/tests/things_test.ludic b/packages/ludic.things/tests/things_test.ludic index 554acd49..ecf32067 100644 --- a/packages/ludic.things/tests/things_test.ludic +++ b/packages/ludic.things/tests/things_test.ludic @@ -10,6 +10,7 @@ program ThingsTest { shows: int = 0 back: int = 0 wood: int = 0 + edge: float = 1000000.0 # a morning reaches x below this (the game's zones) } function lit_prompt(t: Thing) -> string { @@ -42,7 +43,8 @@ program ThingsTest { function on_removed(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.gone += 1 } function on_shown(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.shows += 1 } function on_restored(things_test_st: mut ThingsTestState, t: Thing) -> void { things_test_st.back += 1 } - bind ThingsWorld { ground: fn hill, placed: fn on_placed, removed: fn on_removed, shown: fn on_shown, restored: fn on_restored } + function in_reach(things_test_st: ThingsTestState, x: float, z: float) -> bool { return x < things_test_st.edge } + bind ThingsWorld { restocks: fn in_reach, ground: fn hill, placed: fn on_placed, removed: fn on_removed, shown: fn on_shown, restored: fn on_restored } function put(things_st: mut ThingsState, kind: int, x: float, z: float) -> Thing { return thing_spawn(things_st, kind, "m", x, 0.0, z, 0.0, 0) } function facts(things_st: ThingsState) -> []ThingFact { return q_drain(things_facts(things_st)) } @@ -129,7 +131,7 @@ program ThingsTest { expect(l.active) things_tick(things_st, 0.6) expect(not l.active) - things_restock(things_st) + things_restock(things_st, 1) expect(b.active) expect(not l.active) expect_eq(f.used, 1) @@ -292,4 +294,27 @@ program ThingsTest { expect_eq(t.seats, 2) expect_eq(t.taken, 0) } + + test "a Thing a morning missed is restocked once a morning would reach it, and only once" (things_st: mut ThingsState, things_test_st: mut ThingsTestState) { + things_clear(things_st) + let near = put(things_st, TH_BRANCH, 0.0, 0.0) + let far = put(things_st, TH_BRANCH, 500.0, 0.0) + thing_use(things_st, near) + thing_use(things_st, far) + things_test_st.edge = 100.0 + things_restock(things_st, 3) + expect(near.active) + expect_eq(near.dawn, 3) + expect(not far.active) + let out = new []Thing + expect_eq(things_catch_up(things_st, things_chunk(500.0, 0.0), 3, out), 0) + expect(not far.active) + things_test_st.edge = 1000000.0 + expect_eq(things_catch_up(things_st, things_chunk(500.0, 0.0), 3, out), 1) + expect(far.active) + expect_eq(far.dawn, 3) + expect(out[0] == far) + expect_eq(things_catch_up(things_st, things_chunk(500.0, 0.0), 3, out), 0) + expect_eq(things_catch_up(things_st, things_chunk(0.0, 0.0), 3, out), 0) + } }