updated
This commit is contained in:
parent
144335f503
commit
97e52dba2d
@ -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<DeathMarker[]>([])
|
||||
const [deathMarkers, setDeathMarkers] = useState<DeathMarker[]>([])
|
||||
|
||||
// Bomb
|
||||
const bombRef = useRef<BombState | null>(null)
|
||||
const [bomb, setBomb] = useState<BombState | null>(null)
|
||||
|
||||
// Flush
|
||||
const flushTimer = useRef<number | null>(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 <circle key={g.id} cx={P.x} cy={P.y} r={Math.max(4, rPx*0.4)} fill={g.kind === 'he' ? UI.nade.heFill : '#999'} stroke={stroke} strokeWidth={Math.max(1, sw*0.8)} />
|
||||
})}
|
||||
|
||||
{/* 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 (
|
||||
<g key={`bomb-${bomb.changedAt}`}>
|
||||
{/* Ping nur wenn geplantet */}
|
||||
{bomb.status === 'planted' && (
|
||||
<circle cx={P.x} cy={P.y} r={r} fill="none" stroke="#ef4444" strokeWidth={2}>
|
||||
<animate attributeName="r" from={r} to={r*3} dur="1.2s" repeatCount="indefinite" />
|
||||
<animate attributeName="opacity" from="0.6" to="0" dur="1.2s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
)}
|
||||
|
||||
{/* Marker */}
|
||||
<circle cx={P.x} cy={P.y} r={r} fill={color} stroke="#111" strokeWidth={2} />
|
||||
<text x={P.x} y={P.y + r*0.35} textAnchor="middle" fontSize={r} fill="#fff" fontWeight="700">B</text>
|
||||
</g>
|
||||
)
|
||||
})()}
|
||||
|
||||
{/* Spieler */}
|
||||
{players
|
||||
.filter(p => (p.team === 'CT' || p.team === 'T') && p.alive !== false)
|
||||
|
||||
@ -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<WebSocket | null>(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(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user