import { LitElement, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; import { GameMode, Team, UnitType } from "../../../core/game/Game"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { renderNumber, translateText } from "../../Utils"; import { Layer } from "./Layer"; interface TeamEntry { teamName: string; totalScoreStr: string; totalGold: string; totalTroops: string; totalSAMs: string; totalLaunchers: string; totalWarShips: string; totalCities: string; totalScoreSort: number; players: PlayerView[]; } @customElement("team-stats") export class TeamStats extends LitElement implements Layer { public game: GameView; public eventBus: EventBus; @property({ type: Boolean }) visible = false; teams: TeamEntry[] = []; private _shownOnInit = false; private showUnits = false; createRenderRoot() { return this; // use light DOM for Tailwind } init() {} tick() { if (this.game.config().gameConfig().gameMode !== GameMode.Team) return; if (!this._shownOnInit && !this.game.inSpawnPhase()) { this._shownOnInit = true; this.updateTeamStats(); } if (!this.visible) return; if (this.game.ticks() % 10 === 0) { this.updateTeamStats(); } } private updateTeamStats() { const players = this.game.playerViews(); const grouped: Record = {}; for (const player of players) { const team = player.team(); if (team === null) continue; grouped[team] ??= []; grouped[team].push(player); } this.teams = Object.entries(grouped) .map(([teamStr, teamPlayers]) => { let totalGold = 0n; let totalTroops = 0; let totalScoreSort = 0; let totalSAMs = 0; let totalLaunchers = 0; let totalWarShips = 0; let totalCities = 0; for (const p of teamPlayers) { if (p.isAlive()) { totalTroops += p.troops(); totalGold += p.gold(); totalScoreSort += p.numTilesOwned(); totalLaunchers += p.totalUnitLevels(UnitType.MissileSilo); totalSAMs += p.totalUnitLevels(UnitType.SAMLauncher); totalWarShips += p.totalUnitLevels(UnitType.Warship); totalCities += p.totalUnitLevels(UnitType.City); } } const totalScorePercent = totalScoreSort / this.game.numLandTiles(); return { teamName: teamStr, totalScoreStr: formatPercentage(totalScorePercent), totalScoreSort, totalGold: renderNumber(totalGold), totalTroops: renderNumber(totalTroops / 10), players: teamPlayers, totalLaunchers: renderNumber(totalLaunchers), totalSAMs: renderNumber(totalSAMs), totalWarShips: renderNumber(totalWarShips), totalCities: renderNumber(totalCities), }; }) .sort((a, b) => b.totalScoreSort - a.totalScoreSort); this.requestUpdate(); } renderLayer(context: CanvasRenderingContext2D) {} shouldTransform(): boolean { return false; } render() { if (!this.visible) return html``; return html`
e.preventDefault()} >
${translateText("leaderboard.team")}
${this.showUnits ? html`
${translateText("leaderboard.launchers")}
${translateText("leaderboard.sams")}
${translateText("leaderboard.warships")}
${translateText("leaderboard.cities")}
` : html`
${translateText("leaderboard.owned")}
${translateText("leaderboard.gold")}
${translateText("leaderboard.troops")}
`}
${this.teams.map((team) => this.showUnits ? html`
${team.teamName}
${team.totalLaunchers}
${team.totalSAMs}
${team.totalWarShips}
${team.totalCities}
` : html`
${team.teamName}
${team.totalScoreStr}
${team.totalGold}
${team.totalTroops}
`, )}
`; } } function formatPercentage(value: number): string { const perc = value * 100; if (Number.isNaN(perc)) return "0%"; return perc.toPrecision(2) + "%"; }