fix team names in replay viewer

This commit is contained in:
FURRO404
2026-06-18 20:49:50 -07:00
parent fb08e99e5d
commit edf28855b6
2 changed files with 77 additions and 34 deletions
+76 -32
View File
@@ -132,6 +132,7 @@ class ReplayCanvasEngine {
_buildDOM() { _buildDOM() {
this.container.innerHTML = '' this.container.innerHTML = ''
this._panelRows = []
const layout = document.createElement('div') const layout = document.createElement('div')
layout.className = 'rc-layout' layout.className = 'rc-layout'
@@ -222,32 +223,28 @@ class ReplayCanvasEngine {
_buildTeamPanel(panel, isWinner) { _buildTeamPanel(panel, isWinner) {
const teamEntities = this.entities.filter((e) => e.playerId > 0 && e.isWinner === isWinner) const teamEntities = this.entities.filter((e) => e.playerId > 0 && e.isWinner === isWinner)
const seen = new Set() // One row per player; a player may have several entities (one per spawn).
const unique = [] const byPlayer = new Map()
for (const e of teamEntities) { for (const e of teamEntities) {
if (e.type === 'ground' && !seen.has(e.playerId)) { if (!byPlayer.has(e.playerId)) byPlayer.set(e.playerId, [])
seen.add(e.playerId) byPlayer.get(e.playerId).push(e)
unique.push(e)
}
}
for (const e of teamEntities) {
if (!seen.has(e.playerId)) {
seen.add(e.playerId)
unique.push(e)
}
} }
for (const list of byPlayer.values()) list.sort((a, b) => a.times[0] - b.times[0])
const color = isWinner ? RC.WIN : RC.LOSE const color = isWinner ? RC.WIN : RC.LOSE
const firstPlayer = unique.length ? this.players[unique[0].playerId] : null const firstId = byPlayer.size ? byPlayer.keys().next().value : null
const clanTag = firstPlayer?.clan || '' const firstPlayer = firstId ? this.players[firstId] : null
const label = clanTag ? `<span class="rc-clan-tag">${esc(clanTag)}</span>` : (isWinner ? 'Winners' : 'Losers') const teamSlot = firstPlayer ? firstPlayer.team : (isWinner ? this.data.teamWon : null)
const teamName = teamSlot != null ? this.data.teamNames?.[String(teamSlot)] : ''
const label = teamName ? esc(teamName) : (isWinner ? 'Winners' : 'Losers')
let html = `<div class="rc-panel-head"><span class="rc-panel-label" style="color:${color}">${label}</span></div><div class="rc-panel-list">` let html = `<div class="rc-panel-head"><span class="rc-panel-label" style="color:${color}">${label}</span></div><div class="rc-panel-list">`
for (const ent of unique) { for (const [pid, list] of byPlayer) {
const p = this.players[ent.playerId] const p = this.players[pid]
const name = p ? esc(p.name) : '?' const name = p ? esc(p.name) : '?'
const ent = list[0]
const veh = esc(ent.vehicleName) const veh = esc(ent.vehicleName)
const panelIcon = ent.miniIcon ? ent.miniIcon.replace('mini:', '') : (ent.iconKey || 'medium') const panelIcon = ent.miniIcon ? ent.miniIcon.replace('mini:', '') : (ent.iconKey || 'medium')
html += `<div class="rc-row" data-player-id="${ent.playerId}" data-entity-index="${ent.entityIndex}"> html += `<div class="rc-row" data-player-id="${pid}">
<img class="rc-type-icon" src="/api/icons/type/${panelIcon}" alt="" loading="lazy" onerror="this.style.display='none'"> <img class="rc-type-icon" src="/api/icons/type/${panelIcon}" alt="" loading="lazy" onerror="this.style.display='none'">
<div class="rc-row-info"> <div class="rc-row-info">
<span class="rc-row-name">${name}</span> <span class="rc-row-name">${name}</span>
@@ -258,13 +255,51 @@ class ReplayCanvasEngine {
} }
html += '</div>' html += '</div>'
panel.innerHTML = html panel.innerHTML = html
panel.querySelectorAll('.rc-row').forEach((row) => {
const rows = panel.querySelectorAll('.rc-row')
let i = 0
for (const [pid, list] of byPlayer) {
const row = rows[i++]
if (!row) continue
const pr = {
row,
playerId: pid,
entities: list,
vehEl: row.querySelector('.rc-row-veh'),
iconEl: row.querySelector('.rc-type-icon'),
statusEl: row.querySelector('.rc-row-status'),
shownEntityIndex: list[0].entityIndex,
currentEntityIndex: list[0].entityIndex,
}
this._panelRows.push(pr)
row.addEventListener('mouseenter', () => { row.addEventListener('mouseenter', () => {
const ent = this.entities.find((e) => e.entityIndex === Number(row.dataset.entityIndex)) const ent = pr.currentEntityIndex != null
if (ent && !this._isEntityGone(ent, this.currentTime)) this._setHighlight(Number(row.dataset.playerId)) ? this.entities.find((e) => e.entityIndex === pr.currentEntityIndex)
: null
if (ent && !this._isEntityGone(ent, this.currentTime)) this._setHighlight(pid)
}) })
row.addEventListener('mouseleave', () => this._setHighlight(null)) row.addEventListener('mouseleave', () => this._setHighlight(null))
}) }
}
// Resolve which of a player's spawned vehicles is relevant at time t, and
// whether the player is currently alive, down (awaiting respawn), or gone.
_playerStateAtTime(entities, t) {
for (const e of entities) {
const first = e.times[0]
const last = e.times[e.times.length - 1]
if (t >= first && t <= last && !this._isEntityDead(e, t)) {
return { entity: e, dead: false, gone: false }
}
}
let recent = null
for (const e of entities) {
if (e.times[0] <= t && (!recent || e.times[0] >= recent.times[0])) recent = e
}
if (!recent) return { entity: entities[0] || null, dead: false, gone: false }
const hasFutureSpawn = entities.some((e) => e.times[0] > t)
const gone = !hasFutureSpawn && this._isEntityGone(recent, t)
return { entity: recent, dead: true, gone }
} }
_buildEventList() { _buildEventList() {
@@ -402,16 +437,25 @@ class ReplayCanvasEngine {
_updatePanelDeathStates() { _updatePanelDeathStates() {
const t = this.currentTime const t = this.currentTime
this.container.querySelectorAll('.rc-row').forEach((row) => { if (!this._panelRows) return
const ent = this.entities.find((e) => e.entityIndex === Number(row.dataset.entityIndex)) for (const pr of this._panelRows) {
if (!ent) return const st = this._playerStateAtTime(pr.entities, t)
const dead = this._isEntityDead(ent, t) pr.currentEntityIndex = st.entity ? st.entity.entityIndex : null
const gone = this._isEntityGone(ent, t) if (st.entity && st.entity.entityIndex !== pr.shownEntityIndex) {
row.classList.toggle('rc-dead', dead) pr.shownEntityIndex = st.entity.entityIndex
row.classList.toggle('rc-gone', gone) if (pr.vehEl) pr.vehEl.textContent = st.entity.vehicleName || ''
const status = row.querySelector('.rc-row-status') if (pr.iconEl) {
status.textContent = dead || gone ? 'x' : '' const panelIcon = st.entity.miniIcon
}) ? st.entity.miniIcon.replace('mini:', '')
: (st.entity.iconKey || 'medium')
pr.iconEl.src = `/api/icons/type/${panelIcon}`
pr.iconEl.style.display = ''
}
}
pr.row.classList.toggle('rc-dead', st.dead)
pr.row.classList.toggle('rc-gone', st.gone)
if (pr.statusEl) pr.statusEl.textContent = st.dead || st.gone ? 'x' : ''
}
} }
_updateBattleLog() { _updateBattleLog() {
+1 -2
View File
@@ -524,8 +524,7 @@ h3 {
.rc-layout { .rc-layout {
display: grid; display: grid;
grid-template-columns: minmax(190px, 230px) minmax(560px, 720px) minmax(190px, 230px); grid-template-columns: minmax(160px, 200px) min(720px, 60vh, 92vw) minmax(160px, 200px);
width: min(100%, 1210px);
gap: 0.5rem; gap: 0.5rem;
align-items: start; align-items: start;
justify-content: center; justify-content: center;