add WL to tss scoreboards (#1345)

This commit is contained in:
NotSoToothless
2026-06-20 18:55:17 -07:00
committed by GitHub
parent f855e289c8
commit b665eb118f
5 changed files with 327 additions and 26 deletions
+51 -16
View File
@@ -225,14 +225,20 @@ def _create_scoreboard_sync(model: dict[str, Any], output_path: str, bar_color:
name_x = start_x if not flipped else start_x + section_width - name_w
draw.text((name_x, header_y), team_name, font=fonts["team"], fill=header_fill)
# W/L slot — only drawn once real per-tournament standings exist (stub returns
# None today, so nothing renders under the team name).
standings = get_tss_standings(team.get("team_id"))
# W/L slot — per-tournament record under the team name. Coloured exactly like
# SREBOT: green W · grey dash · red L · grey dash · win-rate% on a red→green
# gradient. Placed below the team name's full glyph box (descender-inclusive)
# so names with p/g/y descenders don't clip into the record line.
standings = get_tss_standings(team.get("team_id"), model.get("tournament_id"))
if standings:
wl_text = f"{standings['wins']}W - {standings['losses']}L"
wb = draw.textbbox((0, 0), wl_text, font=fonts["sub"])
wl_x = start_x if not flipped else start_x + section_width - (wb[2] - wb[0])
draw.text((wl_x, header_y + (nb[3] - nb[1]) + 18), wl_text, font=fonts["sub"], fill=GREY_FILL)
wl_segs = _winloss_segments(standings)
seg_w = _segments_width(draw, wl_segs, fonts["sub"])
wl_x = start_x if not flipped else start_x + section_width - seg_w
# Sit halfway between the original (descender-clipping) baseline and a
# full descender-clear drop — enough to clear p/g/y, no more.
name_full_h = fonts["team"].getbbox("ApgjyQ")[3]
wl_y = header_y + ((nb[3] - nb[1] + 18) + (name_full_h + 10)) // 2
_draw_segments(draw, wl_x, wl_y, wl_segs, fonts["sub"])
# Stat columns (rating now lives inline next to the player name).
col_labels = ["Air", "Ground", "Assists", "Deaths", "Caps"]
@@ -270,7 +276,7 @@ def _create_scoreboard_sync(model: dict[str, Any], output_path: str, bar_color:
pass
# Player rows. Layout per row:
# top line — player name + inline (rating ±delta) at the column edge; stats
# top line — player name + inline (rating) at the column edge; stats
# lower line — vehicle icons next to their translated names
body = fonts["body"]
small = fonts["veh"]
@@ -287,7 +293,7 @@ def _create_scoreboard_sync(model: dict[str, Any], output_path: str, bar_color:
veh_y = line_y + name_h + 12 # vehicle line (icons + names together)
vname_y = veh_y + (veh_icon - small_h) // 2
name_w = draw.textbbox((0, 0), name, font=body)[2]
rating_segs = _rating_segments(p.get("pvp_ratio"), p.get("pvp_ratio_delta"))
rating_segs = _rating_segments(p.get("pvp_ratio"))
rating_w = _segments_width(draw, rating_segs, body)
if not flipped:
@@ -342,16 +348,45 @@ def _create_scoreboard_sync(model: dict[str, Any], output_path: str, bar_color:
RATING_FILL = (170, 200, 255, 255)
def _rating_segments(rating, delta):
"""Build colored (text, fill) segments for the inline ``(1468 +2)`` rating.
# W/L record colours — matched exactly to SREBOT's scoreboard.
WL_WIN_FILL = (0, 255, 0, 255)
WL_LOSS_FILL = (255, 60, 60, 255)
WL_DASH_FILL = (200, 200, 200, 255)
``delta`` is a signed change vs the player's previous rating; it's ``None`` until
rating history is tracked (see wl.py), so for now only the current value shows.
def _winrate_gradient(win_rate: float) -> tuple:
"""Red→yellow→green gradient for a win-rate percentage (SREBOT's get_gradient_color).
0% = red (255,0,0), 50% = yellow (255,255,0), 100% = green (0,255,0).
"""
win_rate = max(0.0, min(100.0, win_rate))
if win_rate <= 50:
return (255, int(255 * (win_rate / 50)), 0, 255)
return (int(255 * (1 - (win_rate - 50) / 50)), 255, 0, 255)
def _winloss_segments(standings: dict[str, int]) -> list[tuple[str, tuple]]:
"""Colored ``3W - 2L - 60%`` segments for the per-tournament record line.
Win count + 'W' green, ' - ' grey, loss count + 'L' red, ' - ' grey, then the
win-rate% on the red→green gradient — mirroring SREBOT's _draw_winloss.
"""
wins = int(standings.get("wins") or 0)
losses = int(standings.get("losses") or 0)
total = wins + losses
pct = (wins / total) * 100 if total else 0.0
return [
(f"{wins}W", WL_WIN_FILL),
(" - ", WL_DASH_FILL),
(f"{losses}L", WL_LOSS_FILL),
(" - ", WL_DASH_FILL),
(f"{pct:.0f}%", _winrate_gradient(pct)),
]
def _rating_segments(rating):
"""Build colored (text, fill) segments for the inline ``(1468)`` rating."""
rating_str = f"{int(round(rating))}" if isinstance(rating, (int, float)) else ""
if isinstance(delta, (int, float)) and delta != 0:
dcol = WIN_FILL if delta > 0 else LOSS_FILL
return [(f"({rating_str} ", RATING_FILL), (f"{'+' if delta > 0 else ''}{int(delta)}", dcol), (")", RATING_FILL)]
return [(f"({rating_str})", RATING_FILL)]