Light detection
Light changes how far away enemies notice the player.
On this page
Light detection ties how far enemies notice the player to light. In Silent Hill the flashlight switches which environment preset’s flags are live once it is half lit1, and enemy code reads those flags. In a dark map a larval stalker targets Harry from 17 units with the flashlight on and from 2.5 with it off2. A walking creeper’s range also depends on which way it faces: with the flashlight on it runs from 12 units behind to 20 ahead, and in the dark from 0.8 to 1.23.
- Own lightSilent Hill (1999). In dark maps the flashlight lengthens the range at which enemies notice Harry, from 2.5 to 17 units for a larval stalker.1,2,3
Games
| Game | Year | How | Note |
|---|---|---|---|
| Silent Hill | 1999 | Own light | in dark maps the flashlight lengthens the range at which enemies notice Harry, from 2.5 to 17 units for a larval stalker |
Every claim about this mechanic, by game (3)
References
- ^ abIn the US build, the flashlight swaps which environment preset's flags are live, and enemy code reads those flags.
Gfx_EffectsUpdatemovesflashlightIntensitytoward 1 while the flashlight is on and toward 0 while it's off, by 4 ×g_DeltaTimeper update, andfunc_8003F838copies the live flags (field_154) from the flashlight-off preset (field_1C[0]) while the intensity is under 0.5 and from the flashlight-on preset (field_1C[1]) from 0.5 up.WorldEnv_MapPresetSetgives a map whose header's unnamedfield_16is 2 the preset pair 6 and 3, or 7 and 4 in low-visibility interiors (off, then on). Presets 6 and 7 setSpecialEnvEventFlags_DarkEnvironmentandSpecialEnvEventFlags_FlashlightAllowed; presets 3 and 4 setFlashlightAllowedwithoutDarkEnvironment. A map whosefield_16is 1 gets preset 1 or 2 for both, which set neither flag, andGfx_MapEnvStepUpdatemarks the flashlight unavailable whenever the two presets are the same. So in those dark maps, the flags read as dark with the flashlight off and as lit with it on; map events can load other pairs.Codeshdecompilations/silent-hill-decompHow we know
393 if (g_SysWork.field_2388.isFlashlightOn) ⋮ 395 g_SysWork.field_2388.flashlightIntensity += Q12_MULT_FLOAT_PRECISE(g_DeltaTime, 4.0f); ⋮ 397 else ⋮ 399 g_SysWork.field_2388.flashlightIntensity -= Q12_MULT_FLOAT_PRECISE(g_DeltaTime, 4.0f); ⋮ 402 g_SysWork.field_2388.flashlightIntensity = CLAMP(g_SysWork.field_2388.flashlightIntensity, Q12(0.0f), Q12(1.0f));
src/bodyprog/world/world_effects.c lines 393–403 at 405a621 · upstream ↗ 412 if (ptr->primitiveType == PrimitiveType_None) ⋮ 414 ptr->field_1C[0] = ptr->field_84[0]; 415 ptr->field_1C[1] = ptr->field_84[1];
src/bodyprog/world/world_effects.c lines 412–415 at 405a621 · upstream ↗ 430 func_8003F838(&ptr->field_154, &ptr->field_1C[0], &ptr->field_1C[1], ptr->flashlightIntensity);src/bodyprog/world/world_effects.c line 430 at 405a621 · upstream ↗ 636static void func_8003F838(s_StructUnk3* target, s_StructUnk3* envSettings0, s_StructUnk3* envSettings1, q19_12 weight) // 0x8003F838 ⋮ 649 // Copy enviroment flags. 650 if (weight < Q12(0.5f)) ⋮ 652 target->effectsInfo.flags.field_00[0] = envSettings0->effectsInfo.flags.field_00[0]; ⋮ 654 else ⋮ 656 target->effectsInfo.flags.field_00[0] = envSettings1->effectsInfo.flags.field_00[0];
src/bodyprog/world/world_effects.c lines 636–657 at 405a621 · upstream ↗ 319 if (preset0 == preset1) ⋮ 321 g_SysWork.field_2388.isFlashlightUnavailable = true; ⋮ 336 Gfx_FogParametersSet(&g_SysWork.field_2388.field_84[0], preset0); 337 Gfx_FogParametersSet(&g_SysWork.field_2388.field_84[1], preset1);
src/bodyprog/world/world_effects.c lines 316–337 at 405a621 · upstream ↗ 22static s_MapEnvPresetIdxs D_800A9F80 = { 1, 1 }; 23static s_MapEnvPresetIdxs D_800A9F84 = { 2, 2 }; 24static s_MapEnvPresetIdxs D_800A9F88 = { 6, 3 }; 25static s_MapEnvPresetIdxs D_800A9F8C = { 7, 4 };
src/bodyprog/world/world_effects.c lines 22–25 at 405a621 · upstream ↗ 210 switch (mapHdr->field_16) ⋮ 212 case 1: 213 if (hasActiveChunk) ⋮ 215 presetIdxPtr = &D_800A9F84; ⋮ 217 else ⋮ 219 presetIdxPtr = &D_800A9F80; ⋮ 221 break; ⋮ 223 case 2: 224 if (hasActiveChunk) ⋮ 226 presetIdxPtr = &D_800A9F8C; ⋮ 228 else ⋮ 230 presetIdxPtr = &D_800A9F88; ⋮
src/bodyprog/world/world_effects.c lines 210–232 at 405a621 · upstream ↗ 5s_MapEffectsInfo MAP_EFFECTS_INFOS[21] = { ⋮ 7 .flags = { .field_00 = { SpecialEnvEventFlags_None, 0xFF, UnkGfxEnum_0, 0 }}, ⋮ 23 .flags = { .field_00 = { SpecialEnvEventFlags_None, 0xFF, UnkGfxEnum_0, 0 }}, ⋮ 39 .flags = { .field_00 = { SpecialEnvEventFlags_None, 0xFF, UnkGfxEnum_2, 0 }}, ⋮ 55 .flags = { .field_00 = { (SpecialEnvEventFlags_FlashlightAllowed | SpecialEnvEventFlags_EnableBrightness | SpecialEnvEventFlags_EnableLensflare), 0xFF, UnkGfxEnum_1, 0 }}, ⋮ 71 .flags = { .field_00 = { (SpecialEnvEventFlags_FlashlightAllowed | SpecialEnvEventFlags_EnableBrightness | SpecialEnvEventFlags_EnableLensflare), 0xFF, UnkGfxEnum_1, 0 }}, ⋮ 87 .flags = { .field_00 = { (SpecialEnvEventFlags_FlashlightAllowed | SpecialEnvEventFlags_EnableBrightness | SpecialEnvEventFlags_EnableLensflare), 0xFF, UnkGfxEnum_1, 0 }}, ⋮ 103 .flags = { .field_00 = { (SpecialEnvEventFlags_DarkEnvironment | SpecialEnvEventFlags_FlashlightAllowed | SpecialEnvEventFlags_EnableBrightness), 0xFF, UnkGfxEnum_0, 0 }}, ⋮ 119 .flags = { .field_00 = { (SpecialEnvEventFlags_DarkEnvironment | SpecialEnvEventFlags_FlashlightAllowed | SpecialEnvEventFlags_EnableBrightness), 0xFF, UnkGfxEnum_0, 0 }},
src/bodyprog/sys/map_info.c lines 5–119 at 405a621 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abIn the US build,
LarvalStalker_ControlUpdatesets the larval stalker's target position to Harry's whenever his distance on the ground plane is under 2 × base + step, with base and step picked by the live light flags (silent-hill/flashlight-presets): 2.5 and 8 when neitherDarkEnvironmentnorFlashlightAllowedis set, 3.5 and 10 when onlyFlashlightAllowedis, and 0.5 and 1.5 otherwise. Computed, that's 13 world units in fog presets; in the dark map presets, 17 units with the flashlight on and 2.5 with it off. This check reads no facing and no line of sight.Codeshdecompilations/silent-hill-decompHow we know
84void LarvalStalker_ControlUpdate(s_SubCharacter* larvalStalker)src/maps/characters/larval_stalker.c line 84 at 405a621 · upstream ↗ 111 idxInfo = g_SysWork.field_2388.field_154.effectsInfo.flags.field_0 & (SpecialEnvEventFlags_DarkEnvironment | SpecialEnvEventFlags_FlashlightAllowed); 112 if (idxInfo == 0) ⋮ 114 baseDistMax = Q12(2.5f); 115 distStep = Q12(8.0f); ⋮ 117 else if (idxInfo == 2) ⋮ 119 baseDistMax = Q12(3.5f); 120 distStep = Q12(10.0f); ⋮ 122 else ⋮ 124 baseDistMax = Q12(0.5f); 125 distStep = Q12(1.5f); ⋮ 128 if (Math_Vector2MagCalcSafeQ6(g_SysWork.playerWork.player.position.vx - larvalStalker->position.vx, 129 g_SysWork.playerWork.player.position.vz - larvalStalker->position.vz) < ((baseDistMax * 2) + distStep)) ⋮
src/maps/characters/larval_stalker.c lines 111–133 at 405a621 · upstream ↗ 298 SpecialEnvEventFlags_DarkEnvironment = 1 << 0, // Double check. 299 SpecialEnvEventFlags_FlashlightAllowed = 1 << 1,
include/game.h lines 298–299 at 405a621 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abIn the US build, a walking creeper (
Creeper_ControlWalkForward) retargets Harry's position whenfunc_8006FD90passes, with arguments picked by the live light flags (silent-hill/flashlight-presets).func_8006FD90scales a range by how directly the creeper faces Harry, f = 1 − |angle off its heading|/180° (1 straight ahead, 0 behind), raised to the power count + 1; it fails beyond base + step × that, and otherwise a ray test toward Harry (Ray_CharaTraceQuery) decides. With onlyFlashlightAllowedset, the creeper calls it with count 0, base 12, and step 8: 12 units behind to 20 ahead. WithFlashlightAllowedandDarkEnvironment, count 2, base 0.8, step 0.4: 0.8 to 1.2 units. With neither flag, count 1, base 4, step 12: 4 to 16 units (all computed). A separate check (func_80070360with 0.5) or an alert flag also retargets it.Codeshdecompilations/silent-hill-decompHow we know
372void Creeper_ControlWalkForward(s_SubCharacter* creeper) ⋮ 384 if (((g_SysWork.field_2388.field_154.effectsInfo.flags.field_0 & (SpecialEnvEventFlags_DarkEnvironment | SpecialEnvEventFlags_FlashlightAllowed)) == SpecialEnvEventFlags_FlashlightAllowed && func_8006FD90(creeper, 0, Q12(12.0f), Q12(8.0f))) || 385 ((g_SysWork.field_2388.field_154.effectsInfo.flags.field_00[0] & SpecialEnvEventFlags_FlashlightAllowed) && 386 (g_SysWork.field_2388.field_154.effectsInfo.flags.field_00[0] & SpecialEnvEventFlags_DarkEnvironment) && func_8006FD90(creeper, 2, Q12(0.8f), Q12(0.4f))) || 387 (!(g_SysWork.field_2388.field_154.effectsInfo.flags.field_0 & (SpecialEnvEventFlags_DarkEnvironment | SpecialEnvEventFlags_FlashlightAllowed)) && func_8006FD90(creeper, 1, Q12(4.0f), Q12(12.0f))) || 388 (func_80070360(creeper, 0, Q12(0.5f)) || creeperProps.flags & CreeperFlag_Alerted)) ⋮ 390 creeperProps.targetPositionX = playerChara.position.vx; 391 creeperProps.targetPositionZ = playerChara.position.vz;
src/maps/characters/creeper.c lines 372–392 at 405a621 · upstream ↗ 137bool func_8006FD90(s_SubCharacter* chara, s32 count, q19_12 baseDistMax, q19_12 distStep) // 0x8006FD90 ⋮ 149 g_SysWork.playerWork.player.position.vz - chara->position.vz) - ⋮ 152 distMult = (Math_AngleNormalizeSigned(ratan2(g_SysWork.playerWork.player.position.vx - chara->position.vx, 153 g_SysWork.playerWork.player.position.vz - chara->position.vz) - 154 chara->rotation.vy) * 2) + Q12_ANGLE(360.0f); ⋮ 158 distMult = (Q12_ANGLE(180.0f) - Math_AngleNormalizeSigned((ratan2(g_SysWork.playerWork.player.position.vx - chara->position.vx, 159 g_SysWork.playerWork.player.position.vz - chara->position.vz) - 160 chara->rotation.vy))) * 2; ⋮ 163 for (i = distMult; count > 0; count--) ⋮ 165 distMult = Q12_MULT_PRECISE(distMult, i); ⋮ 168 dist = Math_Vector2MagCalcSafeQ6(g_SysWork.playerWork.player.position.vx - chara->position.vx, 169 g_SysWork.playerWork.player.position.vz - chara->position.vz); ⋮
src/bodyprog/collision/los.c lines 137–171 at 405a621 · upstream ↗ 173 return false; ⋮ 195 return !Ray_CharaTraceQuery(&sp10, &pos, &offset, chara) || sp20.vx != Q12(0.0f);
src/bodyprog/collision/los.c lines 172–195 at 405a621 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
Sources
Code
- shdecompilations contributors. 2026. “silent-hill-decomp: a matching decompilation of Silent Hill for the PlayStation (shdecompilations/silent-hill-decomp).” Commit 405a621. shdecompilations/silent-hill-decomp @ 405a621 · licence, all uses