ludic/packages/ludic.base/system.ludic
Orkuncakilkaya 808c4a6f7a feat(lang): 0.R1 - actions and reducers
action Name { fields } is a typed record; reducer State on Action(s: mut State, a: Action) { ... }
in the module that owns the state takes exactly that state and the action (a second state is
refused); dispatch Action { fields } queues one from anywhere, the queue supplied by the runtime.
The queue is drained at the end of every phase of the frame loop, after every phase of ludic.base's
core_tick_all, and by drain_actions(): in dispatch order, each action's reducers in the order of
their states' names, an action a reducer dispatches queued behind, a queue still growing after 64
rounds stopped with the action named. Examples actions/pack, phases, runaway; rejects for a second
state, a reducer on a non-action and an unknown dispatch; ludic.base's actions_test; LANGUAGE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 19:13:46 +03:00

87 lines
2.9 KiB
Text

# ludic.base/system.ludic - a system is a record of what it does at each verb, and the runner
# calls them: init, reset, save and load in the order added, tick phase by phase. A null
# function is a verb the system does not have. The game's list is the order co-op's dice need.
export property System {
key: string = ""
phase: int = 2
version: int = 1
init: fn() -> void = null
reset: fn() -> void = null
tick: fn(Tick) -> void = null
save: fn() -> Val = null
load: fn(Val, int) -> void = null
}
# the declared list: a mechanic or the game writes `def Systems fishing { phase: PH_ACT, tick: fn
# fishing_tick }` from its own module; the runner starts from these, then core_add appends
export open registry Systems of System as SYS
function cs_all(base_st: mut BaseState) -> []System {
if base_st.cs_list == null {
base_st.cs_list = new []System
for i in 0 .. SYS_COUNT { push(base_st.cs_list, Systems[i]) }
}
return base_st.cs_list
}
export function system_new(key: string, phase: int) -> System {
let s = new System
s.key = key
s.phase = phase
return s
}
# a key is a save section's name, so two systems may not share one
export function core_add(base_st: mut BaseState, s: System) -> void {
let all = cs_all(base_st)
for i in 0 .. len(all) {
if all[i].key == s.key {
print(`ludic.base: two systems are keyed "{s.key}"`)
exit(1)
}
}
push(all, s)
}
export function core_clear(base_st: mut BaseState) -> void { base_st.cs_list = new []System }
export function core_count(base_st: mut BaseState) -> int { return len(cs_all(base_st)) }
export function core_init_all(base_st: mut BaseState) -> void {
let all = cs_all(base_st)
for i in 0 .. len(all) { if all[i].init != null { all[i].init() } }
}
export function core_reset_all(base_st: mut BaseState) -> void {
let all = cs_all(base_st)
for i in 0 .. len(all) { if all[i].reset != null { all[i].reset() } }
}
export function core_tick_all(base_st: mut BaseState, t: Tick) -> void {
let all = cs_all(base_st)
for ph in 0 .. PH_COUNT {
for i in 0 .. len(all) {
if all[i].phase == ph and all[i].tick != null { all[i].tick(t) }
}
drain_actions() # 0.R: the phase's actions, reduced before the next
}
}
# every system that saves, under its key with its version
export function core_save_all(base_st: mut BaseState) -> Val {
let root = save_tree()
let all = cs_all(base_st)
for i in 0 .. len(all) {
if all[i].save != null { save_section(root, all[i].key, all[i].version, all[i].save()) }
}
return root
}
# each system its own section; one with no section (or no load) is reset instead
export function core_load_all(base_st: mut BaseState, root: Val) -> void {
let all = cs_all(base_st)
for i in 0 .. len(all) {
let n = load_section(root, all[i].key)
if n.found and all[i].load != null { all[i].load(n.data, n.version) } else if all[i].reset != null { all[i].reset() }
}
}