Stealth, Vision & Noise¶
Source: PROJECT_CONTEXT.md §3, §8.2, §8.3, §8.6;
docs/design/stealth-visibility-analysis.md;config.jsfog,stealth,noise;game.jsisVisibleToPlayer/perceive/isSensed/drawSilhouette/makeNoise/alertPack. Status: ✅ Implemented (Phases A+B of the stealth-visibility plan shipped; Phases C–D pending).
What it is¶
The perception layer that makes Shadow Raiders a stealth game: you see only what is near and in line of sight, you hear threats a bit further out as blips, and enemies see you only through directional vision cones. The skill is reading the field, skirting cones, and striking when unseen — design pillars #1 (hide-and-kill) and #2 (route over reflexes).
How it works¶
- Fog of war + player vision sphere. A visibility polygon (DDA raycast,
fog.raysrays) clipped to the player sight radius (fog.radius, 300px) and walls. Terrain is always visible; the fog only gates actors and loot (isVisibleToPlayer,inVision). An enemy is "lit" (drawn in full) when it is within sight radius, line-of-sight is clear (a closed vault gate blocks LOS), and it isn't idle-in-a-bush past thestealth.bushSightCutoff(70px) cutoff. - Per-enemy vision cones + LOS. Each creature perceives a target only if it is inside its
visRsight radius, inside its facing conevisA(half-angle), with clear LOS, and the player isn't in dodge i-frames (perceive). A bush shrinks the effective detect radius (stealth.bushDetectMult, ×0.30) while the enemy is unaware. Cover/walls break LOS, so enemies hide behind cover and you route around their gaze. - Attack requires the target in vision range. A creature can only attack a target inside its vision range (PROJECT_CONTEXT §3) — perception gates engagement.
- Echo-sense blips. Unlit creatures within the larger echo-sense range (
fog.senseRadius, 660px, > the 300px sight) and along a clear line of sight render as pulsing sonar blips (isSensed→drawSilhouette): position + threat state, no health bar, identity only up close (≤360px). Sense is LOS-gated (walls and the closed vault gate hide their occupants — sealed rooms keep their mystery); around-corner awareness is carried by the noise system instead. Sensed blips show no precise cone — the cone is a reward for actually seeing the enemy. Rival raiders are sensed only when loud (firing or cracking a chest); a quiet rival stalks the fog unseen. - Gradual detection (the "back off in time" window). Inside a creature's cone,
alertfills overstealth.detectTime(0.9s mid-range) rather than snapping to spotted — faster point-blank / in the open, slower at range / in a bush (detectProxMin/detectProxMax,bushDetectFill). Thresholds aroundalertSpotted(0.5): below = unnoticed; 0.5–1 =suspect(turns toward you, creeps in, holds fire); ==1 = fully spotted (locks on, opens fire, and only now rallies the pack viaalertPack). Breaking LOS / leaving the cone mid-fill decaysalert(combat.mobAlertDecay) and drops suspect → patrol, so you can retreat. (Mobs only; rival raiders keep instant 360° detection.) - Detectability stat. The Stalker set lowers detectability by −30% (
setBonusesstealth *= 0.7) — you can sit closer, longer, before a cone fills. Dodge i-frames also grant a brief un-spottable window (stealth.invulnWindow, 0.34s). - Noise propagation. Loud actions emit a sound radius that draws/recruits enemies. Shots, chest-cracking, and deaths each have a distinct radius (
makeNoise); idle mobs near the sound switch toinvestigate(walk to the point and scan). The FTUE "Heard" stage teaches this: cracking a chest near an unaware sentry pulls it in (huntOnNoise). Backstabs are near-silent (noise.backstab, 110px — only a point-blank witness reacts). Re-ping cooldowns (noise.gunCd,chestCd, …) stop a continuous action from spamming recruits, andnoise.recruitCap(2) caps how many creatures a single pulse pulls in (anti-swarm).
Tunables¶
fog
| Key | Default | Meaning |
|---|---|---|
| fog.radius | 300 | player sight / fog reveal radius (px) |
| fog.senseRadius | 660 | echo-sense blip range (px) — beyond sight |
| fog.rays | 480 | visibility-polygon ray count |
| fog.litHold | 0.2 | keep an enemy drawn this long (s) after LOS drops |
stealth
| Key | Default | Meaning |
|---|---|---|
| stealth.detectTime | 0.9 | s of mid-range cone exposure to fully spot you |
| stealth.detectProxMin / detectProxMax | 0.5 / 1.8 | detection fill × at far edge / point-blank |
| stealth.bushDetectMult | 0.30 | bush shrinks an unaware enemy's detect radius to this |
| stealth.bushDetectFill | 0.5 | detection fills at this × the open rate while concealed |
| stealth.bushBreakDecay | 0.9 | alert/lock decay rate once LOS lost AND you're in a bush |
| stealth.alertSpotted | 0.5 | suspicion line; above this a bush stops concealing |
| stealth.silenceWindow | 3 | s after a backstab opener a follow-up kill still stays quiet |
| stealth.raiderVsMob | 0.62 | rivals are harder for creatures to spot (×) |
| stealth.bushSightCutoff | 70 | can't see an idle bushed enemy past this (px) |
| stealth.invulnWindow | 0.34 | un-spottable during dodge i-frames (s) |
| stealth.bushSpeedPerk | 1.1 | "+10% move in bushes" × |
noise (sound radii in px / cooldowns in s)
| Key | Default | Meaning |
|---|---|---|
| noise.gunshot | 340 | gunshot radius |
| noise.chestCrack / chestPop | 300 / 500 | chest cracking / open-pop radius |
| noise.deathMob / deathRaider | 380 / 460 | creature / raider-boss death radius |
| noise.backstab | 110 | near-silent backstab radius |
| noise.meleeClash | 200 | melee clash radius |
| noise.recruitCap | 2 | max creatures one noise pulls in (nearest-first) |
| noise.gunCd / chestCd | 2 / 2 | re-ping cooldown per source |
| noise.hunterStep / hunterStepCd | 130 / 0.5 | unseen-hunter footstep ping radius / interval |
| noise.hearRadius | 1.5 | player hearing sensitivity × (sound radius) |
Design intent¶
Stealth is pillar #1 (hide-and-kill) and #2 (route over reflexes): the headline skill is where you go, not aim. Limited vision creates tension; echo-sense + gradual detection turn that fog from blindness into a readable puzzle — you perceive a threat a screen ahead, feel the cone fill, and decide to slip past, back off, or commit to a silent kill. Noise is the first information layer (Arc Raiders' "hear them before you see them") and a systemic cost on loud actions, so quiet play is rewarded.
Open questions / deltas¶
- Phase C/D pending (
docs/design/stealth-visibility-analysis.md§7): tapping a sensed (unlit) enemy to queue an approach, own-noise legibility ring, broader stealth toolkit (wait/peek, distraction, light/shadow), and a legitimate tactical-map intel layer (sensed blips + last-seen ghosts) replacing thedevRevealdebug cheat. Sensing is currently in-world only. - Cone tinting deferred: the amber-while-filling cone tint (R3) was left to a later polish pass; the per-enemy fill arc + player vignette carry the read for now.
- Rivals have no cone: rival raiders keep instant 360° detection (no cone to skirt) — scoped out of the gradual-detection model deliberately.
- Unity port (§9): the perception simulation, noise economy, and silent-backstab payoff should carry forward; decide whether Unity keeps the 2.5D tilt that the fog/LOS raycast assumes.
- Per-enemy sense range (e.g. a loud Rocketeer sensed farther than a Spider) is noted as a future tuning lever;
fog.senseRadiusis currently global.