Skip to main content

AbstractProcedure

AbstractProcedure is the abstract base class for all procedures in SR3E. It provides the scaffolding needed to formalize advanced and opposed rolls: how they are set up, how modifiers are applied, how dice are rolled, and how outcomes are published.

Concrete subclasses (e.g. FirearmProcedure, MeleeProcedure, SpellcastingProcedure) inherit from this base and override specific hooks.


Responsibilities

  • Registration & serialization
    • Each subclass registers itself under a kind string.
    • Procedures can be serialized to JSON and deserialized back, including actor/item references and internal state.
  • State management
    • Writable Svelte stores for title, target number, dice, pool dice, karma dice.
    • Derived stores for total modifiers, final target number, difficulty label.
    • Tracks whether the roll is defaulting, what attribute it links to, and contest identifiers for opposed rolls.
  • Locking
    • Integrates with ProcedureLock to ensure only one procedure flow per actor at a time.
  • Contest support
    • Knows whether it is opposed (hasTargets).
    • Maintains contest identifiers and can deliver contest responses through OpposeRollService.
    • Can build a matching defense procedure on the target side.
  • Hooks for subclasses
    • execute() — must be overridden; drives the actual roll.
    • onChallengeWillRoll() — optional pre-roll hook (attach metadata, adjust dice).
    • onChallengeResolved() — optional post-roll hook.
    • buildResistancePrep() — subclasses can return structured data for follow-up resistance rolls.
    • getResponderPromptHTML() — customize defender prompts in opposed rolls.

State model

StoreTypePurpose / Notes
targetNumberStorewritable<number &#124; null>Base TN before modifiers; finalTNStore clamps to ≥ 2.
modifiersArrayStorewritable<Array<{id?, name, value, poolCap?, forbidPool?, meta?}>>Source of TN adjustments and pool caps/forbid flags.
modifiersTotalStorederived<number>Sum of modifier values.
finalTNStorederived<number &#124; null>max(2, target + sum(mods)); null if no base.
difficultyStorederived<string>Localized label from TN (Simple/Routine/Hard/etc.).
titleStorewritable<string>Panel/roll title; defaults to item name if present.
diceStorewritable<number>Base dice (skill/attr/spec/default).
poolDiceStorewritable<number>User-selected pool dice; clamped by availability and poolCap/forbidPool.
karmaDiceStorewritable<number>Bonus dice from Karma.
linkedAttributeStorewritable<string &#124; null>For defaulting and labeling (e.g., Reaction).
hasTargetsStorereadable<boolean>Live "do I have targets selected?" signal.

Helper: getTotalDiceBreakdown() returns { baseDice, poolDice, karmaDice, totalDice }.


Roll lifecycle

execute({ OnClose?, CommitEffects? })
→ OnClose?.() // close composer UI
→ baseRoll = SR3ERoll.create(buildFormula(true), { actor })
→ onChallengeWillRoll({ baseRoll, actor }) // attach options: dice, pools, TN, basis
→ roll = await baseRoll.evaluate(this)
→ await baseRoll.waitForResolution()
→ CommitEffects?.() // apply ammo/recoil/etc. in subclasses
→ deliverContestResponse(roll) // if contestId is set
→ onChallengeResolved({ roll, actor })
→ return roll

API reference

Construction

  • ProcedureFactory.Create(kind, { actor, item?, args? }) → AbstractProcedure | null: Create the correct subclass instance; used by the Composer, not directly.
  • AbstractProcedure.registerSubclass(kind, Ctor): Register a subclass so it can be deserialized and used in contests.
  • AbstractProcedure.getCtor(kind): Look up a registered subclass constructor.
  • AbstractProcedure.listKinds(): List all currently registered kinds.

Targeting & contests

  • hasTargets: boolean: Snapshot — whether the user currently has targets selected.
  • hasTargetsStore: Readable<boolean>: Reactive store that updates on targeting changes.
  • contestId: string | null: Current contest identifier, if attached.
  • setContestId(id): Bind this procedure to a contest.
  • setContestIds(ids): Replace the local list of contest ids (initiator side).
  • appendContestId(id): Add another contest id.
  • clearContests(): Clear all locally tracked contest ids.
  • deliverContestResponse(rollOrJson): Send roll results back into the contest service.

Core stores & numbers

  • targetNumberStore: Writable<number | null>: Base target number before modifiers.
  • modifiersArrayStore: Writable<Array>: Collection of TN mods and pool restrictions.
  • finalTNStore: Derived<number | null>: Base + modifiers, clamped at minimum 2.
  • difficultyStore: Derived<string>: Localized label for the TN (Simple, Hard, etc.).
  • dice: number / diceStore: Writable<number>: Base dice (skill, attribute, specialization).
  • poolDice: number / poolDiceStore: Writable<number>: User-selected pool dice (clamped).
  • karmaDice: number / karmaDiceStore: Writable<number>: Bonus dice from Karma.
  • linkedAttribute: string | null: Governing attribute, if any.
  • isPrimaryActionEnabled(): Checks if the roll button should be active (TN ≥ 2).
  • finalTN({ floor? }): Compute the effective TN with optional floor.
  • getTotalDiceBreakdown(): Return { baseDice, poolDice, karmaDice, totalDice }.

Modifiers & pools

  • upsertMod({ id?, name, value, poolCap?, forbidPool? }): Add or replace a modifier entry.
  • removeModByIndex(i): Remove a modifier by array index.
  • markModTouchedAt(i): Mark a modifier as user-touched.
  • setSelectedPoolKey(key): Hint which named pool the pool dice come from.

Roll orchestration

  • buildFormula(explodes = true) → "Xd6xTN": Construct the dice formula string.
  • async execute({ OnClose?, CommitEffects? }): Entry point for running a roll; must be implemented by subclasses.
  • async onChallengeWillRoll({ baseRoll, actor }): Pre-roll hook to attach metadata/options.
  • async onChallengeResolved({ roll, actor }): Post-roll hook to apply side-effects.
  • shouldSelfPublish(): Whether this roll should be self-published to chat/logs.

Opposed flow helpers

  • exportForContest(): Package state into a payload for building a defense step.
  • async buildDefenseProcedure(exportCtx, { defender, contestId }): Construct the matching defense procedure on the responder side.
  • async getResponderPromptHTML(exportCtx, { contest }): Build the HTML prompt for the responder; default is yes/no.
  • async renderContestOutcome(exportCtx, ctx): Render contested outcome; subclasses override for detailed breakdowns.
  • buildResistancePrep(exportCtx, { initiator, target }): Return structured data for resistance rolls (damage soak, etc.).

Serialization

  • toJSON(): Serialize to a plain JS object.
  • serialize(): Serialize to a JSON string.
  • static fromJSON(obj, { resolveActor?, resolveItem? }): Deserialize from a JSON object.
  • static deserialize(json, opts?): Deserialize from a JSON string.
  • static registerSubclass(kind, Ctor): Register a new subclass (needed for deserialization).
  • static getCtor(kind): Look up a registered subclass.
  • static listKinds(): List all registered kinds.