How It Works
Eligibility
Section titled “Eligibility”A token is processed only if all of the following are true:
- The actor type is
characterornpc(other types are ignored). - The actor has current HP greater than 0 (downed and dead tokens have effects cleared).
- The actor carries the
flags.tokenlightcondition.initializedflag. The first time the module sees a fresh token, it sets this flag and triggers an initial calculation on the next pass.
All other tokens (vehicles, hazards, group actors, dead NPCs) are skipped entirely.
What triggers a recalculation
Section titled “What triggers a recalculation”The GM client listens to a set of canvas hooks. Each trigger queues either a single-token or all-tokens recalculation:
| Hook | Scope | Trigger |
|---|---|---|
createToken | Single | Token dropped on the scene |
updateToken | Single | hidden flag toggled |
updateToken | All tokens | Any of light.bright, light.dim, light.luminosity, light.angle, light.rotation changed on a token |
moveToken | Single | Token movement; uses the resolved end-of-waypoint position |
createAmbientLight / updateAmbientLight / deleteAmbientLight | All tokens | Ambient light placed, edited, or removed |
updateScene | All tokens | environment.darknessLevel or environment.globalLight changed on the active scene |
Player clients do not run calculations. They receive the resulting actor flag updates and render the HUD indicator from flags.tokenlightcondition.lightLevel.
Debouncing
Section titled “Debouncing”Recalculations honour the Delay Calculations setting. At zero delay, calculations fire immediately. Above zero, each trigger restarts a timer and a burst of events resolves in a single calculation.
Single-token and all-tokens timers are independent. A second all-tokens refresh started while the first is still running is skipped, not queued.
Effect queue
Section titled “Effect queue”Once a token’s new light level differs from its stored flags.tokenlightcondition.lightLevel, the change is added to an in-memory queue keyed by token id. The queue:
- Schedules itself on the next animation frame via
requestAnimationFrame. - Discards stale entries.
- Processes each token in turn: clear existing module effects, apply the new effect if the level is
darkordim, then write the newlightLevelflag.
This batching prevents the createActiveEffect / deleteActiveEffect hooks fired by Foundry from re-entering the calculation pipeline and causing infinite loops.
Per-token vs. all-tokens
Section titled “Per-token vs. all-tokens”| Trigger source | Behaviour |
|---|---|
| Token movement or hidden toggle | Recalculate that one token |
| Token light property change | Recalculate every eligible token |
| Ambient light or scene environment change | Recalculate every eligible token |
Disabling the system
Section titled “Disabling the system”Setting Add Token Effects off or flipping the toolbar button off both stop effect application, but they differ in how cleanup happens. See Active Effects and Scene Control Toggle.