Skip to main content

Crate dotzuki_rules

Crate dotzuki_rules 

Source
Expand description

§dotzuki-rules — no-code RON authoring for the battle effect-stack (Phase 1)

A game-side loader that turns a declarative rules.ron into runtime Effects dispatched through ONE zero-capture interpreter-bridge fn (interpret) plus a closed primitive-op interpreter (run_ops).

§What this crate is (and is NOT)

  • It depends on the game-agnostic dotzuki_engine only — zero pokered / pokered-core / pokered-data / minimon, zero concrete game type in non-test code, no rand.
  • It is a consumer of the engine’s closed primitive vocabulary (doc 11 §1.1 + doc 12 §3). It amortizes content (one InflictStatus covers every secondary-status move) — it does not extend mechanics. A genuinely new mechanic still needs a Rust primitive + test (doc 11 §5).

§The bridge (doc 11 §2 — Option A, ZERO engine change)

The fold’s only handler call site is a zero-capture fn pointer (HandlerFn); data cannot be a fn pointer. So every data hook points its call field at the single generic interpret fn, which on each call looks up its op-list by the [EffectId] the engine already threads as source_effect (dispatch.rs:128). The loader mints one distinct EffectId per (effect, event) hook and registers each as its own tiny runtime Effect through the existing defaulted resolvers — exactly the Option-A shape doc 11 §2.2 recommends, and the shape minimon already proves with effectiveness_chart_hook. No engine edit, no new trait method on an engine trait.

§Determinism (doc 11 §4)

The interpreter has NO entropy except ctx.rng (a &mut dyn BattleRng). The chance gate compiles to ctx.rng.chance(num, den); there is no clock, no pointer hashing, no HashMap iteration affecting draw order. A ScriptedRng replays a data ruleset identically (same draw count and order) as the native path — a structural guarantee, proved by [tests::scripted_rng_replays_identically].

§Dual-mode sourcing (Phase 2, doc 11 §4.2)

RuleSource yields the same runtime Ruleset from either a baked include_str!’d text (RELEASE; the default build, zero file IO) or a disk path (DEV; behind the hot-reload feature it also watches the file and RuleSource::poll_changed signals an edit so the game rebuilds the registry between turns). A mid-battle reload is safe because effects are addressed by EffectId and live state lives in the engine’s EffectState arena, not the data — the reload swaps the vocabulary, never the in-flight state.

Structs§

CompiledHook
One compiled hook program (doc 11 §2): the closed op-list + its ordering + the optional RNG gate. The driver / interpreter reads this by EffectId.
CompiledRuleset
A compiled ruleset: the synthesized-id → CompiledHook map plus the interned types/stats vocabularies (doc 11 §2 side registry). Built once at load, addressed by EffectId, so a hot-reload swaps the map between turns without invalidating in-flight engine EffectState (doc 11 §4.2).
EffectRecord
One effect record (doc 11 §1). kind selects which resolver hosts it and the engine EffectType; the optional category/power/type/accuracy are per-move data the provider’s damage formula reads (the engine never sees them).
HookRecord
One hook = one (event, ordering, gate, op-list) (doc 11 §1).
Rational
An integer rational [num, den] deserialized from the doc’s bracket-pair RON syntax (doc 11 chance:[30,100], doc 12 mult:[2,1]). RON parses a fixed [u32; 2] as a tuple (requiring (..)), so we deserialize a 2-element list and validate the length — keeping the authored [num, den] form exact.
ResourceCost
One (resource, amount) cost entry on a move (doc 13 §4). resource names a resource from the ruleset’s resources: list; an unknown name is a LoadError::UnknownResource at LOAD.
RulesHost
A &'static bundle of the compiled registry + binding the zero-capture interpret fn reaches without capturing (doc 11 §2.2). A game installs one once (e.g. in a OnceLock) and returns it from RulesProvider::rules_host.
Ruleset
A whole no-code ruleset (doc 11 §1). Flat table of effect records plus the shared types vocabulary and the optional type_chart relation (doc 12 §2).
TraceEvent
One recorded interpreter step (doc 11 §5).
TraceSink
A collector of TraceEvents. Installed thread-locally by a debug flag so the interpreter’s hot path stays branch-light when tracing is off.
TypeChartEntry
One (atk, def, mult) chart edge (doc 12 §2). mult is an integer rational [num, den]; [2,1] = super-effective, [1,2] = resisted, [0,1] = immune. Omitted pairs default to [1,1] (neutral) at lookup time.

