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

@ -1,54 +1,54 @@
# ludic.settings/file.ludic - the store as a JSON object, by key: a switch is 0 or 1, a whole number
# is itself, a float is thousandths (sv_put_float), text is a string. A key the file lacks keeps
# what the store holds, and a key the store does not know is ignored.
export function settings_to_json(settings_st: mut SettingsState) -> Val {
sg_init(settings_st)
export function settings_to_json() -> Val {
sg_init()
let v = Value.object()
for i in 0 .. len(settings_st.sg_defs) { settings_put(settings_st, v, i) }
for i in 0 .. len(sg_defs) { settings_put(v, i) }
return v
}
# one setting into `v` under its key (a game building its own file adds its extras beside them)
export function settings_put(settings_st: SettingsState, v: Val, id: int) -> void {
let d = settings_st.sg_defs[id]
export function settings_put(v: Val, id: int) -> void {
let d = sg_defs[id]
if not d.saved { return }
if d.kind == SETTING_TEXT {
sv_put_str(v, d.key, settings_st.sg_text[id])
sv_put_str(v, d.key, sg_text[id])
} else if d.kind == SETTING_FLOAT {
sv_put_float(v, d.key, settings_st.sg_num[id])
sv_put_float(v, d.key, sg_num[id])
} else {
sv_put_int(v, d.key, int(settings_st.sg_num[id]))
sv_put_int(v, d.key, int(sg_num[id]))
}
}
# every key `v` names, held to its range; quiet, since a load is applied whole by its caller
export function settings_from_json(settings_st: mut SettingsState, v: Val) -> void {
sg_init(settings_st)
export function settings_from_json(v: Val) -> void {
sg_init()
if v == null { return }
for i in 0 .. len(settings_st.sg_defs) { sg_read(settings_st, v, i) }
for i in 0 .. len(sg_defs) { sg_read(v, i) }
}
function sg_read(settings_st: mut SettingsState, v: Val, id: int) -> void {
let d = settings_st.sg_defs[id]
function sg_read(v: Val, id: int) -> void {
let d = sg_defs[id]
if not d.saved or Value.kind(v) != 6 or Value.has(v, d.key) == 0 { return }
if d.kind == SETTING_TEXT {
settings_st.sg_text[id] = sv_str(v, d.key, settings_st.sg_text[id])
sg_text[id] = sv_str(v, d.key, sg_text[id])
} else if d.kind == SETTING_FLOAT {
settings_st.sg_num[id] = settings_held(settings_st, id, sv_float(v, d.key, settings_st.sg_num[id]))
sg_num[id] = settings_held(id, sv_float(v, d.key, sg_num[id]))
} else {
settings_st.sg_num[id] = settings_held(settings_st, id, float(sv_int(v, d.key, int(settings_st.sg_num[id]))))
sg_num[id] = settings_held(id, float(sv_int(v, d.key, int(sg_num[id]))))
}
}
# one setting from a JSON value in the file's own form (a row in a settings screen hands these):
# a change is a fact
export function settings_set_value(base_st: mut BaseState, settings_st: mut SettingsState, id: int, x: Val) -> void {
let d = settings_st.sg_defs[id]
export function settings_set_value(id: int, x: Val) -> void {
let d = sg_defs[id]
if d.kind == SETTING_TEXT {
if Value.kind(x) == 4 { settings_set_text(base_st, settings_st, id, Value.as_str(x)) }
if Value.kind(x) == 4 { settings_set_text(id, Value.as_str(x)) }
} else if d.kind == SETTING_FLOAT {
settings_set_float(base_st, settings_st, id, float(Value.as_int(x)) / 1000.0)
settings_set_float(id, float(Value.as_int(x)) / 1000.0)
} else {
settings_set_int(base_st, settings_st, id, Value.as_int(x))
settings_set_int(id, Value.as_int(x))
}
}

View file

@ -1,35 +1,35 @@
# ludic.settings/rules.ludic - the whole store at once: every setting to its default, every one
# held to its range, and the rescue set. None of these is news: whoever calls them applies it all
export function settings_reset_all(settings_st: mut SettingsState) -> void {
sg_init(settings_st)
for i in 0 .. len(settings_st.sg_defs) {
settings_st.sg_num[i] = settings_st.sg_defs[i].num
settings_st.sg_text[i] = settings_st.sg_defs[i].text
export function settings_reset_all() -> void {
sg_init()
for i in 0 .. len(sg_defs) {
sg_num[i] = sg_defs[i].num
sg_text[i] = sg_defs[i].text
}
}
# a hand-edited file, or one from a build with more choices, never hands the game a value it has
# no case for
export function settings_clamp_all(settings_st: mut SettingsState) -> void {
sg_init(settings_st)
for i in 0 .. len(settings_st.sg_defs) { settings_st.sg_num[i] = settings_held(settings_st, i, settings_st.sg_num[i]) }
export function settings_clamp_all() -> void {
sg_init()
for i in 0 .. len(sg_defs) { sg_num[i] = settings_held(i, sg_num[i]) }
}
# the values every machine can run: each setting marked `rescue` back to its default, and the
# rest (keys, language, volume) left the player's
export function settings_rescue(settings_st: mut SettingsState) -> void {
sg_init(settings_st)
for i in 0 .. len(settings_st.sg_defs) {
if settings_st.sg_defs[i].rescue {
settings_st.sg_num[i] = settings_st.sg_defs[i].num
settings_st.sg_text[i] = settings_st.sg_defs[i].text
export function settings_rescue() -> void {
sg_init()
for i in 0 .. len(sg_defs) {
if sg_defs[i].rescue {
sg_num[i] = sg_defs[i].num
sg_text[i] = sg_defs[i].text
}
}
}
# whether a setting is on the rescue list at its safe value
export function settings_is_safe(settings_st: SettingsState, id: int) -> bool {
let d = settings_st.sg_defs[id]
export function settings_is_safe(id: int) -> bool {
let d = sg_defs[id]
if not d.rescue { return true }
return settings_st.sg_num[id] == d.num and settings_st.sg_text[id] == d.text
return sg_num[id] == d.num and sg_text[id] == d.text
}

View file

@ -1,90 +1,88 @@
# ludic.settings/store.ludic - the values, by id (a setting's place in the order it was added),
# and the verbs: nothing else holds a setting, and every set that changes one is a fact
export state SettingsState {
sg_defs: []SettingDef = null
sg_num: []float = null
sg_text: []string = null
sg_changes: Queue<SettingChanged> = null
var sg_defs: []SettingDef = null
var sg_num: []float = null
var sg_text: []string = null
var sg_changes: Queue<SettingChanged> = null
function sg_init() -> void {
if sg_defs != null { return }
sg_defs = new []SettingDef
sg_num = new []float
sg_text = new []string
}
function sg_init(settings_st: mut SettingsState) -> void {
if settings_st.sg_defs != null { return }
settings_st.sg_defs = new []SettingDef
settings_st.sg_num = new []float
settings_st.sg_text = new []string
}
export function settings_changes(base_st: mut BaseState, settings_st: mut SettingsState) -> Queue<SettingChanged> {
if settings_st.sg_changes == null { settings_st.sg_changes = queue_new(base_st, "settings.changes") }
return settings_st.sg_changes
export function settings_changes() -> Queue<SettingChanged> {
if sg_changes == null { sg_changes = queue_new("settings.changes") }
return sg_changes
}
# forget every setting (a test, or a game that declares its table again)
export function settings_clear(base_st: mut BaseState, settings_st: mut SettingsState) -> void {
settings_st.sg_defs = new []SettingDef
settings_st.sg_num = new []float
settings_st.sg_text = new []string
q_clear(base_st, settings_changes(base_st, settings_st))
export function settings_clear() -> void {
sg_defs = new []SettingDef
sg_num = new []float
sg_text = new []string
q_clear(settings_changes())
}
# a setting, at its default; its id is its place in the order added
export function settings_add(settings_st: mut SettingsState, d: SettingDef) -> int {
sg_init(settings_st)
push(settings_st.sg_defs, d)
push(settings_st.sg_num, d.num)
push(settings_st.sg_text, d.text)
return len(settings_st.sg_defs) - 1
export function settings_add(d: SettingDef) -> int {
sg_init()
push(sg_defs, d)
push(sg_num, d.num)
push(sg_text, d.text)
return len(sg_defs) - 1
}
export function settings_count(settings_st: mut SettingsState) -> int {
sg_init(settings_st)
return len(settings_st.sg_defs)
export function settings_count() -> int {
sg_init()
return len(sg_defs)
}
export function settings_def(settings_st: SettingsState, id: int) -> SettingDef { return settings_st.sg_defs[id] }
export function settings_def(id: int) -> SettingDef { return sg_defs[id] }
# the id of `key`, or -1
export function settings_id_of(settings_st: mut SettingsState, key: string) -> int {
sg_init(settings_st)
for i in 0 .. len(settings_st.sg_defs) { if settings_st.sg_defs[i].key == key { return i } }
export function settings_id_of(key: string) -> int {
sg_init()
for i in 0 .. len(sg_defs) { if sg_defs[i].key == key { return i } }
return -1
}
export function settings_get_float(settings_st: SettingsState, id: int) -> float { return settings_st.sg_num[id] }
export function settings_get_int(settings_st: SettingsState, id: int) -> int { return int(settings_st.sg_num[id]) }
export function settings_get_bool(settings_st: SettingsState, id: int) -> bool { return settings_st.sg_num[id] != 0.0 }
export function settings_get_text(settings_st: SettingsState, id: int) -> string { return settings_st.sg_text[id] }
export function settings_get_float(id: int) -> float { return sg_num[id] }
export function settings_get_int(id: int) -> int { return int(sg_num[id]) }
export function settings_get_bool(id: int) -> bool { return sg_num[id] != 0.0 }
export function settings_get_text(id: int) -> string { return sg_text[id] }
function sg_changed(base_st: mut BaseState, settings_st: mut SettingsState, id: int) -> void {
function sg_changed(id: int) -> void {
let c = new SettingChanged
c.id = id
c.key = settings_st.sg_defs[id].key
q_push(base_st, settings_changes(base_st, settings_st), c)
c.key = sg_defs[id].key
q_push(settings_changes(), c)
}
# a value outside the setting's range is its default: a damaged file never reaches the game
export function settings_set_float(base_st: mut BaseState, settings_st: mut SettingsState, id: int, v: float) -> void {
let h = settings_held(settings_st, id, v)
if h == settings_st.sg_num[id] { return }
settings_st.sg_num[id] = h
sg_changed(base_st, settings_st, id)
export function settings_set_float(id: int, v: float) -> void {
let h = settings_held(id, v)
if h == sg_num[id] { return }
sg_num[id] = h
sg_changed(id)
}
export function settings_set_int(base_st: mut BaseState, settings_st: mut SettingsState, id: int, v: int) -> void { settings_set_float(base_st, settings_st, id, float(v)) }
export function settings_set_int(id: int, v: int) -> void { settings_set_float(id, float(v)) }
export function settings_set_bool(base_st: mut BaseState, settings_st: mut SettingsState, id: int, v: bool) -> void {
if v { settings_set_float(base_st, settings_st, id, 1.0) } else { settings_set_float(base_st, settings_st, id, 0.0) }
export function settings_set_bool(id: int, v: bool) -> void {
if v { settings_set_float(id, 1.0) } else { settings_set_float(id, 0.0) }
}
export function settings_set_text(base_st: mut BaseState, settings_st: mut SettingsState, id: int, v: string) -> void {
if v == settings_st.sg_text[id] { return }
settings_st.sg_text[id] = v
sg_changed(base_st, settings_st, id)
export function settings_set_text(id: int, v: string) -> void {
if v == sg_text[id] { return }
sg_text[id] = v
sg_changed(id)
}
# `v` held to the setting's range: outside it, the default
export function settings_held(settings_st: SettingsState, id: int, v: float) -> float {
let d = settings_st.sg_defs[id]
export function settings_held(id: int, v: float) -> float {
let d = sg_defs[id]
var x = v
if d.kind == SETTING_BOOL {
if x != 0.0 { x = 1.0 }

View file

@ -6,70 +6,70 @@ program SettingsTest {
numbers float
registry Toy of SettingDef as TOY from "packages/ludic.settings/tests/toy.lres"
function declare(base_st: mut BaseState, settings_st: mut SettingsState) -> void {
settings_clear(base_st, settings_st)
for i in 0 .. TOY_COUNT { settings_add(settings_st, Toy[i]) }
function declare() -> void {
settings_clear()
for i in 0 .. TOY_COUNT { settings_add(Toy[i]) }
}
function facts(base_st: mut BaseState, settings_st: mut SettingsState) -> []SettingChanged { return q_drain(base_st, settings_changes(base_st, settings_st)) }
function facts() -> []SettingChanged { return q_drain(settings_changes()) }
test "a registry declares the store, each at its default" (base_st: mut BaseState, settings_st: mut SettingsState) {
declare(base_st, settings_st)
expect_eq(settings_count(settings_st), 5)
expect(settings_get_bool(settings_st, TOY_BLOOM))
expect_eq(settings_get_int(settings_st, TOY_SHADOW_Q), 2)
expect(settings_get_float(settings_st, TOY_RSCALE) > 0.77)
expect(settings_get_text(settings_st, TOY_LANG) == "en")
expect_eq(settings_id_of(settings_st, "rscale"), TOY_RSCALE)
expect_eq(settings_id_of(settings_st, "nothing"), -1)
test "a registry declares the store, each at its default" {
declare()
expect_eq(settings_count(), 5)
expect(settings_get_bool(TOY_BLOOM))
expect_eq(settings_get_int(TOY_SHADOW_Q), 2)
expect(settings_get_float(TOY_RSCALE) > 0.77)
expect(settings_get_text(TOY_LANG) == "en")
expect_eq(settings_id_of("rscale"), TOY_RSCALE)
expect_eq(settings_id_of("nothing"), -1)
}
test "a change is a fact, and setting what is there is none" (base_st: mut BaseState, settings_st: mut SettingsState) {
declare(base_st, settings_st)
settings_set_int(base_st, settings_st, TOY_SHADOW_Q, 3)
settings_set_int(base_st, settings_st, TOY_SHADOW_Q, 3)
settings_set_text(base_st, settings_st, TOY_LANG, "tr")
let fs = facts(base_st, settings_st)
test "a change is a fact, and setting what is there is none" {
declare()
settings_set_int(TOY_SHADOW_Q, 3)
settings_set_int(TOY_SHADOW_Q, 3)
settings_set_text(TOY_LANG, "tr")
let fs = facts()
expect_eq(len(fs), 2)
expect(fs[0].key == "shadow_q")
expect_eq(fs[1].id, TOY_LANG)
}
test "a value out of range is the default" (base_st: mut BaseState, settings_st: mut SettingsState) {
declare(base_st, settings_st)
settings_set_int(base_st, settings_st, TOY_SHADOW_Q, 9)
expect_eq(settings_get_int(settings_st, TOY_SHADOW_Q), 2)
settings_set_float(base_st, settings_st, TOY_RSCALE, 0.2)
expect(settings_get_float(settings_st, TOY_RSCALE) > 0.77)
test "a value out of range is the default" {
declare()
settings_set_int(TOY_SHADOW_Q, 9)
expect_eq(settings_get_int(TOY_SHADOW_Q), 2)
settings_set_float(TOY_RSCALE, 0.2)
expect(settings_get_float(TOY_RSCALE) > 0.77)
}
test "a rescue puts back only what it lists" (base_st: mut BaseState, settings_st: mut SettingsState) {
declare(base_st, settings_st)
settings_set_int(base_st, settings_st, TOY_SHADOW_Q, 0)
settings_set_bool(base_st, settings_st, TOY_BLOOM, false)
expect(not settings_is_safe(settings_st, TOY_SHADOW_Q))
settings_rescue(settings_st)
expect_eq(settings_get_int(settings_st, TOY_SHADOW_Q), 2)
expect(not settings_get_bool(settings_st, TOY_BLOOM))
test "a rescue puts back only what it lists" {
declare()
settings_set_int(TOY_SHADOW_Q, 0)
settings_set_bool(TOY_BLOOM, false)
expect(not settings_is_safe(TOY_SHADOW_Q))
settings_rescue()
expect_eq(settings_get_int(TOY_SHADOW_Q), 2)
expect(not settings_get_bool(TOY_BLOOM))
}
test "the file is by key, and what it lacks or cannot hold keeps the store's" (base_st: mut BaseState, settings_st: mut SettingsState) {
declare(base_st, settings_st)
settings_set_float(base_st, settings_st, TOY_RSCALE, 0.9)
settings_set_text(base_st, settings_st, TOY_LANG, "tr")
settings_set_bool(base_st, settings_st, TOY_HELD, true)
let text = Json.encode(settings_to_json(settings_st))
declare(base_st, settings_st)
settings_set_bool(base_st, settings_st, TOY_BLOOM, false)
test "the file is by key, and what it lacks or cannot hold keeps the store's" {
declare()
settings_set_float(TOY_RSCALE, 0.9)
settings_set_text(TOY_LANG, "tr")
settings_set_bool(TOY_HELD, true)
let text = Json.encode(settings_to_json())
declare()
settings_set_bool(TOY_BLOOM, false)
let v = Json.parse(text)
Value.put(v, "shadow_q", Value.int(7))
Value.put(v, "stranger", Value.int(1))
settings_from_json(settings_st, v)
expect(settings_get_float(settings_st, TOY_RSCALE) > 0.89)
expect(settings_get_text(settings_st, TOY_LANG) == "tr")
expect_eq(settings_get_int(settings_st, TOY_SHADOW_Q), 2)
expect(settings_get_bool(settings_st, TOY_BLOOM))
expect(not settings_get_bool(settings_st, TOY_HELD))
expect(Value.has(settings_to_json(settings_st), "held") == 0)
settings_from_json(v)
expect(settings_get_float(TOY_RSCALE) > 0.89)
expect(settings_get_text(TOY_LANG) == "tr")
expect_eq(settings_get_int(TOY_SHADOW_Q), 2)
expect(settings_get_bool(TOY_BLOOM))
expect(not settings_get_bool(TOY_HELD))
expect(Value.has(settings_to_json(), "held") == 0)
}
}