// CS2 map calibration. `pos_x`, `pos_y` are the world coords of the radar // PNG's upper-left corner; `scale` is units-per-pixel. Multi-floor maps use // `lower_level_max_units` as a Z threshold: any player at or below it is on // the lower floor and should be drawn against the `_lower` radar. // // Source: awpy's map-data.json (https://awpycs.com//maps.zip). export type MapData = { pos_x: number; pos_y: number; scale: number; rotate: number | null; zoom: number | null; lower_level_max_units: number; }; export const RADAR_PX = 1024; let cache: Record | null = null; let inflight: Promise> | null = null; export async function loadMapData(fetchFn: typeof fetch = fetch): Promise> { if (cache) return cache; if (!inflight) { inflight = fetchFn('/maps/map-data.json') .then((r) => { if (!r.ok) throw new Error(`map-data.json ${r.status}`); return r.json() as Promise>; }) .then((d) => { cache = d; return d; }); } return inflight; } export type Floor = 'upper' | 'lower'; export function radarUrl(mapName: string, floor: Floor): string { return floor === 'lower' ? `/maps/${mapName}_lower.webp` : `/maps/${mapName}.webp`; } export function hasLowerFloor(map: MapData): boolean { // awpy uses -1_000_000 as the sentinel for "no lower floor". return map.lower_level_max_units > -100_000; } export function floorForZ(map: MapData, z: number): Floor { if (!hasLowerFloor(map)) return 'upper'; // de_vertigo is sky-high (lower_level_max_units = 11700 means below that = // lower B site). de_nuke / de_train use a negative threshold. The // inequality is the same in both cases. return z <= map.lower_level_max_units ? 'lower' : 'upper'; } export function worldToImage(map: MapData, x: number, y: number): { px: number; py: number } { return { px: (x - map.pos_x) / map.scale, py: (map.pos_y - y) / map.scale }; } // CS yaw: 0° = +X (east), 90° = +Y (north), CCW positive. Image space has // Y pointing down, so a screen-space rotation of `-yaw` makes an icon whose // "forward" is +X point in the player's facing direction. export function yawToScreenDeg(yaw: number): number { return -yaw; }