A single person, creature, or NPC.

A Being is the most detailed actor type in SoHL, representing an individual entity with a full anatomy model (body roles, body parts, body locations), skills, traits, injuries, afflictions, gear, and mystical abilities. Beings are the primary participants in combat, skill tests, and social interactions.

The being's physical body — anatomy, body weight, reach, body-scale — lives on its own body sub-object (system.body), dissolved from the former Corpus item into the Being (#535). Movement (feetPerRound / leaguesPerWatch / moveProfile) is a universal actor capability on sohl.document.actor.logic.SohlActorBaseLogic. BeingLogic additionally derives movement's strengthModifier / encumbrance from its strength and carried weight, and being-owned state (healthBand plus the numeric system.health it writes, healingBase, shockState, pull, carriedWeight). An incorporeal being is one with an empty body structure (see sohl.document.actor.logic.BodyLogic).

Type Parameters

Hierarchy (View Summary)

Indexable

  • [key: symbol]: true

Constructors

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

The being's body — its anatomy, weight, reach, and body-scale, derived from system.body. Constructed directly in initialize (no embedded item, no cross-document registration). An incorporeal being has an empty body structure (sohl.document.actor.logic.BodyLogic.isIncorporeal).

Running total of carried-gear weight (pounds) as a sohl.entity.modifier.ValueModifier, accumulated ground-up: each carried gear item adds a delta of its weight × quantity during its own evaluate() phase (see sohl.document.item.logic.GearLogic.evaluate). Reset to an empty modifier at the start of initialize and fully populated (read via carriedWeight.effective) by the time the being's own evaluate()/finalize() and the sheet read it.

The being's encumbrance, as a sohl.entity.modifier.ValueModifier. Derived in finalize from the active movement profile's encumbrance expression of the being's carriedWeight.

The being's Fatigue Penalty as a sohl.entity.modifier.ValueModifier — the total Fatigue Levels across every fatigue-subtype trauma (windedness / weariness / weakness are recorded as separate instances because each recovers at its own rate). It penalizes all tests and Move rate. Seeded in finalize once traumas are prepared; there is no persisted field.

The actor's tactical move (feet per combat round) as a sohl.entity.modifier.ValueModifier so runtime modifiers (haste, encumbrance, etc.) can layer on. Seeded from the active moveProfile when it is enabled.

The being's Healing Base as a sohl.entity.modifier.ValueModifier — the mastery-level factor governing recovery, seeded in evaluate to the average of the being's Endurance and Will scores (rounded up when END > WIL, else down; see healingBaseFor) and open to trait and treatment deltas on top. Multiplied by a Healing Rate, it is the target of nearly every recovery test in the system. An empty modifier (base 0) when the being lacks an Endurance or Will attribute (e.g. an incorporeal being).

The actor's overland travel speed (leagues per watch) as a sohl.entity.modifier.ValueModifier. Seeded from the active moveProfile when it is enabled.

moveProfile: MovementProfile

The active movement profile, selected during initialize by this actor's SohlActorData.currentMoveMedium. A disabled MOVEMENT_MEDIUM.NONE profile when the actor has no matching profile (a non-mover).

The being's pull score, determining whether it can draw certain bow weapons.

strengthModifier: sohl.entity.modifier.ValueModifier

The being's strength modifier to encumbrance, as a sohl.entity.modifier.ValueModifier. Derived in evaluate from the active movement profile's strMod expression of the being's strength.

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 availableStrikeModes(): sohl.entity.strikemode.StrikeModeBase[]
  • The strike modes currently available to this being:

    • every combat technique's strike mode (intrinsic, always available), and
    • each weapon strike mode whose weapon is held in at least the mode's minParts limbs.
    • If a missile weapon, the draw must be less then or equal to the being's pull.

    Reads each strike mode's already-prepared data, so it should be read after item preparation. Returns an empty array when no mode is available.

    Returns sohl.entity.strikemode.StrikeModeBase[]

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

  • get document(): TData["parent"]
  • The owning document — the actor or item this logic is embedded in.

    Returns TData["parent"]

  • get item(): SohlItem
  • The owning SohlItem.

    Returns SohlItem

    If this logic is not embedded in an item.

  • get kind(): string
  • The owning document's kind (its actor or item type id).

    Returns string

  • get pallStress(): number
  • The being's accrued Pall Stress Levels (PSL) (#561) — the level of its single pall-subtype trauma (the Pall Cloud), or 0 when it carries none.

    Returns number

  • get reach(): number
  • The being's melee reach (feet): the greatest reach among its currently available melee strike modes.

    • Combat techniques are intrinsic and always available — every melee technique mode counts.
    • A weapon's melee mode counts only when the weapon is currently held in at least the mode's minParts limbs (a body part that canHoldItem).

    Returns 0 when no melee mode is available (e.g. an unarmed being with no combat techniques). Reads each strike mode's already-evaluated reach, so it should be read after item preparation.

    Returns number

  • get tokens(): SohlTokenDocument[]
  • This being's tokens on the world's active scene.

    • For a synthetic (token) actor, this is the single token the actor is embedded in, provided that token lives on the active scene.
    • For a world (linked) actor, this is every linked token on the active scene that represents this actor.

    Returns an empty array when there is no active scene or no matching token.

    Returns SohlTokenDocument[]

  • get typeLabel(): string
  • Localized type (and sub-type, when present) label for the owning document.

    Returns string

  • get uuid(): string
  • The owning document's UUID — the opaque identity token from the data port.

    Returns string

