# HANDOFF REPORT: World Canvas Visual Fog Overlay & Entity Suppression

**From**: Explorer M4 2 (`explorer_m4_2`)  
**To**: Parent Orchestrator (`1cc48fc5-ce57-4f48-8964-24cab4bfcacc`) / Worker M4  
**Date**: 2026-10-01T21:45:00Z  
**Type**: Hard Handoff (Investigation Complete)

---

## 1. Observation

1. **`client/webapp/js/ui/war_fog_renderer.js` (Current State)**:
   - File length: 287 lines.
   - Lines 6–124 define `renderProceduralMap(ctx, state)` for a legacy 480x480 modal simulator (`modal-war-fog`), not for the 2.5D isometric world canvas.
   - It is only imported by `war_fog.js` at line 6 (`import { renderProceduralMap } from './war_fog_renderer.js';`).
   - Line count constraint in dispatch requires `war_fog_renderer.js` to stay $\le 250$ lines.

2. **`client/webapp/js/engine/world_renderer.js` (Current State)**:
   - File length: 462 lines (near Hard Cap of 500 lines).
   - Lines 34–83 iterate over `groundDrops.forEach(drop => ...)` and render loot beams, text label boxes, and currency icons without checking any Fog of War visibility.
   - Lines 372–450 render the Wilderness Return Waypoint safe zone indicator and beacon at `(wp.wx, wp.wy)` unconditionally.
   - Lines 452–461 invoke `bgc.render(ctx, camObj)` for the Boss Gate seal rune.
   - No Fog of War overlay is currently rendered over the procedural tile map.

3. **`client/webapp/js/engine/entity_renderer.js` (Current State)**:
   - File length: 430 lines (Hard Cap 500 lines).
   - Lines 65–83 iterate over `getActiveMonsters()` and push live monsters to `renderEntities` unconditionally without checking if the monster tile is in `VISIBLE` (2) or fogged/unexplored (0, 1).
   - Lines 85–94 push `getNpcsInCurrentZone()` into `renderEntities` unconditionally.
   - Lines 54–62 push `mapProps` into `renderEntities` unconditionally.

4. **`client/webapp/js/engine/monster_system.js` (Current State)**:
   - File length: 485 lines (Hard Cap 500 lines).
   - Lines 102–115 define `getBestCombatTarget(originWx, originWy, maxRange = 7.5)`, which acquires the closest hostile monster purely by Euclidean distance, ignoring fog state.

5. **`client/webapp/js/engine/canvas_renderer.js` (Render Pipeline Order)**:
   - Lines 306–316 execute:
     1. `renderWorldEnvironment(ctx, now, renderDt)`
     2. `renderTelegraphs(ctx)`
     3. `renderVfxProjectiles(ctx, renderDt)`
     4. `renderEntities(ctx, now, renderDt)`
     5. `renderWeaponSwing(ctx, player)`
     6. `renderVfxParticlesAndOverlay(ctx, renderDt, now)`
     7. `renderDamageNumbers(ctx)`

6. **Test Environment Baseline**:
   - `python -m pytest tests/unit/test_war_fog_and_procedural_map.py` passes 20/20 tests.
   - `python -m pytest tests/e2e/test_poe2_map_system_e2e.py` passes 81/81 tests.
   - Node.js version: v24.14.0.

---

## 2. Logic Chain

1. **Need for Dedicated World Canvas Fog Renderer**:
   From Observation 1 and 2, `world_renderer.js` is already at 462 lines and cannot accommodate the full isometric fog rendering logic without violating the 500-line hard cap. Therefore, `client/webapp/js/ui/war_fog_renderer.js` must be refactored into the authoritative world canvas fog renderer, exposing `WarFogRenderer.render(ctx, camera, viewport)` and `isEntityVisible(wx, wy)`.
   Keeping a 25-line backward-compatible `renderProceduralMap` export ensures no breaking changes for modal simulators.

2. **Zero-Heap 2-Pass Batch Draw Architecture**:
   From Observation 5, isometric rendering runs at 120 FPS. Creating individual paths or allocating point objects per tile creates massive heap churn and GC pauses on mobile devices.
   By pre-calculating the tile frustum box (`minTx`, `maxTx`, `minTy`, `maxTy`) with a 2-tile margin, screening out offscreen coordinates, and batching all `UNEXPLORED` (0) tiles into one `ctx.beginPath()` / `ctx.fill()` pass and all `EXPLORED_FOGGED` (1) tiles into a second pass, total draw calls drop to **2 passes**, with **0 bytes heap allocated per frame**.

3. **Smooth Horizon Radial Vignette**:
   In 2:1 isometric projection, a world radius of 8.0 units forms an ellipse with semi-major axis $8.5 \times 32\text{px} = 272\text{px}$ and semi-minor axis $8.5 \times 16\text{px} = 136\text{px}$.
   By applying `ctx.scale(1.0, 0.5)` with a radial gradient between radius 6.8 and 8.5, tile edges at the vision perimeter are softly blended into the fogged veil without jagged steps.

