A lamp lights the people standing in it

The goods yard fight made the case: three agents inside the same pool of light shot at
-50% while the fourth, four metres away, shot at -10%.

A lamp now has a reach, per item rather than global, because a weapon lamp and a
vehicle searchlight are not the same offer — 4 m for cold light strips, 6 m for a
weapon lamp, 10 m for the issue field lamp, 30 m for a roof searchlight. Anyone inside
it is in the light and the card names whose lamp it is, so both halves of the rule
travel together: the pool is one position and the whole group is standing in it.

Measured from the tokens. The shot that started this — no lamp of her own, four metres
from somebody else's, in pitch dark — goes from 20% to 60%, while an agent
twenty-five metres out still eats the full -50%.

An UNKNOWN distance is explicitly not lit: Number(null) is 0, so an unmeasured
distance would otherwise have read as standing on top of the lamp and lit any scene
with no tokens placed. The dialog carries a tick instead, pre-set when the canvas could
measure it and the GM's answer when it could not.

Note: template.json is read when Foundry loads the system, so a deployed server needs
a restart and not just a pull before the migration can stamp the new field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
slaguru666
2026-08-11 09:49:30 +01:00
co-authored by Claude Opus 5
parent 5c640d65cf
commit 4bfa7110d0
114 changed files with 262 additions and 50 deletions
+33
View File
@@ -730,6 +730,39 @@ export const LIGHTING = {
/** A lamp never makes it daylight. This is the best it gets you. */
export const LIGHT_FLOOR = "dim";
/**
* HOW FAR A LAMP THROWS.
*
* A lamp used to light exactly one person: the one holding it. Played out, that put
* three agents standing inside the same pool of light shooting at −50% while the
* fourth, an arm's length away, shot at −10%. Nobody would run that at a table.
*
* So a lamp has a REACH, and anyone inside it is in the light — including the people
* it is showing to whatever is out there, which is the half of the rule that costs
* something. The reach is per item, because a weapon lamp and a roof searchlight are
* not the same offer.
*/
export const LAMP = { reachM: 10 };
export function lampReachFor(reachM) {
const r = Number(reachM);
return r > 0 ? r : LAMP.reachM;
}
/**
* Is this person inside that lamp's pool? Distance in metres, as everything else is.
*
* An UNKNOWN distance is not "in the light". `Number(null)` is 0, so a missing
* measurement would otherwise read as standing on top of the lamp and light the whole
* scene for free — which is precisely the failure this rule exists to correct.
*/
export function lampCovers(distanceM, reachM) {
if (distanceM === null || distanceM === undefined || distanceM === "") return false;
const d = Number(distanceM);
if (!Number.isFinite(d) || d < 0) return false;
return d <= lampReachFor(reachM);
}
export function lightingFor(id) {
return LIGHTING[id] || LIGHTING.lit;
}