Element Crystals — an alternate element ruleset (A/B vs reactions)
Element Crystals — an alternate element ruleset (A/B vs reactions)
Section titled “Element Crystals — an alternate element ruleset (A/B vs reactions)”Status: DESIGN APPROVED 2026-06-25 — not yet implemented. Build after the other agent’s
cycle-21 sim.gd churn settles (avoid conflicts). Author: Opus, from Toby’s playtest analysis +
Chris’s direction.
Background — the problem with the current (reactions) system
Section titled “Background — the problem with the current (reactions) system”Toby’s playtest conclusion, confirmed by the project’s own notes: with many fast auto-firing weapons of different elements, elemental reactions fire constantly and collapse into “permanent status effects + a ton of damage” — the strategy evaporates (CLAUDE.md cycle 12-13: “reactions are VERY lethal … constant AoE bursts + terrain DoT; ~350 kills/min, player untouched … trivializes challenge”). Toby framed it as combinatorial blow-up (17 elements → 17C2 = 136 pairs). Technical nuance: the sim is actually single-active-aura (1 aura + innate base, react-then-replace — not 136 live pairs), so the mechanism is constant re-triggering under auto-fire, not simultaneous coexistence. The outcome Toby describes is exactly right either way: reactions become background noise, not a decision.
Prototype a fundamentally different element system — Element Crystals — that turns elements into a build economy driving weapon upgrades instead of real-time reactions. Run it as an A/B against the current reactions system in the SAME repo, both playable, telemetry-tagged, so real data decides which ships.
Why this fits the game’s pillars: it preserves Pillar 1 (one-stick, auto-fire/auto-target — no manual element management) and leans into Pillar 2 (deep transformative build-craft). (The rejected alternative — “one player-controlled weapon at a time + richer reactions” — fights Pillar 1.)
Locked decisions
Section titled “Locked decisions”- Vehicle: in-repo A/B behind a ruleset flag (NOT a separate game; reuses the deterministic sim / enemies / renderers / telemetry — a true apples-to-apples comparison).
- Upgrade model: weapons upgrade automatically when crystal thresholds are met; the manual
per-weapon mods (
wm:) become the threshold table. Level-up offers crystals (~1/3) + the existing weapon-grant / stat-mod / transformative-mod pool (~2/3). High thresholds trigger evolutions. - Enemy elements: pure economy — reactions OFF, enemy element is cosmetic only. Crystals are the player’s build resource; no resistances, no status effects.
- Crystal types: start with a curated ~6 core types (e.g. FIRE / COLD / LIGHTNING / VOID / BLOOD / LIGHT) for a legible portfolio; expandable later.
Architecture — the A/B seam
Section titled “Architecture — the A/B seam”Sim.ruleset: int = RULESET_REACTIONS(enum:RULESET_REACTIONSdefault |RULESET_CRYSTALS). Mirrors the provenSim.storynull-object pattern. Defaults to reactions and is set only bymainafter construction → the determinism baseline is byte-identical (the determinism test never sets it). ASim.enable_crystals()(or a setter) flips it before the run, likeenable_story().- In
RULESET_CRYSTALS, the reaction path short-circuits.Sim._apply_element(ei, el)still sets the cosmetic aura tint for visuals but skips_on_reaction/_reaction_burst/ terrain zones / priming.StatusEffectsDoT + shock-vuln are off (they’re part of the reaction system). Enemies take direct weapon damage only. Every reaction hook is guardedif ruleset == RULESET_REACTIONS. - Determinism for crystals mode itself: crystal drops draw from
upgrade_rng(NOT the spawnrng, same discipline as today). A separatetest_determinism_crystals.gdcan pin a crystals-mode trace once built; the existing baseline test stays on reactions.
Components
Section titled “Components”sim/crystal_state.gd — CrystalState (pure /sim, RefCounted)
Section titled “sim/crystal_state.gd — CrystalState (pure /sim, RefCounted)”Per-run crystal wallet. counts: Dictionary (element_id → int). add(element_id, n),
count(element_id) -> int, total(), to_dict/from_dict. Lives on Sim.crystals. Per-run only
(lost at game over — NOT meta-persisted).
Crystal economy (drops)
Section titled “Crystal economy (drops)”- ~6 core crystal types to start (subset of existing
bible.jsonelements; expandable). - At level-up,
roll_upgrade_choices(crystals mode) gives ~1/3 chance one of the 3 choices is an Element Crystals grant: a randomised bundle (e.g. 2-5 crystals across 1-3 types) drawn fromupgrade_rng. Picking it callscrystals.add(...). Randomisation forces build variety. - The other ~2/3 of choices come from the existing pool: weapon grants + stat mods + transformative
mods. The
wm:per-weapon mechanical mods are NOT offered in crystals mode (thresholds do that job). Manualevolve:offers are likewise replaced by threshold-driven evolutions.
Weapon thresholds — sim/weapon_thresholds.gd + data
Section titled “Weapon thresholds — sim/weapon_thresholds.gd + data”- Data-driven table (in
bible.json, per weapon): a list of rules{element, amount, effect}, whereeffectis an existing weapon-mod kind (e.g.power,shards,radius,chain, …) orevolve. Example (orbit):[{cold:5,"shards"}, {cold:12,"spin"}, {lightning:8,"reach"}, {void:15,"evolve"}]. Each weapon keys off several element types → varied builds. Sim._eval_thresholds()runs whenever crystals change, when a weapon is granted (a late weapon instantly inherits thresholds already met by your existing pile), and once at run start. For each owned weapon, any rule whosecrystals.count(element) >= amountAND not-yet-applied fires the effect via the weapon’s existingapply_mod(kind, mag)/evolve()(mechanics already exist — we just trigger them from crystals instead of manual picks). A per-weapon “applied thresholds” set makes it idempotent (only newly-crossed rules fire).- Non-consuming + shared: thresholds only read crystal counts; one FIRE pile can satisfy thresholds on multiple weapons simultaneously → emergent synergy (the headline feature).
Level-up integration
Section titled “Level-up integration”Sim.roll_upgrade_choices(n) branches on ruleset. Crystals mode: inject a crystal grant with ~1/3
weight, fill remaining slots from weapon-grant + stat-mod + transformative-mod pools (exclude wm:
and evolve:). apply_upgrade handles a new crystals:<bundle> id by adding to CrystalState then
calling _eval_thresholds().
Balance
Section titled “Balance”Removing reactions removes a large AoE-clear contribution → player DPS drops; the threshold ramp compensates but on a different curve. Crystals mode gets its own enemy HP/count tuning (it’s an independent ruleset, free to diverge from reactions mode). Start by reusing current enemy stats, measure via telemetry, adjust. Expect to tune: crystal drop amounts, threshold magnitudes, and enemy HP for crystals mode.
A/B comparison + telemetry
Section titled “A/B comparison + telemetry”- Add
ruleset(“reactions” | “crystals”) to the gameplay summary (gameplay_telemetry.report_run) and aruns.rulesetcolumn; dashboard groups runs by ruleset. - Comparison metrics: median run length, level reached, build variety (
build_picks/ distinct weapon-evolution paths), kills/min, and — once we add it — session count / replay rate. The reactions-vs-crystals verdict comes from this data, not opinion. - Launch menu gains a Crystals entry (alongside Survival / Story) so Chris + Toby can play either; reactions stays the default.
Scope — explicitly OUT for v1 (YAGNI)
Section titled “Scope — explicitly OUT for v1 (YAGNI)”No enemy resistances/weaknesses; no crystals dropped from kills (level-up only); no meta-persistence of crystals (per-run); no new elements beyond the core ~6; no crystals-only enemies or arenas. This is a clean swap of the element layer only — everything else (movement, weapons, enemies, bosses, spawning, meta-progression) is shared between the two rulesets.
Implementation coordination
Section titled “Implementation coordination”The crystals path touches sim.gd heavily (ruleset guards, _eval_thresholds, level-up branch).
The other agent is currently deep in sim.gd (cycle 21). Build this after their churn settles, on
a clean tree, and keep every change behind the ruleset guard so the reactions baseline never moves.
Open / tuning items (resolve during implementation)
Section titled “Open / tuning items (resolve during implementation)”- Exact 6 core elements + their visual identities.
- Crystal bundle size + per-type distribution; level-up crystal-offer probability (~1/3 start).
- Per-weapon threshold tables (amounts + which effect at each tier) for all 6 weapons.
- Crystals-mode enemy HP/count curve.
- Whether
_eval_thresholdsruns every crystal change (simple) vs batched per level-up (cheaper) — start simple (per change), it’s bounded by weapon count × rules.