wip(0.S2): migrate - a var nothing writes becomes a let; a state is keyed by its module, directory or program, so every program's plan agrees; paths normalized; program states named SceneDemoState / scene_demo_st; the LSP reads state, mut and entry/handler parameters

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 15:31:34 +03:00
parent 3eda72e8c2
commit 5ffe50ed02
463 changed files with 72045 additions and 67421 deletions

View file

@ -21,10 +21,10 @@ export function thing_prompt(t: Thing) -> string {
}
# the kind does what using it does; the use is a fact whatever the kind made of it
export function thing_use(base_st: mut BaseState, things_st: mut ThingsState, t: Thing) -> void {
export function thing_use(t: Thing) -> void {
if t == null or not thing_kind_ok(t.kind) { return }
let k = ThingKinds[t.kind]
th_fact(base_st, things_st, THING_USED, t)
th_fact(THING_USED, t)
if k.use != null { k.use(t) }
}
@ -46,8 +46,8 @@ export function thing_is_personal(t: Thing) -> bool { return thing_kind_ok(t.kin
export function thing_is_put_down(t: Thing) -> bool { return ThingsWorld.put_down(t) }
# every active Thing whose kind ticks, `hours` of the world's clock on
export function things_tick(things_st: mut ThingsState, hours: float) -> void {
let all = things_all(things_st)
export function things_tick(hours: float) -> void {
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if not t.active or not thing_kind_ok(t.kind) { continue }
@ -58,8 +58,8 @@ 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: mut ThingsState) -> void {
let all = things_all(things_st)
export function things_restock() -> void {
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if thing_is_put_down(t) { continue }

View file

@ -3,8 +3,8 @@
function th_of(t: Thing, kind: int) -> bool { return kind < 0 or t.kind == kind }
# the first active one of a kind (and of that id, unless id < 0), in the order placed
export function thing_find(things_st: mut ThingsState, kind: int, id: int) -> Thing {
let all = things_all(things_st)
export function thing_find(kind: int, id: int) -> Thing {
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if t.active and th_of(t, kind) and (id < 0 or t.id == id) { return t }
@ -12,17 +12,17 @@ export function thing_find(things_st: mut ThingsState, kind: int, id: int) -> Th
return null
}
export function thing_by_uid(things_st: mut ThingsState, uid: int) -> Thing {
let all = things_all(things_st)
export function thing_by_uid(uid: int) -> Thing {
let all = things_all()
for i in 0 .. len(all) { if all[i].uid == uid { return all[i] } }
return null
}
# the nearest active one of a kind to (x, z), or null
export function thing_nearest(things_st: mut ThingsState, kind: int, x: float, z: float) -> Thing {
export function thing_nearest(kind: int, x: float, z: float) -> Thing {
var best: Thing = null
var bd = 0.0
let all = things_all(things_st)
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if not t.active or not th_of(t, kind) { continue }
@ -43,9 +43,9 @@ export function thing_dist2(t: Thing, x: float, z: float) -> float {
}
# every active one within r of (x, z), nearest first
export function things_within(things_st: mut ThingsState, x: float, z: float, r: float) -> []Thing {
export function things_within(x: float, z: float, r: float) -> []Thing {
let out = new []Thing
let all = things_all(things_st)
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if t.active and thing_dist2(t, x, z) <= r * r { th_insert(out, t, x, z) }
@ -64,17 +64,17 @@ function th_insert(out: []Thing, t: Thing, x: float, z: float) -> void {
}
# every active one of a kind, in the order placed
export function things_each(things_st: mut ThingsState, kind: int) -> []Thing {
export function things_each(kind: int) -> []Thing {
let out = new []Thing
let all = things_all(things_st)
let all = things_all()
for i in 0 .. len(all) { if all[i].active and th_of(all[i], kind) { push(out, all[i]) } }
return out
}
# every one of a kind, active or not
export function things_each_all(things_st: mut ThingsState, kind: int) -> []Thing {
export function things_each_all(kind: int) -> []Thing {
let out = new []Thing
let all = things_all(things_st)
let all = things_all()
for i in 0 .. len(all) { if th_of(all[i], kind) { push(out, all[i]) } }
return out
}

View file

@ -1,18 +1,15 @@
# store.ludic - the Things, and the only verbs that make, remove, move, show and hide one; each
# placing and removing is a fact, and the port is told at once so the game can draw it
export state ThingsState {
th_list: []Thing = null
th_uid: int = 0
th_facts: Queue<ThingFact> = null
th_marked: int = -1
var th_list: []Thing = null
var th_uid: int = 0
var th_facts: Queue<ThingFact> = null
export function things_facts() -> Queue<ThingFact> {
if th_facts == null { th_facts = queue_new("things.facts") }
return th_facts
}
export function things_facts(base_st: mut BaseState, things_st: mut ThingsState) -> Queue<ThingFact> {
if things_st.th_facts == null { things_st.th_facts = queue_new(base_st, "things.facts") }
return things_st.th_facts
}
function th_fact(base_st: mut BaseState, things_st: mut ThingsState, what: int, t: Thing) -> void {
function th_fact(what: int, t: Thing) -> void {
let f = new ThingFact
f.what = what
f.uid = t.uid
@ -21,28 +18,28 @@ function th_fact(base_st: mut BaseState, things_st: mut ThingsState, what: int,
f.owner = t.owner
f.x = t.x
f.z = t.z
q_push(base_st, things_facts(base_st, things_st), f)
q_push(things_facts(), f)
}
# whether a world has been set out (things_clear) since the program began
export function things_ready(things_st: ThingsState) -> bool { return things_st.th_list != null }
export function things_ready() -> bool { return th_list != null }
# every Thing, in the order placed; the list is the package's, never to be pushed to
export function things_all(things_st: mut ThingsState) -> []Thing {
if things_st.th_list == null { things_st.th_list = new []Thing }
return things_st.th_list
export function things_all() -> []Thing {
if th_list == null { th_list = new []Thing }
return th_list
}
export function things_count(things_st: mut ThingsState) -> int { return len(things_all(things_st)) }
export function things_count() -> int { return len(things_all()) }
# a new world: every Thing forgotten, quietly (the game has already dropped its drawings)
export function things_clear(things_st: mut ThingsState) -> void { things_st.th_list = new []Thing }
export function things_clear() -> void { th_list = new []Thing }
export function thing_spawn(base_st: mut BaseState, things_st: mut ThingsState, kind: int, look: string, x: float, y: float, z: float, yaw: float, id: int) -> Thing {
return th_place(base_st, things_st, new Thing, kind, look, x, y, z, yaw, id)
export function thing_spawn(kind: int, look: string, x: float, y: float, z: float, yaw: float, id: int) -> Thing {
return th_place(new Thing, kind, look, x, y, z, yaw, id)
}
function th_place(base_st: mut BaseState, things_st: mut ThingsState, t: Thing, kind: int, look: string, x: float, y: float, z: float, yaw: float, id: int) -> Thing {
function th_place(t: Thing, kind: int, look: string, x: float, y: float, z: float, yaw: float, id: int) -> Thing {
t.kind = kind
t.look = look
t.x = x
@ -50,45 +47,46 @@ function th_place(base_st: mut BaseState, things_st: mut ThingsState, t: Thing,
t.z = z
t.yaw = yaw
t.id = id
things_st.th_uid += 1
t.uid = things_st.th_uid
push(things_all(things_st), t)
th_uid += 1
t.uid = th_uid
push(things_all(), t)
ThingsWorld.placed(t)
th_fact(base_st, things_st, THING_PLACED, t)
th_fact(THING_PLACED, t)
return t
}
# a Thing on the ground (the port's), set into it by `sink`, used from `reach`
export function thing_put(base_st: mut BaseState, things_st: mut ThingsState, kind: int, look: string, x: float, z: float, yaw: float, sink: float, reach: float, id: int) -> Thing {
export function thing_put(kind: int, look: string, x: float, z: float, yaw: float, sink: float, reach: float, id: int) -> Thing {
let t = new Thing
t.reach = reach
return th_place(base_st, things_st, t, kind, look, x, ThingsWorld.ground(x, z) - sink, z, yaw, id)
return th_place(t, kind, look, x, ThingsWorld.ground(x, z) - sink, z, yaw, id)
}
# gone for good
export function thing_remove(base_st: mut BaseState, things_st: mut ThingsState, t: Thing) -> void {
export function thing_remove(t: Thing) -> void {
if t == null { return }
things_st.th_marked = t.uid
things_drop(base_st, things_st, fn th_is_marked)
things_st.th_marked = -1
th_marked = t.uid
things_drop(fn th_is_marked)
th_marked = -1
}
function th_is_marked(things_st: ThingsState, t: Thing) -> bool { return t.uid == things_st.th_marked }
var th_marked: int = -1
function th_is_marked(t: Thing) -> bool { return t.uid == th_marked }
# every Thing `gone` says so is removed for good
export function things_drop(base_st: mut BaseState, things_st: mut ThingsState, gone: fn(Thing) -> bool) -> void {
export function things_drop(gone: fn(Thing) -> bool) -> void {
let keep = new []Thing
let all = things_all(things_st)
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if gone(t) {
ThingsWorld.removed(t)
th_fact(base_st, things_st, THING_REMOVED, t)
th_fact(THING_REMOVED, t)
} else {
push(keep, t)
}
}
things_st.th_list = keep
th_list = keep
}
export function thing_move(t: Thing, x: float, z: float, yaw: float) -> void {

View file

@ -1,8 +1,8 @@
# system.ludic - the things as a system: its save section carries every active Thing the port
# keeps, by its kind's KEY (so the game may reorder its kinds), and a load puts each back
export function things_save(things_st: mut ThingsState) -> Val {
export function things_save() -> Val {
let l = Value.list()
let all = things_all(things_st)
let all = things_all()
for i in 0 .. len(all) {
let t = all[i]
if t.active and thing_kind_ok(t.kind) and ThingsWorld.keeps(t) { Value.add(l, th_to_json(t)) }
@ -33,18 +33,18 @@ function th_to_json(t: Thing) -> Val {
}
# every kept Thing removed for good: what a load or a new trip is about to put back
export function things_reset(base_st: mut BaseState, things_st: mut ThingsState) -> void { things_drop(base_st, things_st, fn th_kept) }
export function things_reset() -> void { things_drop(fn th_kept) }
function th_kept(t: Thing) -> bool { return ThingsWorld.keeps(t) }
# a kind this build does not know is dropped
export function things_load(base_st: mut BaseState, things_st: mut ThingsState, v: Val, version: int) -> void {
things_reset(base_st, things_st)
export function things_load(v: Val, version: int) -> void {
things_reset()
if v == null or Value.kind(v) != 6 or Value.has(v, "placed") == 0 { return }
let l = Value.get(v, "placed")
for i in 0 .. Value.count(l) { th_from_json(base_st, things_st, Value.at(l, i)) }
for i in 0 .. Value.count(l) { th_from_json(Value.at(l, i)) }
}
function th_from_json(base_st: mut BaseState, things_st: mut ThingsState, o: Val) -> void {
function th_from_json(o: Val) -> void {
let k = thing_kind_by_key(sv_str(o, "kind", ""))
if k < 0 { return }
let t = new Thing
@ -59,7 +59,7 @@ function th_from_json(base_st: mut BaseState, things_st: mut ThingsState, o: Val
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))
th_place(base_st, things_st, t, k, sv_str(o, "look", ""), x, y, z, sv_float(o, "yaw", 0.0), sv_int(o, "id", 0))
th_place(t, k, sv_str(o, "look", ""), x, y, z, sv_float(o, "yaw", 0.0), sv_int(o, "id", 0))
ThingsWorld.restored(t)
}

View file

@ -4,13 +4,11 @@ import "ludic.things"
import "ludic.base"
program ThingsTest {
numbers float
state ThingstestState {
drawn: int = 0
gone: int = 0
shows: int = 0
back: int = 0
wood: int = 0
}
var drawn: int = 0
var gone: int = 0
var shows: int = 0
var back: int = 0
var wood: int = 0
function lit_prompt(t: Thing) -> string {
if t.used != 0 { return "Add wood" }
@ -21,8 +19,8 @@ program ThingsTest {
if t.owner == 0 { return "Your tent" }
return ""
}
function branch_use(thingstest_st: mut ThingstestState, t: Thing) -> void {
thingstest_st.wood += 1
function branch_use(t: Thing) -> void {
wood += 1
thing_hide(t)
}
function lure_tick(t: Thing, hours: float) -> void {
@ -38,121 +36,121 @@ program ThingsTest {
def ThingKinds sign { name: "A sign", reach_of: fn far_reach }
function hill(x: float, z: float) -> float { return x * 0.5 }
function on_placed(thingstest_st: mut ThingstestState, t: Thing) -> void { thingstest_st.drawn += 1 }
function on_removed(thingstest_st: mut ThingstestState, t: Thing) -> void { thingstest_st.gone += 1 }
function on_shown(thingstest_st: mut ThingstestState, t: Thing) -> void { thingstest_st.shows += 1 }
function on_restored(thingstest_st: mut ThingstestState, t: Thing) -> void { thingstest_st.back += 1 }
function on_placed(t: Thing) -> void { drawn += 1 }
function on_removed(t: Thing) -> void { gone += 1 }
function on_shown(t: Thing) -> void { shows += 1 }
function on_restored(t: Thing) -> void { back += 1 }
bind ThingsWorld { ground: fn hill, placed: fn on_placed, removed: fn on_removed, shown: fn on_shown, restored: fn on_restored }
function put(base_st: mut BaseState, things_st: mut ThingsState, kind: int, x: float, z: float) -> Thing { return thing_spawn(base_st, things_st, kind, "m", x, 0.0, z, 0.0, 0) }
function facts(base_st: mut BaseState, things_st: mut ThingsState) -> []ThingFact { return q_drain(base_st, things_facts(base_st, things_st)) }
function put(kind: int, x: float, z: float) -> Thing { return thing_spawn(kind, "m", x, 0.0, z, 0.0, 0) }
function facts() -> []ThingFact { return q_drain(things_facts()) }
test "a spawn is drawn, numbered and said" (base_st: mut BaseState, things_st: mut ThingsState, thingstest_st: ThingstestState) {
things_clear(things_st)
let a = put(base_st, things_st, TH_TENT, 1.0, 1.0)
let b = put(base_st, things_st, TH_FIRE, 2.0, 2.0)
expect_eq(thingstest_st.drawn, 2)
test "a spawn is drawn, numbered and said" {
things_clear()
let a = put(TH_TENT, 1.0, 1.0)
let b = put(TH_FIRE, 2.0, 2.0)
expect_eq(drawn, 2)
expect(a.uid != b.uid)
expect_eq(things_count(things_st), 2)
let fs = facts(base_st, things_st)
expect_eq(things_count(), 2)
let fs = facts()
expect_eq(len(fs), 2)
expect_eq(fs[1].what, THING_PLACED)
expect_eq(fs[1].kind, TH_FIRE)
expect(thing_by_uid(things_st, b.uid) == b)
expect(thing_by_uid(b.uid) == b)
}
test "the spatial questions see only what is active" (base_st: mut BaseState, things_st: mut ThingsState, thingstest_st: ThingstestState) {
things_clear(things_st)
let near = put(base_st, things_st, TH_BRANCH, 1.0, 0.0)
let far = put(base_st, things_st, TH_BRANCH, 5.0, 0.0)
put(base_st, things_st, TH_FIRE, 3.0, 0.0)
expect(thing_nearest(things_st, TH_BRANCH, 0.0, 0.0) == near)
expect(thing_nearest(things_st, -1, 3.2, 0.0).kind == TH_FIRE)
expect_eq(len(things_within(things_st, 0.0, 0.0, 3.5)), 2)
expect(things_within(things_st, 6.0, 0.0, 10.0)[0] == far)
test "the spatial questions see only what is active" {
things_clear()
let near = put(TH_BRANCH, 1.0, 0.0)
let far = put(TH_BRANCH, 5.0, 0.0)
put(TH_FIRE, 3.0, 0.0)
expect(thing_nearest(TH_BRANCH, 0.0, 0.0) == near)
expect(thing_nearest(-1, 3.2, 0.0).kind == TH_FIRE)
expect_eq(len(things_within(0.0, 0.0, 3.5)), 2)
expect(things_within(6.0, 0.0, 10.0)[0] == far)
thing_hide(near)
expect(thing_nearest(things_st, TH_BRANCH, 0.0, 0.0) == far)
expect_eq(len(things_each(things_st, TH_BRANCH)), 1)
expect_eq(len(things_each_all(things_st, TH_BRANCH)), 2)
expect(thing_find(things_st, TH_BRANCH, -1) == far)
expect(thing_find(things_st, TH_TENT, -1) == null)
expect_eq(thingstest_st.shows, 1)
expect(thing_nearest(TH_BRANCH, 0.0, 0.0) == far)
expect_eq(len(things_each(TH_BRANCH)), 1)
expect_eq(len(things_each_all(TH_BRANCH)), 2)
expect(thing_find(TH_BRANCH, -1) == far)
expect(thing_find(TH_TENT, -1) == null)
expect_eq(shows, 1)
}
test "remove and move tell the port" (base_st: mut BaseState, things_st: mut ThingsState, thingstest_st: ThingstestState) {
things_clear(things_st)
let a = put(base_st, things_st, TH_TENT, 1.0, 1.0)
put(base_st, things_st, TH_TENT, 2.0, 2.0)
facts(base_st, things_st)
thing_remove(base_st, things_st, a)
expect_eq(thingstest_st.gone, 1)
expect_eq(things_count(things_st), 1)
expect_eq(facts(base_st, things_st)[0].what, THING_REMOVED)
let b = thing_find(things_st, TH_TENT, -1)
test "remove and move tell the port" {
things_clear()
let a = put(TH_TENT, 1.0, 1.0)
put(TH_TENT, 2.0, 2.0)
facts()
thing_remove(a)
expect_eq(gone, 1)
expect_eq(things_count(), 1)
expect_eq(facts()[0].what, THING_REMOVED)
let b = thing_find(TH_TENT, -1)
thing_move(b, 4.0, 0.0, 1.0)
expect_near(b.y, 2.0, 0.001)
let c = thing_put(base_st, things_st, TH_BRANCH, "stick", 2.0, 0.0, 0.0, 0.25, 1.9, 7)
let c = thing_put(TH_BRANCH, "stick", 2.0, 0.0, 0.0, 0.25, 1.9, 7)
expect_near(c.y, 0.75, 0.001)
expect_near(thing_reach(c), 1.9, 0.001)
expect_eq(c.id, 7)
}
test "the kind answers: name, prompt, use, reach" (base_st: mut BaseState, things_st: mut ThingsState) {
things_clear(things_st)
let t = put(base_st, things_st, TH_TENT, 0.0, 0.0)
test "the kind answers: name, prompt, use, reach" {
things_clear()
let t = put(TH_TENT, 0.0, 0.0)
expect(thing_name(t) == "A tent")
t.owner = 0
expect(thing_name(t) == "Your tent")
let f = put(base_st, things_st, TH_FIRE, 0.0, 0.0)
let f = put(TH_FIRE, 0.0, 0.0)
expect(thing_prompt(f) == "Light it")
thing_use(base_st, things_st, f)
thing_use(f)
expect(thing_prompt(f) == "Add wood")
expect(thing_prompt(t) == "")
expect(not thing_can_use(t))
expect_near(thing_reach(f), 2.2, 0.001)
expect_near(thing_reach(put(base_st, things_st, TH_BRANCH, 0.0, 0.0)), 1.5, 0.001)
expect_near(thing_reach(put(base_st, things_st, TH_SIGN, 0.0, 0.0)), 9.0, 0.001)
let fs = facts(base_st, things_st)
expect_near(thing_reach(put(TH_BRANCH, 0.0, 0.0)), 1.5, 0.001)
expect_near(thing_reach(put(TH_SIGN, 0.0, 0.0)), 9.0, 0.001)
let fs = facts()
expect_eq(fs[len(fs) - 3].what, THING_USED)
}
test "ticks and a restock" (base_st: mut BaseState, things_st: mut ThingsState, thingstest_st: ThingstestState) {
things_clear(things_st)
let l = put(base_st, things_st, TH_LURE, 0.0, 0.0)
test "ticks and a restock" {
things_clear()
let l = put(TH_LURE, 0.0, 0.0)
l.timer = 1.0
let b = put(base_st, things_st, TH_BRANCH, 0.0, 0.0)
let f = put(base_st, things_st, TH_FIRE, 0.0, 0.0)
thing_use(base_st, things_st, b)
thing_use(base_st, things_st, f)
expect_eq(thingstest_st.wood, 1)
things_tick(things_st, 0.6)
let b = put(TH_BRANCH, 0.0, 0.0)
let f = put(TH_FIRE, 0.0, 0.0)
thing_use(b)
thing_use(f)
expect_eq(wood, 1)
things_tick(0.6)
expect(l.active)
things_tick(things_st, 0.6)
things_tick(0.6)
expect(not l.active)
things_restock(things_st)
things_restock()
expect(b.active)
expect(not l.active)
expect_eq(f.used, 1)
}
test "the save keeps what the port keeps, by the kind's key" (base_st: mut BaseState, things_st: mut ThingsState, thingstest_st: ThingstestState) {
things_clear(things_st)
let l = put(base_st, things_st, TH_LURE, 3.0, 4.0)
test "the save keeps what the port keeps, by the kind's key" {
things_clear()
let l = put(TH_LURE, 3.0, 4.0)
l.owner = 0
l.timer = 2.5
put(base_st, things_st, TH_TENT, 0.0, 0.0)
put(TH_TENT, 0.0, 0.0)
let root = save_tree()
save_section(root, "things", 1, things_save(things_st))
save_section(root, "things", 1, things_save())
let text = save_encode(root)
things_clear(things_st)
put(base_st, things_st, TH_LURE, 9.0, 9.0)
things_clear()
put(TH_LURE, 9.0, 9.0)
let n = load_section(save_decode(text), "things")
things_load(base_st, things_st, n.data, n.version)
expect_eq(things_count(things_st), 1)
let t = thing_find(things_st, TH_LURE, -1)
things_load(n.data, n.version)
expect_eq(things_count(), 1)
let t = thing_find(TH_LURE, -1)
expect_near(t.x, 3.0, 0.001)
expect_near(t.timer, 2.5, 0.001)
expect_eq(t.owner, 0)
expect_eq(thingstest_st.back, 1)
expect_eq(back, 1)
}
}