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_engineonly — zero pokered / pokered-core / pokered-data / minimon, zero concrete game type in non-test code, norand. - It is a consumer of the engine’s closed primitive vocabulary
(doc 11 §1.1 + doc 12 §3). It amortizes content (one
InflictStatuscovers 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§
- Compiled
Hook - One compiled hook program (doc 11 §2): the closed op-list + its ordering +
the optional RNG gate. The driver / interpreter reads this by
EffectId. - Compiled
Ruleset - A compiled ruleset: the synthesized-id →
CompiledHookmap plus the internedtypes/statsvocabularies (doc 11 §2 side registry). Built once at load, addressed byEffectId, so a hot-reload swaps the map between turns without invalidating in-flight engineEffectState(doc 11 §4.2). - Effect
Record - One effect record (doc 11 §1).
kindselects which resolver hosts it and the engineEffectType; the optionalcategory/power/type/accuracyare per-move data the provider’s damage formula reads (the engine never sees them). - Hook
Record - 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 11chance:[30,100], doc 12mult:[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. - Resource
Cost - One
(resource, amount)cost entry on a move (doc 13 §4).resourcenames a resource from the ruleset’sresources:list; an unknown name is aLoadError::UnknownResourceat LOAD. - Rules
Host - A
&'staticbundle of the compiled registry + binding the zero-captureinterpretfnreaches without capturing (doc 11 §2.2). A game installs one once (e.g. in aOnceLock) and returns it fromRulesProvider::rules_host. - Ruleset
- A whole no-code ruleset (doc 11 §1). Flat table of effect records plus the
shared
typesvocabulary and the optionaltype_chartrelation (doc 12 §2). - Trace
Event - One recorded interpreter step (doc 11 §5).
- Trace
Sink - A collector of
TraceEvents. Installed thread-locally by a debug flag so the interpreter’s hot path stays branch-light when tracing is off. - Type
Chart Entry - One
(atk, def, mult)chart edge (doc 12 §2).multis 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§
- Damage
Value - The source of a
Op::SetDamagevalue (blueprint15§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 explicitRngScaledLevelwhich draws ONEctx.rngbyte at the op’s ordinal). Game-agnostic: the only game reach is the per-battler level, supplied byRuleBindings::battler_level. - Effect
Kind - The five effect kinds (doc 11 §1). Each maps to an
EffectTypeAND to which provider resolver hosts it (crate::ResolverKind). - Final
HitRider - What
Op::RepeatHitsdoes AFTER the final hit lands (Twineedle’s final-hit-only poison, blueprint15§2).None⇒ a plain multi-hit (Double Kick / Fury Attack).InflictOnFinal⇒ on the LAST hit only, draw ONEchancebyte and (if it passes the gate) apply the named status to the target — the Twineedle 20%+1 (52/256) poison at the legacyside_effectordinal. The guards (poison-type immunity, Substitute block) are authored as the SAMEVetoIfops a side-status move uses, evaluated game-side by the interpreter. - Fraction
Of - The denominator base for a fraction op (doc 11 §1.1).
- HitCount
- The number of times a
Op::RepeatHitsre-applies the in-flight move’s damage (blueprint15§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”. - Load
Error - 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/RelayVarop. This closed set is the entire expressiveness budget (doc 11 §5). - Predicate
- A closed predicate (doc 11 §1.1). Used by
unless/when/condguards. - Resolver
Kind - Which defaulted resolver hosts a compiled effect (doc 11 §1, §2.2 Option A).
- Rule
Source - A source of rule text yielding a runtime
Ruleset, in one of two modes (doc 11 §4.2). Both modes call the sameRuleset::from_ron, so a baked build and a disk build of the samerules.ronproduce byte-identical rulesets — the dual-mode guarantee. - Selector
- A target/host selector (doc 11 §1.1). Resolved against the hook’s
target/sourceBattlerRefs.
Traits§
- Rule
Bindings - Resolves interned data-layer indices to concrete
P::Stat/P::Statusand supplies the type-chart fold + defender-type membership, all pure. - Rules
Provider - The game-side bridge trait (doc 11 §2.2 Option A) — extends
EffectProviderwith the two things the zero-captureinterpretfnneeds 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
fnevery data hook’scallpoints at. Keyed entirely offsource_effect— the engine already threads it to every handler (dispatch.rs:128). Looks the compiled hook up in the game’s&'staticRulesHost, applies thechancegate (the only RNG), then folds the op-list. - parse_
event - Parse a hook’s
on:string to the closedEventenum (doc 11 §3). An unknown name is a load error.Custom(N)is the open tail. - parse_
kind - Parse an
EffectKindto the engine [EffectType] (doc 11 §1). The resolver-host mapping is incrate::ResolverKind::from_kind. - run_ops
- The closed primitive interpreter (doc 11 §1.1). Folds the hook’s op-list over
relay, returning the engineHandlerResult. Short-circuits on the firstFail/FailSilentexactly like the native fold (dispatch.rs:285); a numeric op producesSet; a side-effecting op producesUnchanged(the relay is threaded through). - take_
trace - Disable tracing and take the recorded sink (
Noneif tracing was off).
Type Aliases§
- StatRef
- A stat reference: a name string, interned to a stat index by the loader and
resolved to
P::Statby the game binding. - Type
Name - A type name string (interned to a chart index by
Ruleset::type_index).