4. **Dynamic Entity Suppression Rationale**:
   From Observations 2, 3, and 4:
   - Dynamic loot beams and PoELabels (`world_renderer.js`) must be hidden when `WarFog.getFogState(floor(drop.wx), floor(drop.wy)) !== 2`.
   - Live monsters and NPCs (`entity_renderer.js`) must not be pushed into `renderEntities` when their tile fog state $\neq 2$, naturally suppressing their sprites, shadows, nameplates, HP bars, and leader auras.
   - Auto-target acquisition (`monster_system.js:getBestCombatTarget`) must ignore monsters where fog state $\neq 2$ so players cannot auto-lock or cast onto hidden enemies.
   - Persistent props in $\text{EXPLORED\_FOGGED}$ (1) must remain visible but attenuated by 45% darkness to match the environment, while props in $\text{UNEXPLORED}$ (0) must be skipped to avoid 2.5D height projection protruding into unrevealed void.
   - Landmarks (Waypoints and Boss Gate runes) must remain hidden until discovered ($\text{fogState} \ge 1$).

5. **Hygiene & Line Cap Compliance**:
   - `proposed_war_fog_renderer.js` is **191 lines**, comfortably below the 250-line requirement.
   - Modifications to `world_renderer.js` require only 9 lines (471 total $\le 500$).
   - Modifications to `entity_renderer.js` require 15 lines (445 total $\le 500$).
   - Modifications to `monster_system.js` require 4 lines (489 total $\le 500$).

---

## 3. Caveats

1. **`war_fog.js` Parallel Development**: Explorer M4 1 is concurrently designing the core `WarFog` state matrix and `localStorage` persistence. The renderer and entity suppression logic defensively check `typeof window.WarFog?.getFogState === 'function'` and fall back gracefully to `window.fogGrid` or `true` if fog is not initialized (e.g. in safe haven town/hideout).
2. **Minimap Integration**: Explorer M4 3 is handling the $120 \times 80\text{px}$ Minimap HUD canvas. The `WarFogRenderer` world canvas overlay is completely decoupled from the minimap canvas, ensuring independent maintenance.
3. **Safe Haven Zones**: In `zone_player_hideout` and `zone_boundless_sanctuary`, no fog grid is instantiated, so all entity suppression guards immediately evaluate to visible, preserving full functionality in social and hideout zones.

---

## 4. Conclusion

The visual Fog of War world canvas overlay and entity suppression system is fully investigated, mathematically verified, and validated against performance and line-count constraints.
Artifacts provided in this directory:
1. `c:\Projects\FreeExile\.agents\teamwork\explorer_m4_2\proposed_war_fog_renderer.js` (Complete 191-line drop-in implementation).
2. `c:\Projects\FreeExile\.agents\teamwork\explorer_m4_2\war_fog_renderer.patch` (Git unified patch).
3. `c:\Projects\FreeExile\.agents\teamwork\explorer_m4_2\test_proposed_renderer.js` (Node.js test harness validating draw calls $\le 3$ and zero heap allocation).
4. `c:\Projects\FreeExile\.agents\teamwork\explorer_m4_2\report.md` (Comprehensive technical report).

The worker agent can directly apply `war_fog_renderer.patch` and wire the minor entity suppression hooks into `world_renderer.js`, `entity_renderer.js`, and `monster_system.js`.

---

## 5. Verification Method

To independently verify all findings and performance guarantees:

1. **Run Proposed Renderer Test Harness**:
   ```bash
   node c:\Projects\FreeExile\.agents\teamwork\explorer_m4_2\test_proposed_renderer.js
   ```
   *Expected Output*:
   - `✅ FogState constants verified.`
   - `✅ Entity visibility check logic verified.`
   - `✅ Render executed cleanly in 2 draw calls with 190 tiles culled and batched.`
   - `✅ Zero heap pressure confirmed: delta over 5000 frames <= 0 KB.`
   - `--- ALL TESTS PASSED! ---`

2. **Verify Line Counts & Hygiene Compliance**:
   ```powershell
   (Get-Content c:\Projects\FreeExile\.agents\teamwork\explorer_m4_2\proposed_war_fog_renderer.js).Length
   # Must be <= 250 (Actual: 191)
   
   python tools/lint/check_code_and_doc_hygiene.py --strict
   # Must return exit code 0
   ```

3. **Verify Zero Regression Across Existing Test Suites**:
   ```bash
   python -m pytest tests/unit/test_war_fog_and_procedural_map.py
   python -m pytest tests/e2e/test_poe2_map_system_e2e.py
   ```
   *Expected Output*: 101/101 tests pass.

4. **Invalidation Conditions**:
   - If `war_fog_renderer.js` exceeds 250 lines.
   - If rendering allocates heap memory inside the frame loop.
   - If monsters, NPCs, or dynamic loot outside `VISIBLE` tiles are rendered on screen.
