diff --git a/web/views/game-detail.ejs b/web/views/game-detail.ejs index fea6327..0466265 100644 --- a/web/views/game-detail.ejs +++ b/web/views/game-detail.ejs @@ -339,6 +339,8 @@ .log-entry .bl-action { color: rgba(255,255,255,0.4); } .log-entry.chat-team { color: rgba(255, 255, 255, 0.7); } .log-entry.chat-all { color: rgba(255, 200, 100, 0.9); } + .log-entry.chat-team.chat-win { color: rgb(100, 255, 100); } + .log-entry.chat-team.chat-lose { color: rgb(255, 100, 100); } /* Loading state */ .loading-state { @@ -729,15 +731,55 @@ return div.innerHTML; } - function formatBattleLogLine(line) { + function normalizeSquadronTag(tag) { + return String(tag || '').replace(/^\[|\]$/g, '').trim(); + } + + function sameSquadronTag(a, b) { + const left = normalizeSquadronTag(a).toLowerCase(); + const right = normalizeSquadronTag(b).toLowerCase(); + return Boolean(left && right && left === right); + } + + function chatTeamClass(line, replay) { + if (!line.includes('[TEAM]')) return 'chat-all'; + const m = line.match(/^\[[^\]]*\]\s+\[TEAM\]\s+\[([^\]]*)\]/); + const squadron = m ? m[1] : ''; + if (sameSquadronTag(squadron, replay.winning_team_squadron)) return 'chat-team chat-win'; + if (sameSquadronTag(squadron, replay.losing_team_squadron)) return 'chat-team chat-lose'; + return 'chat-team'; + } + + function buildReplayPlayerSquadronMap(replay) { + const map = new Map(); + for (const team of replay.teams || []) { + const squadron = normalizeSquadronTag(team.squadron || team.squadron_tagged); + if (!squadron) continue; + for (const player of team.players || []) { + const nick = String(player.nick || '').trim(); + if (nick) map.set(nick, squadron); + } + } + return map; + } + + function addVictimSquadron(victim, playerSquadrons) { + if (/^\[[^\]]+\]\s+/.test(victim)) return victim; + const nameMatch = victim.match(/^(.+?)\s+\(/); + const victimName = nameMatch ? nameMatch[1].trim() : victim.trim(); + const squadron = playerSquadrons.get(victimName); + return squadron ? `[${squadron}] ${victim}` : victim; + } + + function formatBattleLogLine(line, playerSquadrons = new Map()) { // Format: +[TIME] [SQUAD] Player (Vehicle) ACTION [SQUAD] Player (Vehicle) // or: -[TIME] [SQUAD] Player (Vehicle) ACTION [SQUAD] Player (Vehicle) - const isWinnerAction = line.startsWith('+'); - const winCls = isWinnerAction ? 'bl-win' : 'bl-lose'; - const loseCls = isWinnerAction ? 'bl-lose' : 'bl-win'; // Parse: sign[TIME] rest const m = line.match(/^([+-])(\[[^\]]*\])\s+(.+)$/); if (!m) return escapeHtml(line); + const isWinnerAction = m[1] === '+'; + const winCls = isWinnerAction ? 'bl-win' : 'bl-lose'; + const loseCls = isWinnerAction ? 'bl-lose' : 'bl-win'; const time = m[2]; const rest = m[3]; // Split on action words @@ -748,7 +790,7 @@ const attacker = actionMatch[1]; const action = actionMatch[2]; const actionExtra = actionMatch[3] || ''; - const victim = actionMatch[4]; + const victim = addVictimSquadron(actionMatch[4], playerSquadrons); const actionText = actionExtra ? `${action} (${actionExtra})` : action; return `${escapeHtml(m[1] + time)} ` + `${escapeHtml(attacker)} ` @@ -967,7 +1009,7 @@ const chatBody = document.getElementById('chatLogBody'); if (replay.chat_log && replay.chat_log.length > 0) { chatBody.innerHTML = replay.chat_log.map(line => { - const cls = line.includes('[TEAM]') ? 'chat-team' : 'chat-all'; + const cls = chatTeamClass(line, replay); return `
${escapeHtml(line)}
`; }).join(''); } else { @@ -977,8 +1019,9 @@ // Battle log const battleBody = document.getElementById('battleLogBody'); if (replay.battle_log && replay.battle_log.length > 0) { + const playerSquadrons = buildReplayPlayerSquadronMap(replay); battleBody.innerHTML = replay.battle_log.map(line => { - return `
${formatBattleLogLine(line)}
`; + return `
${formatBattleLogLine(line, playerSquadrons)}
`; }).join(''); } else { battleBody.innerHTML = `
${__t('games.noBattleLog')}
`;