Batalla por x posición
9 · Sesión - Race
¿Cómo usar Batalla por Posición?
La Batalla por Posición es el gráfico que te deja aislar una pelea puntual: “mostrame la lucha por la posición 5 entre 3 autos”. No es la tabla completa ni el Totem completo: es un rótulo chico con 2 a 10 pilotos del rango que elijas, con pos, número, nombre (formato Totem), marca y diferencia al puntero de la batalla. Tiene dos modos independientes que podés usar juntos o separados: 1) Gráfico Batalla (rótulo #ov-batalla) y 2) Expandir en Totem (resalta y expande ese rango dentro del Totem). Vos definís posición inicial y cantidad, y el sistema sigue en vivo quién está en esas posiciones, aunque cambien los pilotos.
1. Antes de empezar
Dos modos, mismo rango
En el modal elegís pos y cant una sola vez, pero tenés dos switches: 📊 Gráfico Batalla por Pos (rótulo) y 🗼 Expandir en Totem. Podés prender solo rótulo, solo Totem, o ambos. Cada uno tiene su endpoint y su socket independiente.
Dónde vive el estado
Server: state["batalla"] = {active, pos, cant, pilotos} y state["batalla_totem"] = {active, pos, cant}. APIs: GET /api/batalla, POST /api/batalla {pos,cant,pilotos}, POST /api/batalla/hide y GET /api/batalla_totem, POST /api/batalla_totem {active,pos,cant}, POST /api/batalla_totem/hide. Sockets: batalla_show / batalla_hide y batalla_totem_show / batalla_totem_hide. Poll de respaldo cada 800ms (10s si 404).
Qué se muestra en el rótulo
#ov-batalla con header bat-head traducible (LBL_BATALLA_POR_POSICION_HEADER → “BATALLA POR POS” + pos en disputa). Filas con t-pos, t-no (nro con foto), t-name (formato Totem corte), ts-logo (marca), t-time (dif al puntero de la batalla). Primer fila = puntero de la batalla, sin tiempo si es líder real, si no con su dif al líder real. Siguientes = gap al puntero de la batalla calculado con battleParseGapSec / battleFormatGapSec (difference - battleLeaderSec).
Qué hace en el Totem
Cuando prendés “Expandir en Totem”, el Totem agrega: fila-leyenda “BATALLA POR X POS” (bat-leg) arriba de la primera fila del rango, gap expandido entre filas (bat-spacer) con diferencia y flecha de tendencia (bt-up verde si se acerca, bt-down rojo si se aleja, bt-same amarillo si igual, con _BAT_GAP_EPS 0.0005s), borde de acento bat-sel/bat-first/bat-last y animación de expandir/contraer (2160ms in, 2880ms out). El alto del Totem se recalcula con _batTotemGapPx + _batRowExtraPx + _batLegPx.
2. Dónde están los botones
Screenshots/batalla-botones.pngBloque con botón ⚔ BATALLA POR POS, Mostrar/Ocultar, Apagar, status “sin batalla”.PLAY → ⚔ Batalla por Posición
Sección nueva en PLAY con 3 botones: ⚔ BATALLA POR POS (abre modal), 👁 MOSTRAR / OCULTAR BATALLA (toggle del rótulo), ⏻ APAGAR BATALLA (hide). Abajo #batalla-status con estado combinado: “⚔ Batalla en aire” + “🗼 Totem: Pos 5-7” o “— sin batalla —”.
Modal de configuración
Overlay #batalla-pos-modal-overlay con dos switches (rotulo ON/OFF, totem ON/OFF), grid de posiciones 1-9 (botones rápidos), input number pos (1-99), input cant rivales (1-10) + botones rápidos 2/3/4/5 (.bat-cant-q con clase on), info Pos: X | Rivales: Y | Rango: X-Y + aviso “⚠ sin aplicar (tocá APLICAR RANGO)” si cambiaste rango pero no lo aplicaste, botones Cerrar y 📡 APLICAR RANGO (envía a los modos que estén en ON).
Flujo de envío
Aplicar Rango llama a enviarBatallaPos(): si ningún modo en ON no hace nada. Si rotulo ON → _batSendRotulo() (resuelve pilotos con _batResolvePilotos(pos,cant) desde currentData.all_competitors o grilla activa, arma payload y POST /api/batalla). Si totem ON → _batSendTotem() (POST /api/batalla_totem {active:true,pos,cant} + GET de verificación). Switches individuales onBatallaSwitch(kind, el): si prendés rotulo manda rótulo, si apagás hace hide. Totem igual con /hide.
3. Cómo se usa — paso a paso
4. Qué muestra cada modo al aire
Screenshots/batalla-layout.pngRótulo con BATALLA POR POS 5, 3 filas con pos/nro/nombre/marca/dif, y Totem con BATALLA POR 5 POS + gaps con flechas.Rótulo #ov-batalla
Header traducible + filas totem-row con: posición actual, nro con foto (nroImgTag), nombre con formatNombreConfigurable(c, 'totem', 'corte', ...) (usa mismo formato que Totem, corte 4 por default), logo marca, tiempo/dif. Tiempo: si es primer fila y es líder real → vacío, si no → su dif al líder real. Si es segunda en adelante → dif al puntero de la batalla (diff - battleLeaderSec) formateado con battleFormatGapSec. Si _staleDif → dif-stale 50%. Animación pos-up/dn.
Totem expandido
Cuando activo, totemBuildData agrega: leyenda arriba de primera fila del rango con texto RG_T('LBL_BATALLA_TOTEM_HEADER','BATALLA POR {X} POS') reemplazando {X} por pos, spacers entre filas del rango con gap (battleParseGapSec difference) y tendencia (_batGapTrend: compara sec actual vs anterior, si sube bt-up, si baja bt-down, si igual bt-same con eps 0.0005s), clases bat-sel en filas del rango, bat-first en primera, bat-last en última, borde de acento en CSS. Animación: _BAT_EXP_IN_MS 2160ms expandir, _BAT_EXP_OUT_MS 2880ms contraer, con candado _batAnimLocked para no redibujar a mitad de animación.
Traducción
Títulos traducibles en 4 idiomas: es BATALLA POR POS / BATALLA POR {X} POS, en BATTLE FOR POS, pt BATALHA PELA POS, it BATTAGLIA PER POS. Claves LBL_BATALLA_POR_POSICION_HEADER y LBL_BATALLA_TOTEM_HEADER. Si no hay traducción, usa fallback.
5. Parámetros a tener en cuenta
| Parámetro | Default | Qué hace |
|---|---|---|
| pos | 1 | Posición inicial del rango. 1-99. Si ponés 1, batalla por punta. Header muestra “BATALLA POR POS 1”. |
| cant | 3 | Cantidad de pilotos en la batalla. 1-10. Rango = pos a pos+cant-1. Ej: pos5 cant3 = 5,6,7. Botones rápidos 2/3/4/5. |
| active | false | Si true, el modo está al aire. Hay dos actives independientes: batalla.active y batalla_totem.active. |
| pilotos (solo rótulo) | [] | Referencia enviada al prender rótulo, pero layout ignora lista vieja y lee clasificación en vivo en cada render. Sirve solo para mensaje “En aire: Pos 3 +3 (10,22,15)”. |
| _BAT_GAP_EPS | 0.0005s | Tolerancia para considerar gap igual. Si diferencia de gap entre polls <0.5ms → bt-same amarillo. |
| _BAT_EXP_IN_MS / OUT | 2160 / 2880 ms | Duración animación expandir/contraer Totem + margen. Durante ese tiempo Totem no se redibuja (candado). |
6. Beneficios de usar batalla por posición
Foco donde importa
En carrera con 30 autos, la tabla completa distrae. Batalla por pos 5 te deja contar historia de 3 autos que pelean sin mostrar 30 filas. Ideal para recta final.
Gap relativo a la pelea
En tabla normal ves gap al líder. En batalla ves gap al puntero de la batalla: si pelean por pos 5, ves +0.3, +0.8 respecto al 5to, no +45s al líder. Mucho más útil para narrar lucha.
Totem expandido con tendencia
En Totem expandido ves flecha si se acerca (verde bt-up) o se aleja (rojo bt-down). Contás si la pelea se cierra o se abre sin decir números.
Vivo, no foto fija
Si hay sobrepaso, el rótulo se reordena solo y anima pos-up/dn. No tenés que re-enviar batalla cada vez que cambian posiciones: el rango se sigue solo.
7. Precauciones
Rango sin pilotos. Si ponés pos 50 con 30 autos, liveRows queda vacío y muestra “Sin pilotos en ese rango”. No es error: es que no hay nadie ahí. Cuando entren datos o cambie clasificación, se completa solo.
Dos modos independientes. Podés tener rótulo prendido y Totem apagado, o al revés. El status combina ambos. Si apagás solo rótulo con toggle, Totem puede seguir prendido. Usá “Apagar Batalla” para apagar todo.
Sin aplicar. Si cambiás pos/cant en modal pero no tocás Aplicar Rango, la info muestra “⚠ sin aplicar”. El rango al aire sigue siendo el viejo (_batAired). Tocá Aplicar para que llegue al layout.
Totem y animación. Durante animación expandir/contraer (2-3s) el Totem no se redibuja (_batAnimLocked). Si llega dato nuevo en ese momento, queda pendiente y se dibuja al terminar ( _batAnimPending → _batTotemRedraw ). No toques otro rango durante animación o se pisa (_batAnimSeq invalida).
Pos 1 especial. Si pos=1, el primer fila es líder real y su tiempo queda vacío (no muestra dif a sí mismo). Las siguientes muestran dif a líder real si es pos 1, o dif al puntero de batalla si pos>1. Es intencional para no mostrar +0.0.
Formato de nombre usa Totem. El rótulo de batalla usa formato de nombres de Totem (corte 4 por default). Si querés nombre más largo en batalla, cambiá --nombre-totem a abreviado o inicial_apellido en Editor Nombres.
Grilla sin carrera. _batResolvePilotos intenta primero con currentData.all_competitors, si no hay (sin crono) busca grilla activa vía /api/grilla_data. Si tampoco hay grilla, pilotos=[] y manda igual pos/cant: el layout queda esperando datos y muestra aviso “Todavía no hay pilotos en ese rango”.
API vieja sin /api/batalla_totem. Si el servidor no tiene endpoint, el poll de respaldo baja a 10s y el POST devuelve 404 con toast “falta el endpoint”. Actualizá server.py.
8. Problemas frecuentes
| Lo que ves | Qué hacer |
|---|---|
| Puse Pos 5 x3 pero al aire salen otros autos | Correcto: rango es vivo. Si hubo sobrepaso, ahora en 5,6,7 hay otros. No es bug, es pelea real. Si querés pilotos fijos, no uses batalla por posición, usa Comparativa de Pilotos. |
| Rótulo vacío “Sin pilotos en ese rango” | Rango fuera de cantidad de autos (pos 50 con 30 autos) o crono sin datos todavía. Esperá a que llegue clasificación o bajá pos. |
| Totem no expande | Verificá que switch Totem esté ON y que hayas tocado Aplicar Rango. Mirá consola: POST /api/batalla_totem debe devolver {ok:true, active:true}. Si 404, actualizá server.py. |
| Cambié pos en modal y no cambia al aire | Te faltó Aplicar Rango. El modal marca “⚠ sin aplicar” cuando _batAired difiere de edición. Tocá 📡 APLICAR RANGO. |
| Gap no cambia o flecha no sale | _batGapTrend compara sec actual vs anterior. Si difference viene como “1 Vuelta” o vacío, battleParseGapSec devuelve null y no hay tendencia. Necesitás que crono mande difference en segundos para que funcione gap y flechas. |
| Animación se corta | Si prendés y apagás rápido batalla en Totem, _batAnimSeq invalida animación anterior. Esperá 3s entre prender/apagar. |
| Nombre muy corto en batalla (CORR) | Usa formato Totem: por default es corte 4. Cambiá en Editor Visual → NOMBRES → Totem → inicial_apellido o abreviado para que batalla muestre “M. CORREA”. |
| Apagué rótulo pero Totem sigue expandido | Son independientes. Toggle de rótulo no apaga Totem. Usá “Apagar Batalla” que hace hide en ambos endpoints, o apagá switch Totem y Aplicar. |
9. Lista de control rápida
Al preparar el evento
Verificá que server.py tenga /api/batalla y /api/batalla_totem. Probá modal con pos 1 cant 3 y ambos modos ON.
Antes de salir al aire
Posicioná #ov-batalla en Editor Visual (tamaño, lugar). Probá Totem expandido: ¿leyenda “BATALLA POR X POS” entra? ¿gaps con flechas se leen? Ajustá --h-totem-batalla-gap si hace falta.
Durante la transmisión
PLAY → Batalla por Pos → elegí pelea caliente (ej: 3 autos por P5) → pos 5 cant 3 → prender Gráfico y Totem → Aplicar. Narrá gap al puntero de la batalla, no al líder. Cuando pelea se resuelve, Apagar Batalla.
Al terminar
Estado no se guarda: al reiniciar server vuelve a active false. No hace falta backup.