Methods

  • Accept an ally's Rally — the open Rally card's button runs this on the accepting player's own character. Self-gates: only a shaken (Withdrawing/Routed/Catatonic) character responds. A steady rally (CS) makes them Steady immediately; a reaction rally (MS) triggers their Reaction Test.

    Parameters

    Returns Promise<void>

    A promise that resolves once the rally is applied.

  • Advance (or, with a negative steps, improve) the being's shockState by steps severity levels from its current state, clamped to [NONE, DEAD]. A convenience over setShockState for effects that read the current state and move it (blood loss, an injury shock result, a shock re-test).

    Parameters

    • steps: number

      Levels to move (positive worsens, negative improves).

    Returns Promise<void>

    A promise that resolves once the shock state has been updated.

  • Record a permanent impairment (#554) on the body part containing locationShortcode, worsening its persisted permanentImpairment to at most magnitude (the worse — more negative — of the two). A no-op for a non-negative magnitude, an unknown location, or when it would not worsen the existing value. The whole parts array is rewritten (an element-by- index write corrupts the array — see the Runtime Contracts).

    Called when an eligible injury heals to level 0 (see the Injury rules — Permanent Impairment); the magnitude comes from sohl.entity.body.permanentImpairmentFor.

    Parameters

    • locationShortcode: string

      The healed injury's body-location shortcode.

    • magnitude: number

      The permanent impairment to apply (a non-positive number).

    Returns Promise<void>

    A promise that resolves once the impairment is persisted.

  • Intrinsic-action executor for contagionCheck (#1183) — the *Check half of contagion.

    A *Check offers, and does nothing else: it posts a card whose button invites this being's controller to make one contagionTest. Nothing is rolled and nothing is written, so it imposes nothing and carries no ownership gate — anyone may initiate a Contagion Check on anyone, which is the point: exposure is something the world does to a character, but catching it is the character's own roll to make.

    Parameters

    Returns Promise<void>

    A promise that resolves once the check card is posted.

  • Intrinsic-action executor for the Contagion Test (#1183) — the *Test half of contagion, and the roll that decides whether this being catches something they were exposed to.

    The dialog asks which affliction (a dropdown keyed by shortcode), a Situational Modifier and Success Level Modifier for the roll, and whether a contracted affliction is added to the character sheet — that checkbox defaulting from the recordTrauma world setting.

    The roll is a d100 test against Contagion Index × Endurance, and failing it means the affliction is caught (sohl.document.actor.logic.isContracted). How fast it takes hold depends on how badly the roll went: a critical failure halves the rolled onsetFormula (rounded down), a marginal failure uses it as-is, and 0 days means onset is immediate (sohl.document.actor.logic.onsetDaysFor).

    A contracted affliction is created with its contract date set to now and its incubation set to the rolled value — but only when the checkbox was ticked. Nothing ever offers to schedule another contagion test: exposure is not a recurring condition.

    Parameters

    Returns Promise<null | sohl.entity.result.SuccessTestResult>

    The success test result, or null when the being has no Endurance attribute or the dialog was dismissed.

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

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

  • Find an embedded item's logic by its shortcode and item kind.

    Type Parameters

    • K extends ItemKind

      The item kind, inferred from the type argument.

    Parameters

    • shortcode: string

      The item's system.shortcode.

    • type: K

      The ItemKind to match (e.g. ITEM_KIND.SKILL).

    Returns undefined | ItemLogicByKind[K]

    The matching item's logic typed for type, or undefined if no item matches.

    Both arguments are required: a shortcode is only unique within a given item kind, so matching the type as well prevents returning an unexpected item that happens to share the shortcode. The item kind also drives the return type — the concrete logic for that kind is resolved from ItemLogicByKind, so no cast is needed at the call site:

    const stealth = actor.logic.getItemLogic("stealth", ITEM_KIND.SKILL);
    // ^? SkillLogic | undefined
  • Find an embedded item's logic by its id.

    Parameters

    • id: string

      The item's id.

    Returns undefined | SohlItemLogic<any>

    The matching item's logic or undefined if no item matches.

  • Return the usable strike modes for this weapon.

    Parameters

    • options: {
          directAllowed: boolean;
          distanceToTarget: number;
          meleeAllowed: boolean;
          volleyAllowed: boolean;
      } = ...

      Filter criteria for the strike mode query.

      • directAllowed: boolean

        if true, direct strike modes are allowed, otherwise not.

      • distanceToTarget: number

        if specified, the distance from the weapon holder to the target, used to consider reach and/or range.

      • meleeAllowed: boolean

        if true, melee strike modes are allowed, otherwise not.

      • volleyAllowed: boolean

        if true, volley strike modes are allowed, otherwise not.

    Returns sohl.entity.strikemode.StrikeModeBase[]

    array of strike modes on this weapon that are currently usable that meet the criteria.

    This method returns the usable strike modes for a weapon, based on whether the item is currently readied, how many body parts are holding the weapon, and other conditions including heft, pull, and similar considerations.

  • Handle a trigger dispatched by the SoHL event queue. Override in subclasses to implement actor-specific trigger handling.

    Parameters

    • kind: string

      Subscription kind identifier

    • _context: SohlTriggerContext

      Trigger context (discriminated by context.name)

    • Optional_payload: Record<string, unknown>

      Optional context data attached when subscribing

    Returns Promise<void>

  • Each body-part role the being can still use but is impaired in, mapped to the worst (most negative) −5 (minor) / −10 (serious) indefinite-impairment penalty among the usable parts carrying that role (#568). A test whose governing skill or attribute lists any of these roles in its impairedByRoles takes that penalty on its effective mastery level.

    Unusable parts are excluded — a grievous injury forces an automatic Critical Failure (see unusableRoles) rather than a numeric penalty — so the two views never both cover the same part.

    Returns Map<string, number>

    Role → worst penalty (≤ 0); empty for an incorporeal being or one with no impaired parts.

  • Resolve the Injury Shock Test (#555) for a wound just taken, worsening the being's shockState accordingly.

    Intrinsic handler for the injury card's Shock Roll button — a specialization of the general shockTest, with the base Shock State Index computed from the wound. The card's scope carries the wound's precomputed shock contribution (shockIndex = body-location Shock Value + Injury Level, already including the glancing-blow point) and a shockBonus (the +10 glancing-blow roll bonus). The shared resolveShockRoll core rolls a headless Shock test (fatigue applies, the glancing bonus is added, and injury-impairment penalties do not); its result maps to a shock state, and the being is worsened to it (shock only ever worsens here — an improving Re-Test is #556). The state is applied directly — the player's click on the injury card's Shock Roll button was the consent — and the Re-Test reminder is then offered.

    Parameters

    Returns Promise<null | sohl.entity.result.SuccessTestResult>

    The Shock-test result, or null if the roll could not be run.

  • Set this actor's SohlActorData.currentMoveMedium — the active movement profile — to the medium carried in the action scope.

    Intrinsic-action executor for the makeDefaultMedium action.

    Parameters

    Returns Promise<void>

    Resolves once the actor update completes, or immediately when no medium was chosen.

    The Profile-tab star names the medium in scope and applies it directly. Invoked any other way — from the Actions menu, a macro, a script — there is no medium to apply, so the action offers the choice (the prefer-dialog rule) rather than returning silently as it once did (#1098). The prompt lists the no-movement medium plus every medium this actor authors a profile for, preselected at the current one. A caller that suppressed the dialog cannot be prompted, so it gets a notice instead of an unexplained no-op.

  • Offer to schedule (or, when it no longer applies, clear) the being's Shock Re-Test reminder for its current state (#569) — the being-level timing half of #556, routed through the shared offerSchedule consent step so nothing auto-arms (Prime Directive: offer, remind, perform).

    While in ordinary shock the reminder rides the state's cadence: an Incapacitated victim re-tests at the end of each combat turn (an event-driven turnEnd schedule), an Unconscious one ten minutes later (a time schedule). Any other state — recovered, merely Stunned, or already in a lasting Extended Shock / Coma (whose recovery is a Course Test, not a Re-Test) — clears the reminder. When due, the event queue posts an owner-gated [Perform] card; the Re-Test runs only on the controller's click.

    Parameters

    • context: OfferContext

      The action context; scope.schedule pre-answers the offer and skipDialog suppresses the prompt (scripted callers).

    Returns Promise<void>

    A promise that resolves once the reminder is armed or cleared.

  • GM re-edit of a settled opposed contest (#1082) — the edit pencil in the Opposed Action Result card's header, and the two-sided counterpart to the standard card's sohl.document.item.logic.SohlItemBaseLogic.resultEdit (#856).

    Re-opens the standard test dialog for each side in turn, pre-filled with that side's current situational and success-level modifiers, applies the new values, re-evaluates the contest on both frozen rolls (never a re-roll, no Fate cost), and reposts the Opposed Action Result card. The settled contest rides in context.scope.opposedTestResult — the whole result serialized into the pencil's data-scope and revived by the chat-card dispatcher — so the edit acts on that contest.

    The contest lives only in the chat log, never on a document, so an edit mutates nothing persistent: it posts a new, corrected card and leaves the original standing (see the action-card consent model). Dismissing either side's dialog cancels the whole edit — no re-evaluation, no repost.

    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.

    Parameters

    • context: sohl.entity.action.SohlActionContext<
          {
              opposedTestResult?: sohl.entity.result.OpposedTestResult;
              source?: { situationalModifier?: number; successLevelMod?: number };
              target?: { situationalModifier?: number; successLevelMod?: number };
          },
      >

      The action context; context.scope.opposedTestResult is the settled contest. When skipDialog is set, each side's new situationalModifier / successLevelMod are taken from context.scope.source / context.scope.target instead of the dialogs.

    Returns Promise<undefined | sohl.entity.result.OpposedTestResult>

    The re-evaluated contest, or undefined when refused (non-GM), missing a contest, or cancelled (a dialog dismissed).

  • Perform a Treatment Success Value test for an affliction (#1183) — the physician's half of the affliction treatment exchange, run from a Treatment Requested card's open @self button or by hand.

    Self-gates: only a Physician-skilled character answers. Rolls this physician's own Physician skill as a success-value test, and posts a result card whose owner-gated Accept button relays the earned Success Stars to the patient's affliction as its Course Bonus — nothing is applied until the patient presses it.

    Treatment for an affliction is mostly ineffectual by design: the bonus improves the odds on subsequent Course Tests, it does not cure anything.

    Parameters

    Returns Promise<undefined | { physicianName: string; successStars: number }>

    The earned Success Stars and physician name, or undefined if it aborts.

  • Perform a Blood Stoppage Test for a bleeding character (#547) — the physician's step of the interactive flow, run from a Request Blood Stoppage card's open @self button (or by hand). Self-gates: only a Physician-skilled character answers. Rolls this physician's own Physician skill (plus any +10 carried from a prior Marginal-Failure stoppage) and posts a Blood Stoppage Result card whose owner-gated Accept button relays the outcome back to the bleeding injury.

    Parameters

    • context: sohl.entity.action.SohlActionContext

      The action context; scope.injuryUuid targets the bleeding injury and scope.stoppageBonus carries the +10 next-test bonus.

    Returns Promise<undefined | { kind: string; physicianName: string }>

    The outcome kind and physician name, or undefined if it aborts.

  • The Perform Treatment Test action — this being (the physician) rolls their own Physician skill against a wound and posts the result. It is fully self-sufficient, so it is the same action however it is triggered:

    • From a wound's Treatment Requested card, the open button pre-fills scope.injuryUuid with skipDialog; the responder is the clicking player's own game.user.character.
    • From the Being's Actions tab (by hand), a dialog gathers the wound — either a pasted injury UUID (Foundry's "Copy Document UUID"), or a described severity/aspect for a GM-directed test.

    When a real wound is identified it posts a Treatment Result card whose owner-gated Accept button records the proposed Healing Rate on that wound (via sohl.document.item.logic.TraumaLogic.treatInjury) — the physician never touches the patient's wound; the patient's own click does. A GM-directed test with no target wound posts an informational result with no button (someone runs Treat Injury by hand).

    Self-gating: with no Physician skill it aborts with a notice and returns undefined, so an open request card stays live for a qualified physician.

    Parameters

    Returns Promise<undefined | { healingRate: number | "HEAL"; physicianName: string }>

    The proposed { healingRate, physicianName }, or undefined when the physician cannot perform it (no skill / unresolved / healed / cancel).

  • Resolve Injury — generate a wound on this being from a blow, the single entry point behind the intrinsic action, the combat cards' injury buttons, and the sheet's Add Injury. The action's scope seeds the parameters (a combat button forwards impact/aspect plus an aimed targetPart + spread; the manual paths start from defaults); unless skipDialog, a dialog lets a human confirm and tune them.

    The flow (see the Injury rules): resolve the hit location (an explicit bodyLocationCode, else derived from the target body part — a random VITAL part when unspecified — and the strike spread); subtract armor (an armorReduction applies only to a piercing aspect) to get the injury level and severity; judge bleeding on a separately-boosted impact (bleedImpactPenalty); and for a G5 edged wound at an amputable location, roll a Strength test whose result may sever the location (fatal if it is vital), make it bleed, or penalize the Shock Roll. It then records the Trauma (when autoAddInjury) with the given treatment modifier and posts the Resolve Injury card. Nothing is recorded for a no-injury result.

    Dispatched as a normal chat-card action through the shared sohl.document.chat.dispatchChatCardAction chokepoint (issue #572).

    Parameters

    Returns Promise<void>

  • Set the being's shockState to level, the single entry point for shock transitions. Clears every shock status effect and then applies only the one for level (none for NONE) — so transitions are clean in both directions and any stray multi-status situation is repaired. Only the statuses that actually change are toggled.

    Parameters

    • level: number

      The target shock-state level; clamped to [NONE, DEAD].

    Returns Promise<void>

    A promise that resolves once the statuses have been updated.

  • Resolve a Shock Re-Test (#556) for an Incapacitated or Unconscious being, attempting to shake off ordinary shock.

    Rolls the being's Shock skill headlessly at −20 (the being's fatigue penalty also applies; injury-impairment penalties do not) and applies the result (shockReTestOutcome): a critical success recovers from all shock, a marginal success improves to Stunned, and a failure drops the victim into Extended Shock (a shock-subtype trauma at Healing Rate 4/5) — or, for an Unconscious victim on a critical failure, a Coma (a coma-subtype trauma whose Healing Rate is 12 − Location Shock Value − Injury Level of the worst active wound). Both lasting-shock traumas then recover through their own Course Test (see sohl.document.item.logic.TraumaLogic.courseCheck).

    A no-op (returns null) unless the being is Incapacitated or Unconscious. The re-test is offered on the state's own cadence by offerShockReTest (end of the being's own turn for Incapacitated, ten minutes later for Unconscious): when due the event queue posts an owner-gated [Perform] card, and the re-test runs only on the controller's click — nothing auto-fires (Prime Directive: offer, remind, perform).

    Parameters

    Returns Promise<null | sohl.entity.result.SuccessTestResult>

    The Shock re-test result, or null when no re-test applies.

  • Roll a Shock skill test (#850) — the general shock primitive. Shock is not specific to injury: blood loss, fear, and other systemic or psychological forces all drive a shock test by supplying a base Shock State Index (SSI). The base SSI comes from context.scope (shockIndex/baseShockIndex) for a scripted cause, or is collected via a dialog when the action is run by hand.

    The Shock skill is rolled without the body-part impairment penalty (the being's fatigue penalty still applies); the result adjusts the SSI (CF +2 / MF +1 / MS 0 / CS −1), which maps to a shock state (shockStateFromIndex). A base SSI below 5 is No Shock and above 10 is immediate Dead, with no roll (shockRollNeeded). The being is then offered the resulting state (worsen-only — a fresh shock never improves an already-worse state; recovery is the shockReTest, #556), and, if it enters ordinary shock, offered the Re-Test reminder.

    Parameters

    • context: sohl.entity.action.SohlActionContext

      The action context; scope.shockIndex/baseShockIndex supplies the base SSI and scope.applyShockState pre-answers the set-state offer.

    Returns Promise<null | sohl.entity.result.SuccessTestResult>

    The Shock-test result, or null when no roll was made or the action was dismissed.

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

  • The set of body-part roles the being currently cannot use — the roles of every body part that is unusable (a grievous injury or a permanent-unusable flag). A test whose governing skill or attribute lists any of these roles in its impairedByRoles automatically Critically Fails (#568).

    Returns Set<string>

    The roles of every unusable body part (empty for an incorporeal being).