Builds a single body zone from its persisted data and constructs the child parts handed to it by BodyStructure.
Persisted zone data.
Construction options
Construction options for a BodyZone instance.
Zero-based index of this zone within the flat zones array.
Every location in the body, each paired with its flat locations index.
The Logic that owns the entity. Required (the constructor throws without it) and transient — held in memory, never written to JSON.
This zone's parts, each paired with its flat parts index.
The first zone number allocated to this zone.
Owning body structure (supplies actor and body-owner logic).
ReadonlyindexZero-based index of this zone within the flat structure.zones array.
ReadonlynameDisplay name of this zone (falls back to the shortcode).
ReadonlypartsBody parts belonging to this zone, in persisted order.
ReadonlyshortcodeUnique zone identifier within the body structure (e.g. "arms").
ReadonlystructureBack-reference to the owning BodyStructure.
ReadonlyzoneThe contiguous run of zone numbers this zone answers to, e.g.
[4, 5, 6, 7, 8]. Sized by the persisted BodyZone.Data.probWeight
and allocated at construction. Empty when the zone has no weight (it can
never be rolled).
The weight is deliberately not wrapped in a ValueModifier: a zone's
numbers are positional — changing one zone's weight shifts every later
zone's run — so the allocation has to come from the persisted value alone.
An active effect that could move the ranges mid-cycle would desync every
zone above it.
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.
Every hit location beneath this zone, across all of its parts.
The dot-notation path prefix for Foundry update() calls targeting this
zone's persisted fields, e.g. "system.body.structure.zones.1".
Build an update() payload that appends a new part to this zone,
stamping it with this zone's shortcode. The part lands at the end of the
flat parts array — and so at the end of this zone's parts.
A complete-array update() payload appending the part.
Deep-copy this entity, re-parenting the copy under parent with no other
changes. Shorthand for clone({}, { parent }).
The Logic to own the cloned entity.
The cloned entity.
Deep-copy this entity, optionally overriding fields and clone options.
Field overrides applied to the clone.
Clone options (e.g. a new parent).
The cloned entity.
Find a part by its position within this zone, or undefined if out of
range. Note this is the zone-relative position, not the part's
BodyPart.index into the flat parts array.
Zero-based position of the part within this zone.
The part at that position, or undefined if out of range.
Select a random part within this zone, weighted by each part's BodyPart.probWeight.
The random source; defaults to the shared sohl.random singleton. Inject a seeded generator to make selection reproducible.
A randomly selected part.
Build an update() payload that removes a part of this zone by
shortcode, cascading to that part's hit locations.
Shortcode of the part to remove.
A complete-array update() payload with the part and its
locations removed, or {} when this zone has no such part.
Serialize this instance to a plain object suitable for JSON serialization.
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.
A broad anatomical region grouping one or more body parts — e.g. "Head" (holding the head part), "Arms" (holding the left and right arms), "Legs".
Zones are the first stage of hit determination. Each zone owns a contiguous run of zone numbers sized by its BodyZone.Data.probWeight, allocated in persisted order across the whole body — so a body whose zones weigh 3 / 5 / 2 hands out
1–3,4–8,9–10. A single roll against sohl.entity.body.BodyStructure.maxZoneNumber therefore selects a zone; the part and location draws follow inside it.Persistence: zones are stored in the flat
body.structure.zonesarray. A zone does not nest its parts — parts declare their owner via sohl.entity.body.BodyPart.Data.bodyZoneCode, and sohl.entity.body.BodyStructure assembles the hierarchy at construction.Lifecycle: Rebuilt from persisted schema data on every preparation cycle. May be mutated during the lifecycle (e.g., modifiers applied by active effects), but mutations are not persisted — they are recomputed on the next cycle.