feat(ludic.effects): timed modifiers as a mechanic package; ludic.base's q_new is queue_new

ludic.effects: effects_add / effects_add_after / effects_clear / effects_run,
effects_sum / effects_has / effects_at / effects_live, an EffectEnded queue
(ran out, pushed out by the bonus cap, crowded out of a full ring) and a
System with its own save section. Nine test blocks in tests/effects_test.ludic.

ludic.base: q_new collided with render3d's quaternion q_new the moment a game
imported both, so the queue constructor is queue_new.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 04:14:09 +03:00
parent 70d05bd504
commit d0ad364ad4
12 changed files with 419 additions and 9 deletions

View file

@ -0,0 +1,46 @@
# state.ludic - what is running, how many may, and the facts the package reports
export property Effect {
kind: int = 0
mag: float = 0.0
left: float = 0.0
delay: float = 0.0
bonus: bool = false
label: string = ""
}
# why an effect ended: it ran out, a newer bonus pushed it past the cap, or the ring was full
export const EFFECT_RAN_OUT: int = 0
export const EFFECT_PUSHED_OUT: int = 1
export const EFFECT_CROWDED_OUT: int = 2
export property EffectEnded {
kind: int = 0
label: string = ""
why: int = 0
bonus: bool = false
}
var ef_list: []Effect = null
var ef_max: int = 8
var ef_bonus_max: int = 3
var ef_ended: Queue<EffectEnded> = null
function ef_all() -> []Effect {
if ef_list == null { ef_list = new []Effect }
return ef_list
}
# the facts: what ended and why, oldest first; the game drains it
export function effects_ended() -> Queue<EffectEnded> {
if ef_ended == null { ef_ended = queue_new("effects.ended") }
return ef_ended
}
# how many may run at once, and how many of those may be bonuses
export function effects_config(max: int, bonus_max: int) -> void {
ef_max = max
ef_bonus_max = bonus_max
}
export function effects_capacity() -> int { return ef_max }
export function effects_bonus_capacity() -> int { return ef_bonus_max }