An executable action attached to a document (an actor or item) and surfaced as an entry on that document's context menu (and on chat-card buttons). Choosing the entry runs the action.

A Script action is, in effect, a macro attached to a document: instead of sitting on the macro bar it lives on a specific actor or item and runs with that document as its context. An intrinsic action (below) is the same thing authored in code rather than typed by a GM.

Actions are how a character or item does something — run a skill test, make an attack, activate a mystical ability. They come in two flavors that differ only in where the executor comes from:

  • Intrinsic actions — defined in code by Logic classes via defineIntrinsicActions(). The executor is the name of a method on the scoped target logic (e.g. successTest on a Skill), looked up and bound at construction. This is how the system ships its built-in actions.
  • Script actions — GM-authored. The executor is the UUID of a Foundry Macro; running the action invokes Macro#execute (which enforces the MACRO_SCRIPT permission and ownership) with a SohlActionContext-derived scope. No code is stored on, or compiled from, the document — see the security model's "reference code, never compile it" rule. Stored per-document and permission-gated; there is no end-user authoring UI.

Either way, an action carries:

  • a scope (SELF / ITEM / ACTOR) selecting which logic the executor binds to — i.e. what this is when it runs (the action's own logic, the owning item's, or the owning actor's);
  • a trigger predicate (availability) and a visible predicate (UI display), each authored as a sohl.entity.expr.SafeExpression string;
  • an executor that performs the work.

The constructor compiles trigger/visible and resolves/binds executor from their stored string forms, so a finished SohlAction is ready to execute. See sohl.core.logic.SohlLogic.getContextOptions for how actions become context-menu entries.

A Script action's Macro is an ordinary Foundry script Macro, so it can use the full client API (actor, token, and the SoHL sohl/.logic surfaces are passed in scope). If the behavior outgrows a single Macro, reach for a Foundry module instead.

// An intrinsic action definition (from a Logic class's
// defineIntrinsicActions): the executor is the name of a method on the
// scoped target logic, bound at construction.
{ subType: "intrinsic", scope: "self", executor: "successTest", trigger: "true" }
// A Script action references a GM-authored Macro by UUID; the Macro body is
// where the GM's code lives (never on the action).
{ subType: "script", scope: "actor", executor: "Macro.p8f2c1a0d9e7b6a5" }

Hierarchy (View Summary)

Constructors

  • Builds an action, compiling its trigger and visibility predicates and resolving its executor.

    The executor is resolved against the target logic selected by data.scope (SELF → this data model's logic, ITEM → the parent item's logic, ACTOR → the owning actor's logic). For Intrinsic actions the executor is the named method on that target (bound to it); for Script actions it runs the Foundry Macro named by data.executor (a UUID) via Macro#execute. When no executor is supplied, a no-op resolving to undefined is used.

    Parameters

    Returns sohl.entity.action.SohlAction

    If options.parent or data is missing, if data.scope is unknown, or if an Intrinsic executor names a non-existent method on the resolved target.

Properties

The persisted action definition (see SohlAction.Data).

The callable that performs the action. For Intrinsic actions, the named method on the scoped target logic, bound to that target; for Script actions, a thunk that runs the referenced Foundry Macro via Macro#execute (permission-gated; no code is compiled from data). A no-op resolving to undefined when no executor is defined. See ActionExecutorFn.

executorTarget?: SohlLogic

The scope-resolved logic the executor runs on (SELF → this action's logic, ITEM → the parent item's logic, ACTOR → the parent actor's logic) — the same target an Intrinsic executor is bound to. Stamped onto the context as sohl.entity.action.SohlActionContext.thisLogic at dispatch, so inside an intrinsic method this === ctx.thisLogic, and an overriding macro calls the intrinsic via ctx.thisLogic.<executor>(ctx).

Availability predicate compiled from data.trigger; gates execute and composes into visible. See ActionTriggerFn.

UI-visibility predicate compiled from data.visible, composed with execute permission and trigger. See ActionVisibilityFn.

DEFAULT_DISABLED_REASON: "SOHL.Actions.unavailable"

Fallback i18n key for unavailableReason — used when a refused action declares no disabledReason of its own.

Accessors

  • get isAvailable(): boolean
  • Whether the action's trigger currently passes — i.e. whether execute would run it rather than refuse it. Evaluated against the action's own resolved owning documents, so a caller needs no context of its own.

    This is the seam a UI surface asks before offering the action: the Actions tab disables a refused action's run control and shows unavailableReason, rather than presenting a live control that silently does nothing (issue #1135). Note it does not include the Script-action permission gate that execute applies first — that gate is already folded into visible, the DOM-driven predicate.

    Returns boolean

    true when the trigger passes.

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

  • get unavailableReason(): string
  • The i18n key explaining why the action is refused while isAvailable is false — the action's declared data.disabledReason, or a generic fallback when it declares none.

    A key, never localized prose: the value is stored and passed around, and localized only where it is rendered or notified.

    Returns string

    The reason's localization key.

Methods

  • Serialize this instance to a plain object suitable for JSON serialization.

    Returns PlainObject

    A plain object representing this instance, consistent with the Data interface of the subclass.

    The base emits only the kind tag. A subclass that adds state overrides this, chaining ...super.toJSON(), and emits keys matching its own Data interface in persisted form (a uuid/shortcode where the live object holds a resolved reference). The governing rule: toJSON() output must be valid data for the constructor. The transient parent is deliberately not emitted — it is re-supplied on revival.