Abstract base class for all business logic in the SoHL system.

Every actor type and item type has a corresponding Logic class that extends SohlLogic. Logic classes are responsible for game rules, calculations, and actions — separated from data persistence (SohlDataModel) and UI presentation (Sheet classes).

Logic instances are created automatically by the data model's create() factory and are accessible via document.system.logic (or the convenience document.logic accessor on SohlActor and SohlItem).

Foundry VTT's default behavior processes each embedded item fully (prepareBaseDataprepareEmbeddedDocumentsprepareDerivedData) before moving to the next item. This means sibling items cannot depend on each other — when Item B prepares, Item A may or may not be ready.

SoHL overrides this in SohlActor.prepareEmbeddedDocuments to run three phases across all items with barriers between them:

  1. initialize — Set up base state from persisted data: create ValueModifiers, set base values. Cannot read sibling items (they may not have initialized yet).
  2. evaluate — Compute derived values that depend on sibling items being initialized (e.g., a Skill reading trait attribute values for its skill base formula). All initialize() calls across every item on the actor complete before any evaluate() runs.
  3. finalize — Resolve cross-item dependencies that require sibling items to have been evaluated (e.g., fate mastery level depending on a fully computed Aura trait). All evaluate() calls complete before any finalize() runs.

How Foundry's data-preparation hooks map onto these phases (the actor's own logic runs around the item passes):

Foundry calls:            SoHL runs:
prepareBaseData()     →   actor.logic.initialize()
prepareEmbeddedDocuments() →   per item: initialize()  ═ barrier ═  evaluate()  ═ barrier ═  finalize()
prepareDerivedData()  →   actor.logic.evaluate(), then actor.logic.finalize()

These method names are deliberately different from Foundry's prepareBaseData/prepareDerivedData to signal that they follow different ordering rules. Do not implement Foundry's preparation methods on items — use these lifecycle methods instead.

See also the Phase-batched lifecycle concept overview and the Lifecycle Hooks extension guide.

Type Parameters

Hierarchy (View Summary)

Indexable

  • [key: symbol]: true

Constructors

  • 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.

    Type Parameters

    Parameters

    • data: PlainObject = {}

      Reserved base data (unused by the base class).

    • options: PlainObject = {}

      Must provide options.parent, the data model this logic is embedded in; the parent's actionDefs are used to build actions.

    Returns SohlLogic<TData>

    If no parent is provided.

Properties

actions: SohlMap<string, sohl.entity.action.SohlAction>

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).

Accessors

  • get actor(): null | SohlActor
  • The owning SohlActor — the document itself when it is an actor, otherwise its owning actor (for an item, combatant, or effect), or null.

    Returns null | SohlActor

  • get actorLogic(): null | SohlActorLogic<any>
  • 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.

    Returns null | SohlActorLogic<any>

    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.

  • get data(): TData
  • 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.

    Returns TData

    Convenience accessor for parent.

Methods

  • Compute derived values that depend on sibling items being initialized.

    Returns void

    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.

    Parameters

    Returns Promise<unknown>

    The result of the action execution, or undefined if the action was not found or could not be executed.

  • Resolve cross-item dependencies that require all items to have been evaluated.

    Returns void

    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.

    Returns ContextMenuEntry[]

    The available context-menu entries.

    One entry per action whose visible predicate currently passes (an action's trigger / domain preconditions can hide it); SCRIPT actions are additionally permission-gated when executed. Use this to discover which actions can be performed on the document.

  • Set up base state from persisted data: create ValueModifiers, set base values.

    Returns void

    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.

  • Serialize this logic to a plain reference.

    Returns PlainObject

    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.