updated
This commit is contained in:
parent
144335f503
commit
97e52dba2d
@ -146,6 +146,14 @@ type PlayerState = {
|
|||||||
defuse?: boolean | null
|
defuse?: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BombState = {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
z: number
|
||||||
|
status: 'carried'|'dropped'|'planted'|'unknown'
|
||||||
|
changedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
type Grenade = {
|
type Grenade = {
|
||||||
id: string
|
id: string
|
||||||
kind: 'smoke' | 'molotov' | 'he' | 'flash' | 'decoy' | 'unknown'
|
kind: 'smoke' | 'molotov' | 'he' | 'flash' | 'decoy' | 'unknown'
|
||||||
@ -186,6 +194,10 @@ export default function LiveRadar() {
|
|||||||
const deathMarkersRef = useRef<DeathMarker[]>([])
|
const deathMarkersRef = useRef<DeathMarker[]>([])
|
||||||
const [deathMarkers, setDeathMarkers] = useState<DeathMarker[]>([])
|
const [deathMarkers, setDeathMarkers] = useState<DeathMarker[]>([])
|
||||||
|
|
||||||
|
// Bomb
|
||||||
|
const bombRef = useRef<BombState | null>(null)
|
||||||
|
const [bomb, setBomb] = useState<BombState | null>(null)
|
||||||
|
|
||||||
// Flush
|
// Flush
|
||||||
const flushTimer = useRef<number | null>(null)
|
const flushTimer = useRef<number | null>(null)
|
||||||
const scheduleFlush = () => {
|
const scheduleFlush = () => {
|
||||||
@ -196,8 +208,11 @@ export default function LiveRadar() {
|
|||||||
setGrenades(Array.from(grenadesRef.current.values()))
|
setGrenades(Array.from(grenadesRef.current.values()))
|
||||||
setTrails(Array.from(trailsRef.current.values()))
|
setTrails(Array.from(trailsRef.current.values()))
|
||||||
setDeathMarkers([...deathMarkersRef.current])
|
setDeathMarkers([...deathMarkersRef.current])
|
||||||
|
updateBombFromPlayers()
|
||||||
|
setBomb(bombRef.current)
|
||||||
}, 66)
|
}, 66)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (flushTimer.current != null) {
|
if (flushTimer.current != null) {
|
||||||
@ -212,6 +227,7 @@ export default function LiveRadar() {
|
|||||||
deathMarkersRef.current = []
|
deathMarkersRef.current = []
|
||||||
trailsRef.current.clear()
|
trailsRef.current.clear()
|
||||||
grenadesRef.current.clear()
|
grenadesRef.current.clear()
|
||||||
|
bombRef.current = null
|
||||||
scheduleFlush()
|
scheduleFlush()
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
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 ───────── */
|
/* ───────── Positions-Callbacks ───────── */
|
||||||
const addDeathMarker = (x:number, y:number, idHint?: string) => {
|
const addDeathMarker = (x:number, y:number, idHint?: string) => {
|
||||||
deathMarkersRef.current.push({ id: idHint ?? `d#${Date.now()}`, x, y, t: Date.now() })
|
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() }}
|
onPlayerUpdate={(p)=> { upsertPlayer(p); scheduleFlush() }}
|
||||||
onPlayersAll={(m)=> { handlePlayersAll(m); scheduleFlush() }}
|
onPlayersAll={(m)=> { handlePlayersAll(m); scheduleFlush() }}
|
||||||
onGrenades={(g)=> { handleGrenades(g); 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) */}
|
{/* 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)} />
|
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 */}
|
{/* Spieler */}
|
||||||
{players
|
{players
|
||||||
.filter(p => (p.team === 'CT' || p.team === 'T') && p.alive !== false)
|
.filter(p => (p.team === 'CT' || p.team === 'T') && p.alive !== false)
|
||||||
|
|||||||
@ -11,8 +11,9 @@ type PositionsSocketProps = {
|
|||||||
onPlayerUpdate?: (p: any) => void
|
onPlayerUpdate?: (p: any) => void
|
||||||
onPlayersAll?: (allplayers: any) => void
|
onPlayersAll?: (allplayers: any) => void
|
||||||
onGrenades?: (g: any) => void
|
onGrenades?: (g: any) => void
|
||||||
onRoundStart?: () => void // ⬅️ NEU
|
onRoundStart?: () => void
|
||||||
onRoundEnd?: () => void // ⬅️ optional
|
onRoundEnd?: () => void
|
||||||
|
onBomb?: (b:any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PositionsSocket({
|
export default function PositionsSocket({
|
||||||
@ -24,6 +25,7 @@ export default function PositionsSocket({
|
|||||||
onGrenades,
|
onGrenades,
|
||||||
onRoundStart,
|
onRoundStart,
|
||||||
onRoundEnd,
|
onRoundEnd,
|
||||||
|
onBomb
|
||||||
}: PositionsSocketProps) {
|
}: PositionsSocketProps) {
|
||||||
const wsRef = useRef<WebSocket | null>(null)
|
const wsRef = useRef<WebSocket | null>(null)
|
||||||
const aliveRef = useRef(true)
|
const aliveRef = useRef(true)
|
||||||
@ -41,6 +43,7 @@ export default function PositionsSocket({
|
|||||||
if (typeof msg.map === 'string') onMap?.(msg.map.toLowerCase())
|
if (typeof msg.map === 'string') onMap?.(msg.map.toLowerCase())
|
||||||
if (Array.isArray(msg.players)) msg.players.forEach(onPlayerUpdate ?? (() => {}))
|
if (Array.isArray(msg.players)) msg.players.forEach(onPlayerUpdate ?? (() => {}))
|
||||||
if (msg.grenades) onGrenades?.(msg.grenades)
|
if (msg.grenades) onGrenades?.(msg.grenades)
|
||||||
|
if (msg.bomb) onBomb?.(msg.bomb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +51,9 @@ export default function PositionsSocket({
|
|||||||
if (msg.allplayers) onPlayersAll?.(msg)
|
if (msg.allplayers) onPlayersAll?.(msg)
|
||||||
if (msg.player || msg.steamId || msg.position || msg.pos) onPlayerUpdate?.(msg)
|
if (msg.player || msg.steamId || msg.position || msg.pos) onPlayerUpdate?.(msg)
|
||||||
if (msg.grenades && msg.type !== 'tick') onGrenades?.(msg.grenades)
|
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(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user