A MasteryLevelModifier specialized for combat resolution — attack rolls, defense rolls, and combat technique tests.

CombatModifier inherits all mastery level test functionality and adds no new properties or methods. Its purpose is type discrimination: combat-related modifiers can be identified and handled separately from general mastery level modifiers (e.g., when listing active modifiers on the combat tab, or when combat-specific hooks need to filter for attack/defense modifiers).

Created by the combat-technique sohl.document.item.logic.SkillLogic during initialize for each strike mode's attack and defense (block/counterstrike) modifiers:

this.defense = {
block: new CombatModifier(this),
counterstrike: new CombatModifier(this),
};

Deltas are added during evaluate/finalize for weapon quality, injuries, tactical advantages, and other combat-specific effects.

Hierarchy (View Summary)

Constructors

Properties

baseValue?: number

The base value before deltas (undefined until set; treated as 0 by base).

critFailureDigits: number[]

Roll last-digits that make a failure critical (e.g. [0]).

critSuccessDigits: number[]

Roll last-digits that make a success critical (e.g. [5]).

customFunction?: Function

Handler invoked by a CUSTOM delta to compute a value, when one is used.

The list of ValueDelta modifiers applied on top of the base.

disabledReason: string

Reason the value is disabled as an i18n key; empty string means enabled. Localize for display via disabledLabel. See disabled.

maxTarget: number

Upper clamp on the effective mastery level — the roll-under target can't exceed this.

minTarget: number

Lower clamp on the effective mastery level — the roll-under target can't go below this.

successLevelMod: number

Flat offset applied to the success level after the roll (e.g. a fate bonus).

Success-value table mapping success levels to graded results for value-producing tests.

testDescTable: LimitedDescription[]

Description table mapping success levels to labels and star ratings for chat output.

title: string

Display title for the test.

type: string

Identifier for the kind of test (used in dialogs and chat).

Accessors

  • get chatHtml(): string
  • Render the deltas as an HTML breakdown (name + adjustment per row) for chat cards and tooltips; when disabled, renders the localized disabled reason instead.

    Returns string

  • get deltaLabel(): string
  • A compact, human-readable summary of how the value is derived: the base contribution followed by each applied delta (e.g. Base +30, SSMod +25), or the disabled marker (Dsbl) when disabled. An unmodified value still summarizes as Base +N, so the summary is never empty for an enabled value.

    Named deltaLabel — not shortcode — because a ValueModifier's derivation summary is unrelated to the document system.shortcode identity key used across the rest of the system.

    Returns string

  • get disabled(): string
  • The disabled reason as stored — an i18n key (or "" when enabled). A non-empty value forces effective to 0. This is the serialized form; use disabledLabel for localized display (#948).

    Returns string

  • set disabled(reason: string | boolean): void
  • Disable with a reason string, or toggle via a boolean (passing true applies a default reason; false clears it). A string reason must be an i18n key (see disabled); it is localized for display by disabledLabel.

    Parameters

    • reason: string | boolean

    Returns void

  • get disabledLabel(): string
  • The disabled reason localized for display, or "" when enabled.

    disabledReason always stores an i18n key (never localized prose), so callers that surface the reason to a human must localize it here rather than emitting the raw key (#948). Idempotent on already-plain text.

    Returns string

  • get effective(): number
  • The computed effective value — the base with all deltas applied (always 0 when disabled). Recomputed lazily on access.

    Returns number

  • get kind(): string
  • The serialization discriminator for this instance — the concrete class's static Kind. Written into the JSON by toJSON under the kind key and read back by sohl.utils.defaultFromJSON to select the constructor. Derived from the class, never stored per-instance.

    Returns string

  • get parent(): SohlLogic<any>
  • The Logic that owns this entity. Always present (the constructor rejects a missing parent) and transient — it is not serialized and is re-supplied when the entity is revived or cloned.

    Returns SohlLogic<any>

Methods

  • Fold another modifier into this one, preserving the full auditable derivation: every labeled delta from other (its name, abbrev, operator, and value) is replayed onto this modifier, so the merged result keeps each source justification in its tooltip and this modifier can then layer its own deltas on top.

    Deltas are additiveother's are appended to whatever this modifier already carries (each replayed through the internal _oper, so same-abbrev replacement and OVERRIDE semantics apply, and the clones are re-parented to this modifier). The base is not additive — a modifier has exactly one base — so other's base is adopted only when includeBase is set, and it replaces any existing base rather than adding to it. Omit includeBase to take other's modifiers while keeping this modifier's own base.

    Parameters

    • other: sohl.entity.modifier.ValueModifier

      The modifier to merge from.

    • options: { includeBase?: boolean } = {}

      Merge options.

      • OptionalincludeBase?: boolean

        When set, replace this modifier's base with other's base.

    Returns sohl.entity.modifier.ValueModifier

    this, for chaining.

  • Performs a Mastery Level success test.

    Parameters

    Returns Promise<undefined | false | sohl.entity.result.SuccessTestResult>

    A Promise resolving to undefined if the test was cancelled, false if there was an error during the test (including an unowned speaker), or the result of the success test.

    If no priorTestResult is provided, this method creates a new test result from the mastery level of the current item, collects situational modifiers, and evaluates the outcome — which rolls the d100 (see sohl.entity.result.SuccessTestResult.evaluate).

    If a priorTestResult is provided, this method collects modifiers but applies them to the prior test's roll (without rolling again), generating a new test result from the modifiers and the prior roll. This is what lets modifiers be changed without disturbing the random roll — e.g. when fate is applied to an already-rolled test.

    Interactive vs. headless. By default the modifiers are collected from a pre-roll dialog. When context.skipDialog is set — the path timed and automated effects use, since they run on the active GM with no user present — the dialog is bypassed and the situational modifier is read from context.scope.situationalModifier instead. Set context.noChat to suppress the result chat card (e.g. to avoid spamming a card per checkpoint when a timed handler catches up many elapsed occurrences).

    The result is attributed to context.speaker (falling back to the owning document's speaker); evaluation is refused — returning false — if that speaker is not owned by the running user (a GM-fired event owns every actor).

    Recognized context.scope fields (all optional — see sohl.entity.result.SuccessTestResult.ContextScope for the typed shape and per-field docs). Supplying these is how a generic success test becomes a bespoke graded test without a subclass (see the pass-data pattern):

    • successStarTable — the result-description table mapping each rung to its label / description / star count. Defaults to this modifier's testDescTable.
    • targetValueFunc — remaps the value the outcome grades against when the test keys off something other than the raw mastery level. Defaults to identity (sl => sl).
    • priorTestResult — reuse an already-rolled result instead of rolling fresh (Fate, GM edits, opposed resume); the die is not re-rolled.
    • situationalModifier — the pre-roll modifier applied when skipDialog bypasses the dialog.
    • canFate — whether the posted card may offer a Fate spend (default true; the Fate test itself passes false).

    Context-level (not scope) switches: context.skipDialog bypasses the pre-roll dialog; context.noChat suppresses the result card (post it yourself with sohl.entity.result.SuccessTestResult.toChat — e.g. to attach follow-up buttons).

    If SuccessTestResult construction fails (internal error).