Enums§

DamageValue
The source of a Op::SetDamage value (blueprint 15 §2/§3 — the special/fixed damage moves that bypass the type chart: Seismic Toss / Night Shade = the user’s level, Dragon Rage = 40, Sonic Boom = 20, Psywave = rng·(num/den)·level). Every variant is pure (no entropy except the explicit RngScaledLevel which draws ONE ctx.rng byte at the op’s ordinal). Game-agnostic: the only game reach is the per-battler level, supplied by RuleBindings::battler_level.
EffectKind
The five effect kinds (doc 11 §1). Each maps to an EffectType AND to which provider resolver hosts it (crate::ResolverKind).
FinalHitRider
What Op::RepeatHits does AFTER the final hit lands (Twineedle’s final-hit-only poison, blueprint 15 §2). None ⇒ a plain multi-hit (Double Kick / Fury Attack). InflictOnFinal ⇒ on the LAST hit only, draw ONE chance byte and (if it passes the gate) apply the named status to the target — the Twineedle 20%+1 (52/256) poison at the legacy side_effect ordinal. The guards (poison-type immunity, Substitute block) are authored as the SAME VetoIf ops a side-status move uses, evaluated game-side by the interpreter.
FractionOf
The denominator base for a fraction op (doc 11 §1.1).
HitCount
The number of times a Op::RepeatHits re-applies the in-flight move’s damage (blueprint 15 §2/§3, the new game-side multi-hit construct). Gen-1 multi-hit checks accuracy ONCE then deals the SAME computed damage N times; this enum is the source of N. Every variant is game-agnostic — the rules crate names no game-specific concept, only “a count, optionally drawn from one byte”.
LoadError
A statement of which thing in the data layer could not be bound to the closed vocabulary. Every variant is a load-time error (doc 11 §4.2: “a malformed record fails at load, never mid-battle”).
Op
The closed primitive op vocabulary (doc 11 §1.1 + doc 12 §3.1). Each variant maps 1:1 to an existing ctx/RelayVar op. This closed set is the entire expressiveness budget (doc 11 §5).
Predicate
A closed predicate (doc 11 §1.1). Used by unless/when/cond guards.
ResolverKind
Which defaulted resolver hosts a compiled effect (doc 11 §1, §2.2 Option A).
RuleSource
A source of rule text yielding a runtime Ruleset, in one of two modes (doc 11 §4.2). Both modes call the same Ruleset::from_ron, so a baked build and a disk build of the same rules.ron produce byte-identical rulesets — the dual-mode guarantee.
Selector
A target/host selector (doc 11 §1.1). Resolved against the hook’s target/source BattlerRefs.

Traits§

RuleBindings
Resolves interned data-layer indices to concrete P::Stat/P::Status and supplies the type-chart fold + defender-type membership, all pure.
RulesProvider
The game-side bridge trait (doc 11 §2.2 Option A) — extends EffectProvider with the two things the zero-capture interpret fn needs but cannot capture: the compiled op-list registry and the game binding. This adds NO method to any engine trait — it is an additive game-side super-trait, so the engine is untouched.

Functions§

enable_trace
Enable tracing for the current thread (the debug flag, doc 11 §5).
interpret
THE bridge (doc 11 §2): the single zero-capture fn every data hook’s call points at. Keyed entirely off source_effect — the engine already threads it to every handler (dispatch.rs:128). Looks the compiled hook up in the game’s &'static RulesHost, applies the chance gate (the only RNG), then folds the op-list.
parse_event
Parse a hook’s on: string to the closed Event enum (doc 11 §3). An unknown name is a load error. Custom(N) is the open tail.
parse_kind
Parse an EffectKind to the engine [EffectType] (doc 11 §1). The resolver-host mapping is in crate::ResolverKind::from_kind.
run_ops
The closed primitive interpreter (doc 11 §1.1). Folds the hook’s op-list over relay, returning the engine HandlerResult. Short-circuits on the first Fail/FailSilent exactly like the native fold (dispatch.rs:285); a numeric op produces Set; a side-effecting op produces Unchanged (the relay is threaded through).
take_trace
Disable tracing and take the recorded sink (None if tracing was off).

Type Aliases§

StatRef
A stat reference: a name string, interned to a stat index by the loader and resolved to P::Stat by the game binding.
TypeName
A type name string (interned to a chart index by Ruleset::type_index).