diff --git a/frontend/assets/css/historico.css b/frontend/assets/css/historico.css index 2d19c88..1a3d407 100644 --- a/frontend/assets/css/historico.css +++ b/frontend/assets/css/historico.css @@ -591,16 +591,16 @@ .current-match-killfeed-screen { position: relative; - width: min(100%, 760px); + width: 100%; min-height: 180px; max-height: 520px; padding: 10px; overflow: hidden; - border: 1px solid rgba(183, 201, 125, 0.28); + border: 1px solid rgba(159, 168, 141, 0.2); border-radius: 6px; background: - linear-gradient(180deg, rgba(183, 201, 125, 0.08), transparent 24%), - rgba(5, 8, 6, 0.92); + linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 24%), + rgba(7, 9, 9, 0.94); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.44), inset 0 18px 48px rgba(0, 0, 0, 0.24); @@ -626,34 +626,41 @@ position: relative; z-index: 2; display: grid; - gap: 6px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: start; + gap: 8px; max-height: 500px; overflow: hidden; } +.current-match-killfeed__column { + display: grid; + align-content: start; + gap: 6px; + min-width: 0; +} + .current-match-killfeed__row { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(92px, 124px) minmax(0, 1fr); + grid-template-columns: minmax(0, 1fr) minmax(86px, 112px) minmax(0, 1fr); align-items: center; gap: 8px; - min-height: 48px; + min-height: 54px; padding: 6px 9px; overflow: hidden; - border: 1px solid rgba(159, 168, 141, 0.12); - border-left: 2px solid rgba(183, 201, 125, 0.46); + border: 1px solid rgba(159, 168, 141, 0.14); + border-left: 2px solid rgba(159, 168, 141, 0.24); border-radius: 3px; color: var(--text); - background: - linear-gradient(90deg, rgba(183, 201, 125, 0.07), transparent 40%), - rgba(15, 20, 13, 0.68); + background: rgba(19, 22, 22, 0.78); animation: current-match-killfeed-enter 180ms ease-out; } -.current-match-killfeed__row:first-child { - border-color: rgba(183, 201, 125, 0.28); +.current-match-killfeed__column:last-child .current-match-killfeed__row:last-child { + border-color: rgba(210, 182, 118, 0.22); background: - linear-gradient(90deg, rgba(183, 201, 125, 0.14), transparent 46%), - rgba(20, 27, 17, 0.86); + linear-gradient(90deg, rgba(210, 182, 118, 0.08), transparent 46%), + rgba(24, 25, 22, 0.92); } .current-match-killfeed__row.is-teamkill { @@ -664,22 +671,36 @@ } .current-match-killfeed__player { + display: grid; + align-content: center; + gap: 3px; min-width: 0; - overflow: hidden; font-size: 0.88rem; line-height: 1.2; +} + +.current-match-killfeed__player-name { + min-width: 0; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .current-match-killfeed__player--killer { - color: var(--accent-strong); + color: var(--text); } .current-match-killfeed__player--victim { color: var(--text-soft); } +.current-match-killfeed__player-meta { + display: flex; + align-items: center; + gap: 4px; + min-width: 0; +} + .current-match-killfeed__weapon { display: grid; grid-template-rows: 25px auto; @@ -689,10 +710,10 @@ min-width: 92px; min-height: 38px; padding: 2px 5px; - border: 1px solid rgba(183, 201, 125, 0.18); + border: 1px solid rgba(159, 168, 141, 0.18); border-radius: 3px; color: var(--text); - background: rgba(7, 10, 7, 0.72); + background: rgba(8, 10, 11, 0.74); } .current-match-killfeed__weapon-icon { @@ -733,19 +754,59 @@ white-space: nowrap; } +.current-match-killfeed__team-badge, .current-match-killfeed__teamkill { display: inline-flex; align-items: center; - margin-left: 4px; - min-height: 20px; + justify-content: center; + min-height: 18px; padding: 0 5px; - border: 1px solid rgba(210, 182, 118, 0.36); border-radius: 4px; - color: var(--accent-warm); font-size: 0.62rem; font-weight: 800; } +.current-match-killfeed__team-badge { + border: 1px solid rgba(159, 168, 141, 0.2); + color: var(--text-soft); + background: rgba(159, 168, 141, 0.08); +} + +.current-match-killfeed__team-badge--allies { + border-color: rgba(100, 139, 178, 0.34); + color: rgba(192, 215, 238, 0.96); + background: rgba(67, 101, 137, 0.28); +} + +.current-match-killfeed__team-badge--axis { + border-color: rgba(167, 109, 96, 0.36); + color: rgba(231, 198, 190, 0.96); + background: rgba(116, 68, 58, 0.3); +} + +.current-match-killfeed__team-badge--unknown { + border-color: rgba(159, 168, 141, 0.22); + color: var(--muted); + background: rgba(159, 168, 141, 0.1); +} + +.current-match-killfeed__teamkill { + border: 1px solid rgba(210, 182, 118, 0.36); + color: var(--accent-warm); +} + +.current-match-player-count { + width: fit-content; + margin: -6px 0 16px; + padding: 7px 10px; + border: 1px solid rgba(159, 168, 141, 0.16); + border-radius: 6px; + color: var(--text-soft); + background: rgba(14, 17, 18, 0.7); + font-size: 0.8rem; + font-weight: 700; +} + @keyframes current-match-killfeed-enter { from { opacity: 0; @@ -977,6 +1038,7 @@ } .current-match-killfeed { + grid-template-columns: 1fr; max-height: 500px; } @@ -997,7 +1059,7 @@ padding-inline: 3px; } - .current-match-killfeed__player { + .current-match-killfeed__player-name { display: -webkit-box; overflow: hidden; font-size: 0.78rem; diff --git a/frontend/assets/js/partida-actual.js b/frontend/assets/js/partida-actual.js index aa3ddb0..ba08361 100644 --- a/frontend/assets/js/partida-actual.js +++ b/frontend/assets/js/partida-actual.js @@ -9,7 +9,7 @@ const CURRENT_MATCH_SCOREBOARDS = Object.freeze({ "comunidad-hispana-01": "https://scoreboard.comunidadhll.es", "comunidad-hispana-02": "https://scoreboard.comunidadhll.es:5443", }); -const CURRENT_MATCH_KILL_FEED_LIMIT = 15; +const CURRENT_MATCH_KILL_FEED_LIMIT = 18; const CURRENT_MATCH_WEAPONS = Object.freeze({ "m1 garand": { label: "M1 Garand", icon: "m1_garand.PNG" }, mp40: { label: "MP40", icon: "mp40.PNG" }, @@ -225,11 +225,15 @@ function initializePlayerStats(nodes) {

Cargando estadisticas en vivo...

+

+ Jugadores detectados: 0 +

`, ); } nodes.playerStatsState = document.getElementById("current-match-player-stats-state"); + nodes.playerCount = document.getElementById("current-match-player-count"); nodes.playerStatsShell = document.getElementById("current-match-player-stats-shell"); return { visibleSignature: "", @@ -249,9 +253,9 @@ function renderKillFeed(data, nodes, state) { }); const events = [...state.byId.values()] .sort(compareKillFeedEvents) - .slice(0, CURRENT_MATCH_KILL_FEED_LIMIT); + .slice(-CURRENT_MATCH_KILL_FEED_LIMIT); state.byId = new Map(events.map((event) => [event.event_id, event])); - state.latestEventId = events[0]?.event_id || state.latestEventId; + state.latestEventId = events[events.length - 1]?.event_id || state.latestEventId; if (events.length === 0) { nodes.feedList.innerHTML = ""; state.visibleSignature = ""; @@ -261,7 +265,7 @@ function renderKillFeed(data, nodes, state) { const visualEvents = events; const visibleSignature = visualEvents.map((event) => event.event_id).join("|"); if (visibleSignature !== state.visibleSignature) { - nodes.feedList.innerHTML = visualEvents.map(renderKillFeedRow).join(""); + nodes.feedList.innerHTML = renderKillFeedColumns(visualEvents); state.visibleSignature = visibleSignature; } nodes.feedState.textContent = formatKillFeedCoverage(data.scope); @@ -269,16 +273,34 @@ function renderKillFeed(data, nodes, state) { } function compareKillFeedEvents(left, right) { - const rightTime = Number(right.server_time); const leftTime = Number(left.server_time); - if (Number.isFinite(rightTime) && Number.isFinite(leftTime) && rightTime !== leftTime) { - return rightTime - leftTime; + const rightTime = Number(right.server_time); + if (Number.isFinite(leftTime) && Number.isFinite(rightTime) && leftTime !== rightTime) { + return leftTime - rightTime; } - return String(right.event_timestamp || "").localeCompare(String(left.event_timestamp || "")); + return ( + String(left.event_timestamp || "").localeCompare(String(right.event_timestamp || "")) || + String(left.event_id || "").localeCompare(String(right.event_id || "")) + ); +} + +function renderKillFeedColumns(events) { + const splitIndex = Math.ceil(events.length / 2); + return [events.slice(0, splitIndex), events.slice(splitIndex)] + .map( + (columnEvents) => ` +
+ ${columnEvents.map(renderKillFeedRow).join("")} +
+ `, + ) + .join(""); } function renderKillFeedRow(event) { const weapon = resolveKillFeedWeapon(event.weapon); + const killerTeam = getKillFeedTeamDisplay(event.killer_team); + const victimTeam = getKillFeedTeamDisplay(event.victim_team); const teamkillBadge = event.is_teamkill ? 'TK' : ""; @@ -287,10 +309,15 @@ function renderKillFeedRow(event) { class="current-match-killfeed__row${event.is_teamkill ? " is-teamkill" : ""}" data-event-id="${escapeHtml(event.event_id || "")}" > - - ${escapeHtml(event.killer_name || "Jugador no disponible")} - ${teamkillBadge} - + + + ${escapeHtml(event.killer_name || "Jugador no disponible")} + + + ${renderKillFeedTeamBadge(killerTeam)} + ${teamkillBadge} + + ${escapeHtml(weapon.label)} - ${escapeHtml(event.victim_name || "Objetivo no disponible")} + + ${escapeHtml(event.victim_name || "Objetivo no disponible")} + + ${renderKillFeedTeamBadge(victimTeam)} `; } +function getKillFeedTeamDisplay(value) { + const team = getPlayerTeamDisplay(value); + return team.key === "unknown" ? { key: "unknown", label: "N/D" } : team; +} + +function renderKillFeedTeamBadge(team) { + return ` + + ${escapeHtml(team.label)} + + `; +} + function resolveKillFeedWeapon(value) { const key = normalizeLookupText(value); return CURRENT_MATCH_WEAPONS[key] || { @@ -335,6 +378,7 @@ function renderKillFeedWeaponIcon(weapon) { function renderPlayerStats(data, nodes, state) { const items = Array.isArray(data.items) ? sortPlayerStats(data.items) : []; + renderDetectedPlayerCount(items.length, nodes); if (items.length === 0) { state.visibleSignature = ""; nodes.playerStatsShell.innerHTML = ""; @@ -367,6 +411,12 @@ function renderPlayerStats(data, nodes, state) { setState(nodes.playerStatsState, "Estadisticas parciales derivadas de eventos recientes."); } +function renderDetectedPlayerCount(count, nodes) { + if (nodes.playerCount) { + nodes.playerCount.textContent = `Jugadores detectados: ${count}`; + } +} + function sortPlayerStats(items) { return [...items].sort( (left, right) =>