Binds this logic to its parent data model and builds the actions map from the parent's intrinsic and scripted action definitions, selecting a default action.
Reserved base data (unused by the base class).
Must provide options.parent, the data model this logic
is embedded in; the parent's actionDefs are used to build
actions.
Executable actions for this document, keyed by shortcode — context-menu entries, chat-card buttons, and lifecycle hooks. A script action shadows (wholly overrides) the intrinsic action of the same shortcode (see the constructor).
The number of mastery-level boosts applied to this skill. Each boost
raises the base mastery level by an amount that diminishes at higher
levels (see calcMasteryBoost).
The fate mastery level as a sohl.entity.modifier.MasteryLevelModifier, used to resolve
fate tests. Seeded from the actor's Aura attribute and
the optionFate setting; disabled when fate does not apply.
The mastery level as a sohl.entity.modifier.MasteryLevelModifier, seeded from SkillData.masteryLevelBase.
The seeded mastery-level base — the value masteryLevel was seeded
with in initialize (a stored SkillData.masteryLevelBase,
or an on-actor skill's opening Skill Base × initSkillMult), captured
before evaluate folds in this skill's own boosts and
clamp. Cross-item effects that boost this skill (a boost Mystery) compute
their contribution from this baseline rather than the mutated
masteryLevel.base.
The parent (base) skill this skill specializes, resolved during
evaluate from SkillData.parentSkillCode, or null if
this skill has no parent.
The computed skill base value, derived from
SkillData.skillBaseFormula — a value-returning
sohl.entity.expr.SafeExpression — evaluated against the actor's
attribute values (the attr.<shortcode> namespace).
0 when the formula is blank, invalid, or off an actor.
OptionalskillThe Skill-Base error message when the formula failed to compile or
evaluate (a sohl.entity.expr.SafeExpressionError message, or a
"did not return a number" message), otherwise undefined. A non-blank
value flags the skill invalid (see skillBaseValid); the sheet
surfaces it and the internal skillBase falls back to 0.
The parsed Skill-Base sohl.entity.expr.SafeExpression, or null
when the formula is blank or failed to compile/evaluate. Retained so
attribute-dependency predicates (e.g. the Aura → no-fate gate) can walk the
AST via sohl.entity.expr.SafeExpression.attrRefs rather than a regex.
OptionalstrikeThe runtime strike-mode instance for a combattechnique skill, built in
initialize from SkillData.strikeMode. undefined for every
other skill subtype. Its attack/defense modifiers are driven by the
governing mastery level in finalize (this skill's own by default,
or an override skill named by the strike mode's assocSkillCode).
The owning SohlActor — the document itself when it is an actor,
otherwise its owning actor (for an item, combatant, or effect), or null.
The logic of the owning actor — the Foundry-free way to reach the actor
layer from any logic. For an actor's own logic this is itself; for an
item's logic it is the owning actor's logic; otherwise null.
Resolved through the SohlLogicData port, so logic code
can navigate to the actor (and iterate items via allLogics /
logicTypes / getItemLogic) without touching the Foundry document.
The Fate Mysteries on the actor that may be spent on this skill's tests:
every fate-subtype Mystery whose scope matches (a general point with
no assocSkillCode, or one specific to this skill's shortcode) that
still has a charge available (infinite, or charges.value > 0).
This is the eligibility set the Fate action is gated on (available iff ≥1) and the source list a spend is drawn from. Fate Points are not a scalar — they live as charges distributed across these Mystery items (#854).
The eligible-and-charged Fate MysteryLogic instances (empty off an actor).
Whether the skill may be improved: true when the current user is a GM or owns the item and the mastery level is not disabled.
This logic's typed data — its *Data interface (e.g. SkillData), the same
persisted object as document.system. Prefer document.logic.data when
reading a document's fields from a macro, Script Action, or module: it is the
typed, API-documented surface (autocomplete and reference links resolve),
whereas document.system is typed as the internal DataModel.
Convenience accessor for parent.
Whether this skill exposes a melee strike mode — the gate the block and counterstrike actions hang their visibility on (#1137). A missile combat technique (a flung quill, spat venom) can never block or counterstrike, so it must not offer those actions.
The owning document's id.
The owning SohlItem.
The owning document's kind (its actor or item type id).
The skill's display label. When this skill specializes another (its
SkillData.parentSkillCode resolves to a parentSkill), the
parent skill's name is appended in parentheses after the base label —
e.g. Sword (Combat). The parenthetical is built from the localizable
SOHL.Skill.labelWithParent format string so the convention can be
adapted per language. Falls back to the inherited label when the skill
has no resolvable parent.
The magic modifier applied to this skill's fate mastery level. The base implementation returns 0; subclasses may override to contribute a bonus.
The owning document's name.
The amount by which improveWithSDR raises the base mastery level on success.
The Skill Base added to the SDR's 1d100 — a skill improves off its own
computed skillBase, so natural aptitude speeds development.
Satisfies sohl.document.item.logic.SdrImprovable.
The attribute shortcodes this skill's Skill Base is based on, ordered primary first — the answer to "which attributes does this skill use?" without a caller having to parse the formula itself (#1175).
Read off the parsed formula, so it can never drift from the formula the way a separately-stored list would:
sb(...) — every skill shipped with the system —
the arguments of that call are the basis, in the order they were
written: sb(attr.rea, attr.per) yields ["rea", "per"]. An attribute
referenced elsewhere in the formula is excluded, because it adjusts the
result rather than forming the basis: sb(attr.str, attr.dex) + attr.aur / 10 yields ["str", "dex"].sb() (e.g.
(attr.str + attr.agl) / 2), every referenced attribute is the basis.[].Consumers include the Aura → no-Fate rule (see evaluate); sheet display and character-build tooling can read the same list.
Whether the Skill-Base formula compiled and evaluated to a number. true
for a blank formula (blank ≠ invalid — it simply yields SB 0); false only
when a non-blank formula failed to compile or did not return a number
(skillBaseError carries the reason).
A SohlSpeaker for the owning actor/item (a blank speaker if neither resolves).
The runtime strike modes for this skill: the single strikeMode
when this is a combattechnique skill, otherwise empty. Lets combat code
aggregate technique strike modes uniformly with weapon strike modes.
The strike-mode instances (zero or one).
Localized type (and sub-type, when present) label for the owning document.
The owning document's UUID — the opaque identity token from the data port.
Whether the skill's base formula is valid (an alias of skillBaseValid). A blank formula is valid; a malformed expression or one that does not return a number is not.
Perform an assisted attack with this combat technique's strike mode.
Intrinsic-action executor for the attackTest action (combat techniques
only). Delegates to the shared runStrikeModeTest, which weapons use
too — a technique always has exactly one strike mode (strikeModes),
so it is auto-selected and never prompts.
The action context; scope.strikeModeId selects the mode.
The test result, undefined if the roll was cancelled, or false
when no strike mode could be resolved.
Perform an assisted block with this combat technique's strike mode.
Intrinsic-action executor for the blockTest action (combat techniques
only). A block requested on a non-melee mode resolves to false (see
runStrikeModeTest).
The action context; scope.strikeModeId selects the mode.
The test result, undefined if the roll was cancelled, or false
when no melee strike mode could be resolved.
Perform an assisted counterstrike with this combat technique's strike mode.
Intrinsic-action executor for the counterstrikeTest action (combat
techniques only). A counterstrike requested on a non-melee mode resolves to
false (see runStrikeModeTest).
The action context; scope.strikeModeId selects the mode.
The test result, undefined if the roll was cancelled, or false
when no melee strike mode could be resolved.
Delete the owning document, after confirming with the user.
The action context; unused.
Open the sheet for the owning document.
The action context; unused.
Compute derived values that depend on sibling items being initialized.
Called on every item after ALL items have completed initialize.
Safe to access: sibling items' initialized state (e.g., reading trait attribute values for a skill base formula).
Not safe to access: sibling items' evaluated state — another item's
evaluate() may not have run yet. Dependencies on evaluated state
belong in finalize.
Example: a Skill reads trait attribute values to compute its skill
base; a gear item resolves its containerId to find its parent container.
Execute an action by shortcode, using the provided context or creating a new one.
The shortcode of the action to execute.
Optionalcontext: sohl.entity.action.SohlActionContextThe action context to use, if any.
The result of the action execution, or undefined if the action was not found or could not be executed.
Spend Fate on this skill's test — the shared post-roll bump flow (sohl.document.item.logic.performFateTest); the die is never re-rolled (#854).
Triggered at the player's behest by the test card's Fate button or the Being sheet's fate cell.
The action context; context.scope.priorTestResult is the
original sohl.entity.result.SuccessTestResult being fated (absent
when invoked with no card to amend — then only the Fate test is rolled).
Resolves once the Fate test, any consumption, and the re-post complete. A no-op (with a warning) when Fate is unavailable, and a silent return when the player dismisses the Fate roll or a required choice.
Resolve cross-item dependencies that require all items to have been evaluated.
Called on every item after ALL items have completed evaluate.
Safe to access: all sibling items' initialized and evaluated state.
Example: fate mastery level (which depends on an already-evaluated Aura trait); encumbrance totals summed across all evaluated gear.
The context-menu options — the actions currently available — for this logic's document.
The available context-menu entries.
Attempts to improve the skill via a Skill Development Roll (SDR): rolls
1d100 + skillBase against the current base mastery level, and on a
success raises SkillData.masteryLevelBase by sdrIncr. The
outcome is persisted — the improve flag is cleared and, on success, the
raised base mastery level is written back — and then posted to chat.
The action context whose speaker receives the chat card.
Resolves once the roll is evaluated, persisted, and the chat card is posted.
Set up base state from persisted data: create ValueModifiers, set base values.
Called on every item before any item's evaluate runs.
Safe to access: own persisted data fields (this.data.*).
Not safe to access: sibling items on the same actor — they may not have initialized yet. Cross-item reads belong in evaluate.
Example: a Skill creates its MasteryLevelModifier
from persisted fields; it does not yet read trait attribute values.
Begins an opposed test backed by this skill's mastery level.
Intrinsic-action executor for the opposedTestStart action. Opposed tests
are token-based: this delegates into the actor's token logic
sohl.document.token.logic.SohlTokenDocumentLogic.opposedTestStart, passing this skill's
logicUuid as the source — exactly as the weapon/technique combat actions
delegate into the combatant.
The action context (speaker, scope) for the test.
The opposed test result, or null if cancelled or unavailable.
Post this item's description to the chat log — a human-triggered, informational card (no follow-up buttons) built by buildItemDescCardData. Assist, never act: this only shows the item's own text; it takes no action on any character.
The action context; unused.
Post the Fate result card for this skill. Delegates to the shared implementation; defined here so the card post is spy-able per logic type.
The evaluated Fate test result.
The resolved fate outcome.
The Fate Mystery a point was consumed from, if any.
Resolves once the card has been handed to the speaker.
Intrinsic action performed after finalize lifecycle stage. This is intended for modules to hook into (or ActionItems to override) to perform additional logic after the main lifecycle stages have completed.
The action context for the post-finalize hook.
Recalculates the mastery level base using the roll formula stored in
flags.sohl.rollFormula. The formula is a standard Foundry VTT roll
expression where the variable sb is replaced with the skill base
value (always 0 for traits).
If no roll formula flag is set, this method does nothing.
GM result-edit for a posted test card (#856) — the higher-fidelity
counterpart to Fate. Re-opens the standard test dialog pre-filled with the
result's current situational and success-level modifiers; on submit it
applies the new modifiers and re-evaluates on the SAME frozen roll
(never a re-roll, no Fate cost), then reposts the card. The prior result
rides in context.scope.priorTestResult (the reconstruction seam), so this
works for any standard test card — skill, attribute, or combat strike mode.
Changing the situational modifier changes the effective target, so the base success level re-derives from the frozen roll; the success-level modifier is a flat offset applied after. Clicking OK without a change is a no-op (nothing re-evaluated, nothing reposted).
GM-only. The pencil is render-hidden from non-GMs (sohl.document.chat.gateEditActionPencil); this is the click-time half of that gate — a synthesized click from a non-GM is refused here.
The action context; context.scope.priorTestResult is the
result being edited. When skipDialog is set, the new
situationalModifier / successLevelMod / rollMode are taken from
context.scope instead of the dialog.
The re-evaluated result, or undefined when refused (non-GM),
cancelled (dialog dismissed), or unchanged (no-op).
Flags this skill for improvement via a Skill Development Roll.
Intrinsic-action executor for the setImproveFlag action.
The action context (unused).
Resolves once the item update completes.
Build an update() payload that replaces this combat technique's single
strike mode. A combattechnique skill stores exactly one strike mode at
system.strikeMode (a discriminated melee/missile field), so the whole
value is written rather than the id-keyed dict a weapon uses.
The strike-mode data to store.
An update() payload writing system.strikeMode.
Performs a success test against this skill's mastery level.
Intrinsic-action executor for the successTest action; delegates to
sohl.entity.modifier.MasteryLevelModifier.successTest.
The action context (speaker, scope) for the test.
The test result, null if cancelled, or false on error.
Performs a Success Value test against this skill's mastery level — a
success test graded into a Success Value (Index + Modifier) and Success
Stars via the skill's svTable, for resolving sustained work (crafting,
sailing, research) in a single roll instead of many (#848).
Intrinsic-action executor for the successValueTest action; delegates to
sohl.entity.modifier.MasteryLevelModifier.successValueTest, which
drives the one generic success-test path with the svTable and grading
targetValueFunc supplied as data — no bespoke test code.
The action context (speaker, scope) for the test.
The graded test result, undefined if cancelled, or false on error.
Toggles this skill's improvement flag.
Intrinsic-action executor for the toggleImproveFlag action.
The action context (unused).
Resolves once the item update completes.
Serialize this logic to a plain reference.
A uuid-keyed reference to this logic.
A logic is a behavior wrapper over a live Foundry document; it is never
revived from its own JSON (its constructor needs that document). Wherever a
logic is persisted — a chat card, an action sohl.entity.action.SohlActionContext.scope
— it is re-resolved from its uuid (e.g. via fvttLogicFromUuidSync), not
rebuilt from a payload. So it serializes as a compact, resolvable reference
(name/kind are carried for display and debugging); the owning document
holds the actual persisted state.
Clears this skill's improvement flag.
Intrinsic-action executor for the unsetImproveFlag action.
The action context (unused).
Resolves once the item update completes.
StaticdefineDefine and return all intrinsic actions for skill logic.
The intrinsic action definitions, including those inherited from the base logic.
A trained capability with a mastery level.
Skills represent learned abilities that characters use to accomplish tasks: combat techniques, social interactions, crafting, perception, and more. Each skill has a skill base formula (typically derived from one or more traits like Strength, Dexterity, or Aura) and a mastery level representing training and experience.
Skills are categorized by subType (e.g., combat, social, physical) and may be associated with a weapon group or a mystery. A skill can also reference a base skill from which it derives or shares advancement.
Skills are the primary mechanism for resolving actions in SoHL. When a character attempts a task, the relevant skill's mastery level is tested against a target number, with modifiers from traits, gear, conditions, and situational factors.
Mastery level progression, fate integration, and SDR improvement are built on sohl.entity.modifier.MasteryLevelModifier.