Scripts — Foundry macros and Script Actions — and modules reach into Song of Heroic Lands through two distinct surfaces. This page explains how they relate and when to use each; what each surface offers is documented on the class it points at.
- The document surface —
document.logic, a SohlLogic — for working with one specific actor or item. - The
sohlsurface — the SohlSystem singleton — for system-wide services and helpers.
The document surface — document.logic
Every SoHL document exposes its Foundry-free domain object as document.logic
(a SohlLogic), holding the document’s computed state and its actions:
const actor = game.actors.getName("Grymm");
const being = actor.logic; // the actor's logic
const sword = actor.items.getName("Broadsword").logic; // an item's logic
The actions it holds include both per-kind ones and the universal actions every
document shares — e.g. every item’s logic carries Output Description to Chat
(outputDescription), which posts that item’s description to the chat log. See
Macros and Actions.
For typed fields, read document.logic.data — the document’s *Data interface,
so editors autocomplete and the API reference links straight to it. SohlLogic
documents the layer and its lifecycle;
Architecture Overview covers how
document.system (the DataModel) and document.logic (the Logic) relate.
Use this surface whenever the task is “read or act on this particular thing.”
The sohl surface — the global singleton
System-wide helpers and services that aren’t tied to any one document live on the
global sohl object — the SohlSystem singleton, created during Foundry’s
init hook (so it’s available from init onward, not earlier):
sohl.utils.romanize(4); // "IV" — a system helper
sohl.log.info("hello from a macro"); // the system logger
What it offers — services, helpers, constants, and direct entry points into the logic layer — is enumerated and kept current on SohlSystem; treat that class as the source of truth rather than any list here.
The namespace tree — addressing every class
Every SoHL class is also reachable through sohl by a source-mirroring path:
sohl.document.effect.foundry.SohlActiveEffect,
sohl.entity.modifier.ValueModifier, sohl.apps.foundry.CalendarSettingsMenu, and so
on — the path equals the file’s location under src/, so a reference is
unambiguous and easy to locate. The top-level namespaces are sohl.document,
sohl.core, sohl.apps, and sohl.entity.
sohl.entity is special: it is both the override-aware construction registry
(the flat, PascalCase getters sohl.entity.ValueModifier and
sohl.entity.register(...)) and a namespace (sohl.entity.modifier.ValueModifier).
The two occupy distinct property names, so both work — but construct or override
through the flat registry: those getters honor a register() override, whereas a
namespace path is for reference/addressing and always resolves to the original
class.
A note on CONFIG
SoHL registers its document, modifier, and result classes into Foundry’s global
CONFIG at init (mirrored at sohl.CONFIG) — registration/wiring used by the system
and by modules adding new types, not a day-to-day scripting surface. For extending
it, see Extension Points.
Changing state
Treat both surfaces as read-first. To change state, prefer a document’s own actions and the system’s mutation paths over writing fields directly: an actor mutates only itself, and cross-actor effects route through a target-addressed acknowledgement flow — see actor state sovereignty.
See also
- Macros and Actions — scripting against these surfaces.
- Writing Modules — extending the system in a package.
- Architecture Overview — the three-layer model.
- API Reference — every exported symbol.