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>
87 lines
2.9 KiB
Text
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() }
|
|
}
|
|
}
|