From 97e52dba2df92ac9b6513e779ddc921779d6afa6 Mon Sep 17 00:00:00 2001 From: Linrador Date: Tue, 9 Sep 2025 13:51:31 +0200 Subject: [PATCH] updated --- src/app/components/radar/LiveRadar.tsx | 95 ++++++++++++++++++++ src/app/components/radar/PositionsSocket.tsx | 10 ++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/app/components/radar/LiveRadar.tsx b/src/app/components/radar/LiveRadar.tsx index 34d58e3..d0f305d 100644 --- a/src/app/components/radar/LiveRadar.tsx +++ b/src/app/components/radar/LiveRadar.tsx @@ -146,6 +146,14 @@ type PlayerState = { defuse?: boolean | null } +type BombState = { + x: number + y: number + z: number + status: 'carried'|'dropped'|'planted'|'unknown' + changedAt: number +} + type Grenade = { id: string kind: 'smoke' | 'molotov' | 'he' | 'flash' | 'decoy' | 'unknown' @@ -186,6 +194,10 @@ export default function LiveRadar() { const deathMarkersRef = useRef([]) const [deathMarkers, setDeathMarkers] = useState([]) + // Bomb + const bombRef = useRef(null) + const [bomb, setBomb] = useState(null) + // Flush const flushTimer = useRef(null) const scheduleFlush = () => { @@ -196,8 +208,11 @@ export default function LiveRadar() { setGrenades(Array.from(grenadesRef.current.values())) setTrails(Array.from(trailsRef.current.values())) setDeathMarkers([...deathMarkersRef.current]) + updateBombFromPlayers() + setBomb(bombRef.current) }, 66) } + useEffect(() => { return () => { if (flushTimer.current != null) { @@ -212,6 +227,7 @@ export default function LiveRadar() { deathMarkersRef.current = [] trailsRef.current.clear() grenadesRef.current.clear() + bombRef.current = null scheduleFlush() } useEffect(() => { @@ -265,6 +281,59 @@ export default function LiveRadar() { } } + /* ───────── Bomben-Helper ───────── */ + function pickVec3(src:any) { + const p = src?.pos ?? src?.position ?? src?.location ?? src?.coordinates + if (Array.isArray(p)) return { x: asNum(p[0]), y: asNum(p[1]), z: asNum(p[2]) } + if (typeof p === 'string') return parseVec3String(p) + return { x: asNum(src?.x), y: asNum(src?.y), z: asNum(src?.z) } + } + + function normalizeBomb(raw:any): BombState | null { + if (!raw) return null + + // „event“-Hüllen abfangen + const payload = raw.bomb ?? raw.c4 ?? raw + + const pos = pickVec3(payload) + let status: BombState['status'] = 'unknown' + const s = String(payload?.status ?? payload?.state ?? '').toLowerCase() + + if (s.includes('plant')) status = 'planted' + else if (s.includes('drop')) status = 'dropped' + else if (s.includes('carry')) status = 'carried' + + // bool-Varianten + if (payload?.planted === true) status = 'planted' + if (payload?.dropped === true) status = 'dropped' + if (payload?.carried === true) status = 'carried' + + // Event-Typen + const t = String(raw?.type ?? '').toLowerCase() + if (t === 'bomb_planted') status = 'planted' + if (t === 'bomb_dropped') status = 'dropped' + if (t === 'bomb_pickup') status = 'carried' + + // Ohne brauchbare Position: ignorieren + if (!Number.isFinite(pos.x) || !Number.isFinite(pos.y)) return null + + return { x: pos.x, y: pos.y, z: pos.z, status, changedAt: Date.now() } + } + + // Fallback: aus Spielerzustand ableiten (Bombenträger) + const updateBombFromPlayers = () => { + const carrier = Array.from(playersRef.current.values()).find(p => p.hasBomb) + if (carrier) { + bombRef.current = { + x: carrier.x, y: carrier.y, z: carrier.z, + status: 'carried', changedAt: Date.now() + } + } else if (bombRef.current?.status === 'carried') { + // Träger hat die Bombe nicht mehr → als „gedroppt“ an letzter Position belassen + bombRef.current = { ...bombRef.current, status: 'dropped', changedAt: Date.now() } + } + } + /* ───────── Positions-Callbacks ───────── */ const addDeathMarker = (x:number, y:number, idHint?: string) => { deathMarkersRef.current.push({ id: idHint ?? `d#${Date.now()}`, x, y, t: Date.now() }) @@ -664,6 +733,8 @@ export default function LiveRadar() { onPlayerUpdate={(p)=> { upsertPlayer(p); scheduleFlush() }} onPlayersAll={(m)=> { handlePlayersAll(m); scheduleFlush() }} onGrenades={(g)=> { handleGrenades(g); scheduleFlush() }} + onRoundStart={() => { bombRef.current = null; scheduleFlush() }} // ⬅️ sinnvoll + onBomb={(b)=> { const nb = normalizeBomb(b); if (nb) { bombRef.current = nb; scheduleFlush() } }} // ⬅️ NEU /> {/* Inhalt: 3-Spalten-Layout (T | Radar | CT) */} @@ -772,6 +843,30 @@ export default function LiveRadar() { return })} + {/* Bombe */} + {bomb && (() => { + const P = worldToPx(bomb.x, bomb.y) + if (!Number.isFinite(P.x) || !Number.isFinite(P.y)) return null + const r = Math.max(6, unitsToPx(28)) // Marker-Größe + const color = bomb.status === 'planted' ? '#ef4444' : '#dddddd' + + return ( + + {/* Ping nur wenn geplantet */} + {bomb.status === 'planted' && ( + + + + + )} + + {/* Marker */} + + B + + ) + })()} + {/* Spieler */} {players .filter(p => (p.team === 'CT' || p.team === 'T') && p.alive !== false) diff --git a/src/app/components/radar/PositionsSocket.tsx b/src/app/components/radar/PositionsSocket.tsx index 0f5eadc..6fb3a98 100644 --- a/src/app/components/radar/PositionsSocket.tsx +++ b/src/app/components/radar/PositionsSocket.tsx @@ -11,8 +11,9 @@ type PositionsSocketProps = { onPlayerUpdate?: (p: any) => void onPlayersAll?: (allplayers: any) => void onGrenades?: (g: any) => void - onRoundStart?: () => void // ⬅️ NEU - onRoundEnd?: () => void // ⬅️ optional + onRoundStart?: () => void + onRoundEnd?: () => void + onBomb?: (b:any) => void } export default function PositionsSocket({ @@ -24,6 +25,7 @@ export default function PositionsSocket({ onGrenades, onRoundStart, onRoundEnd, + onBomb }: PositionsSocketProps) { const wsRef = useRef(null) const aliveRef = useRef(true) @@ -41,6 +43,7 @@ export default function PositionsSocket({ if (typeof msg.map === 'string') onMap?.(msg.map.toLowerCase()) if (Array.isArray(msg.players)) msg.players.forEach(onPlayerUpdate ?? (() => {})) if (msg.grenades) onGrenades?.(msg.grenades) + if (msg.bomb) onBomb?.(msg.bomb) return } @@ -48,6 +51,9 @@ export default function PositionsSocket({ if (msg.allplayers) onPlayersAll?.(msg) if (msg.player || msg.steamId || msg.position || msg.pos) onPlayerUpdate?.(msg) if (msg.grenades && msg.type !== 'tick') onGrenades?.(msg.grenades) + if (msg.bomb || msg.c4 || ['bomb_planted','bomb_dropped','bomb_pickup'].includes(String(msg.type).toLowerCase())) { + onBomb?.(msg) + } } useEffect(() => {