meow
This commit is contained in:
+119
-12
@@ -2789,6 +2789,13 @@ function battleLineColor(line) {
|
||||
return 'text-text-soft'
|
||||
}
|
||||
|
||||
function formatLogTime(ms) {
|
||||
const totalSeconds = Math.floor(Number(ms || 0) / 1000)
|
||||
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0')
|
||||
const seconds = String(totalSeconds % 60).padStart(2, '0')
|
||||
return `${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
function deadVehicleKey(uid, cdk) {
|
||||
return `${String(uid || '').trim()}:${String(cdk || '').trim()}`
|
||||
}
|
||||
@@ -2806,16 +2813,61 @@ function deadVehicleKeysFromEventLog(eventLog) {
|
||||
return keys
|
||||
}
|
||||
|
||||
function logLookups(participants) {
|
||||
const players = new Map()
|
||||
;(participants || []).forEach((participant) => {
|
||||
const result = String(participant.result || '').toLowerCase() === 'win' ? 'win' : 'loss'
|
||||
;(participant.players || []).forEach((player) => {
|
||||
const vehicles = new Map()
|
||||
;(player.vehicles || []).forEach((vehicle) => {
|
||||
vehicles.set(String(vehicle.cdk || ''), vehicle.name || vehicle.cdk || 'Unknown')
|
||||
})
|
||||
players.set(String(player.uid), {
|
||||
name: player.nick || player.uid,
|
||||
team: participant.team_name || '',
|
||||
result,
|
||||
className: result === 'win' ? 'text-win' : 'text-loss',
|
||||
vehicles,
|
||||
})
|
||||
})
|
||||
})
|
||||
return players
|
||||
}
|
||||
|
||||
function logPlayer(players, uid) {
|
||||
return players.get(String(uid)) || {
|
||||
name: uid === undefined || uid === null ? 'Unknown' : `Player#${uid}`,
|
||||
team: '',
|
||||
result: '',
|
||||
className: 'text-text-soft',
|
||||
vehicles: new Map(),
|
||||
}
|
||||
}
|
||||
|
||||
function logVehicle(player, cdk) {
|
||||
if (!cdk) return 'Unknown'
|
||||
return player.vehicles.get(String(cdk)) || String(cdk)
|
||||
}
|
||||
|
||||
function structuredBattleEvents(eventLog) {
|
||||
const kills = Array.isArray(eventLog?.kills) ? eventLog.kills : []
|
||||
const damage = Array.isArray(eventLog?.damage) ? eventLog.damage : []
|
||||
return [
|
||||
...kills.map((event) => ({ ...event, kind: 'kill' })),
|
||||
...damage.map((event) => ({ ...event, kind: 'damage' })),
|
||||
].sort((a, b) => Number(a.time || 0) - Number(b.time || 0))
|
||||
}
|
||||
|
||||
function GamePage({ gameId, navigate }) {
|
||||
const [gameState, setGameState] = useState({ status: 'loading', data: null, error: null })
|
||||
const [logs, setLogs] = useState({ chat_log: [], battle_log: [], event_log: { kills: [], damage: [] } })
|
||||
const [logs, setLogs] = useState({ chat_log: [], battle_log: [], event_log: { kills: [], damage: [], chat: [] } })
|
||||
|
||||
useEffect(() => {
|
||||
if (!gameId) return
|
||||
|
||||
const controller = new AbortController()
|
||||
setGameState({ status: 'loading', data: null, error: null })
|
||||
setLogs({ chat_log: [], battle_log: [], event_log: { kills: [], damage: [] } })
|
||||
setLogs({ chat_log: [], battle_log: [], event_log: { kills: [], damage: [], chat: [] } })
|
||||
|
||||
fetchJson(apiEndpoints.game(gameId), controller.signal)
|
||||
.then((data) => {
|
||||
@@ -2835,7 +2887,7 @@ function GamePage({ gameId, navigate }) {
|
||||
setLogs({
|
||||
chat_log: Array.isArray(data?.chat_log) ? data.chat_log : [],
|
||||
battle_log: Array.isArray(data?.battle_log) ? data.battle_log : [],
|
||||
event_log: data?.event_log || { kills: [], damage: [] },
|
||||
event_log: data?.event_log || { kills: [], damage: [], chat: [] },
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -2847,8 +2899,11 @@ function GamePage({ gameId, navigate }) {
|
||||
}, [gameId])
|
||||
|
||||
const game = gameState.data?.game
|
||||
const participants = gameState.data?.participants || []
|
||||
const participants = useMemo(() => gameState.data?.participants || [], [gameState.data?.participants])
|
||||
const deadVehicleKeys = useMemo(() => deadVehicleKeysFromEventLog(logs.event_log), [logs.event_log])
|
||||
const playersByUid = useMemo(() => logLookups(participants), [participants])
|
||||
const battleEvents = useMemo(() => structuredBattleEvents(logs.event_log), [logs.event_log])
|
||||
const chatEvents = Array.isArray(logs.event_log?.chat) ? logs.event_log.chat : []
|
||||
const participantNames = participants.length
|
||||
? participants.map((participant) => ({
|
||||
name: participant.team_name,
|
||||
@@ -2995,29 +3050,81 @@ function GamePage({ gameId, navigate }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{logs.battle_log.length ? (
|
||||
{battleEvents.length || logs.battle_log.length ? (
|
||||
<details className="rounded-lg border border-border bg-fury-white shadow-sm">
|
||||
<summary className="cursor-pointer px-5 py-4 font-semibold">Battle Log</summary>
|
||||
<div className="log-mono overflow-x-auto px-5 py-3 text-xs leading-relaxed">
|
||||
{logs.battle_log.map((line, i) => (
|
||||
<div className={`whitespace-pre ${battleLineColor(line)}`} key={i}>{line}</div>
|
||||
))}
|
||||
{battleEvents.length ? (
|
||||
battleEvents.map((event, i) => (
|
||||
<BattleEventLine event={event} key={`${event.kind}-${event.time}-${i}`} players={playersByUid} />
|
||||
))
|
||||
) : (
|
||||
logs.battle_log.map((line, i) => (
|
||||
<div className={`whitespace-pre ${battleLineColor(line)}`} key={i}>{line}</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
) : null}
|
||||
|
||||
{logs.chat_log.length ? (
|
||||
{chatEvents.length || logs.chat_log.length ? (
|
||||
<details className="rounded-lg border border-border bg-fury-white shadow-sm">
|
||||
<summary className="cursor-pointer px-5 py-4 font-semibold">Chat Log</summary>
|
||||
<pre className="log-mono overflow-x-auto whitespace-pre-wrap px-5 py-3 text-xs leading-relaxed text-text-soft">
|
||||
{logs.chat_log.join('\n')}
|
||||
</pre>
|
||||
<div className="log-mono overflow-x-auto px-5 py-3 text-xs leading-relaxed">
|
||||
{chatEvents.length ? (
|
||||
chatEvents.map((event, i) => (
|
||||
<ChatEventLine event={event} key={`${event.time}-${event.uid}-${i}`} players={playersByUid} />
|
||||
))
|
||||
) : (
|
||||
logs.chat_log.map((line, i) => (
|
||||
<div className={`whitespace-pre-wrap ${battleLineColor(line)}`} key={i}>{line}</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
) : null}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function BattleEventLine({ event, players }) {
|
||||
const offender = logPlayer(players, event.offender_uid)
|
||||
const victim = logPlayer(players, event.offended_uid)
|
||||
const ts = formatLogTime(event.time)
|
||||
const victimLabel = `${victim.name} (${logVehicle(victim, event.offended_unit)})`
|
||||
|
||||
if (event.kind === 'kill' && (event.crashed || event.offender_uid === undefined || event.offender_uid === null)) {
|
||||
return (
|
||||
<div className="whitespace-pre">
|
||||
<span className="text-text-soft">[{ts}] </span>
|
||||
<span className={victim.className}>[{victim.team}] {victimLabel}</span>
|
||||
<span className="text-text-soft"> crashed</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const offenderLabel = `${offender.name} (${logVehicle(offender, event.offender_unit)})`
|
||||
const action = event.kind === 'kill' ? 'destroyed' : `damaged ${event.afire ? '(FIRE) ' : ''}`
|
||||
|
||||
return (
|
||||
<div className="whitespace-pre">
|
||||
<span className="text-text-soft">[{ts}] </span>
|
||||
<span className={offender.className}>[{offender.team}] {offenderLabel}</span>
|
||||
<span className="text-text-soft"> {action} </span>
|
||||
<span className={victim.className}>{victimLabel}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ChatEventLine({ event, players }) {
|
||||
const player = logPlayer(players, event.uid)
|
||||
return (
|
||||
<div className={`whitespace-pre-wrap ${player.className}`}>
|
||||
[{formatLogTime(event.time)}] [{event.type || 'ALL'}] [{player.team}] `{player.name}`: {event.message || ''}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function RosterTable({ players, status }) {
|
||||
const sortedPlayers = [...players].sort((a, b) => {
|
||||
return (b.total_kills || 0) - (a.total_kills || 0) || String(a.nick || '').localeCompare(b.nick || '')
|
||||
|
||||
Reference in New Issue
Block a user