; ; /* ============================================================================ BATTLEFIELD INTEL — vision, radar, detection, stealth ---------------------------------------------------------------------------- Fog already hid models, bars, FX and the minimap. What it did not do was read the intel data the roster already carried: TYPES[].scout Kestrel (and faction scouts) used the same air bubble as a Wasp. The card said "150 sight"; fog stamped 14 cells. umode === 4 GHOST: findEnemy skipped them, firing broke cover, hulls went translucent — and the player still SAW them the moment they entered a sensor circle. "Unseen until you fire" was targeting-only. BT.uplink Targeting Array / radar dish. Boosted towers and the build grid. Vision was the generic 10-cell building stamp, so a radar mast saw the same as a silo. WC.dark "vision suffers" only forced night. Fog Bank (fogb) was the only wildcard that actually shortened sensors. Jamming has no authored radius/flag. Ghost Net already stuns and scans; a dedicated radar-denial layer is a later plan item, not invented here. Radar is a COMMAND-MAP layer, not a second 3D reveal. Uplink/techlab stamp fogRadar; the minimap draws those contacts; render3d keeps using fogEntityVisible (visual). That split is why this file must not live in the renderer — and why it never writes GL state. Detection is the answer to GHOST. Scouts, uplinks, techlabs, HQs and live survey scans stamp fogDetect. intelCanTarget (sim.js seam) lets a detector lock a silent-running hull; fogEntityVisible hides it from the player until then. Same numbers both ways, so a cloak that fools the eye also fools the gun until a sensor is in range. ============================================================================ */ const INTEL_VIS_GROUND=10, INTEL_VIS_AIR=14, INTEL_VIS_SCOUT=18, INTEL_VIS_GHOST=4; const INTEL_VIS_HQ=22, INTEL_VIS_TURRET=12, INTEL_VIS_BLD=10, INTEL_VIS_CARRIER=24; /* Radar cells are larger than visual on purpose: the mast hears further than it sees. Contacts are minimap-only (see intelRadarContact). */ const INTEL_RADAR_UPLINK=22, INTEL_RADAR_TECHLAB=16; /* Detect is shorter than radar. Omni-style pierce, not a second map hack. */ const INTEL_DET_SCOUT=16, INTEL_DET_UPLINK=14, INTEL_DET_TECHLAB=12, INTEL_DET_HQ=8; const fogRadar=new Uint8Array(FN*FN); const fogDetect=new Uint8Array(FN*FN); const intelGhostIdx=[]; let intelGhostStamp=-1; const intelHasGhost=[0,0,0]; function intelVisionScale(r){ let s=r; if(typeof WC!=='undefined'){ if(WC.fogb) s=Math.max(4,Math.round(s*0.6)); /* Total Eclipse already forces night (sim.js dayT). The card also promised shorter sensors; without this it was a free payout on a cosmetic sky. */ if(WC.dark) s=Math.max(4,Math.round(s*0.72)); } return s; } function intelUnitVision(i,vis,hm){ const T=TYPES[utype[i]]; let base=T.scout?INTEL_VIS_SCOUT:(T.air?INTEL_VIS_AIR:INTEL_VIS_GROUND); if(umode[i]===4) base+=INTEL_VIS_GHOST; const scaled=vis(base); return Math.max(6,Math.round(6+(scaled-6)*hm)); } function intelBldVision(B,vis,hm){ const t=B.type==='hq'?INTEL_VIS_HQ:B.type==='turret'?INTEL_VIS_TURRET:INTEL_VIS_BLD; return Math.max(4,Math.round(vis(t)*hm)); } function intelCell(wx,wy){ return clamp(wy/MAP*FN|0,0,FN-1)*FN+clamp(wx/MAP*FN|0,0,FN-1); } function intelStamp(grid,wx,wy,rc,bit){ if(rc<=0) return; const cx=clamp(wx/MAP*FN|0,0,FN-1), cy=clamp(wy/MAP*FN|0,0,FN-1), r2=rc*rc; for(let y=Math.max(0,cy-rc);y<=Math.min(FN-1,cy+rc);y++) for(let x=Math.max(0,cx-rc);x<=Math.min(FN-1,cx+rc);x++){ const dx=x-cx,dy=y-cy; if(dx*dx+dy*dy<=r2) grid[y*FN+x]|=bit; } } function intelDetectedAt(wx,wy,team){ const bit=1<<(team|0); return !!(fogDetect[intelCell(wx,wy)]&bit); } function intelRadarContact(wx,wy){ if(typeof fogGameplayActive==='function'&&!fogGameplayActive()) return false; return !!(fogRadar[intelCell(wx,wy)]&1); } function intelGhostRefresh(){ const t=(typeof stats!=='undefined'&&stats)?stats.t:-2; if(t===intelGhostStamp) return; intelGhostStamp=t; intelGhostIdx.length=0; intelHasGhost[0]=intelHasGhost[1]=intelHasGhost[2]=0; for(let i=0;i