import { LitElement, css, html } from "lit"; import { customElement, state } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; import { GameMode } from "../../../core/game/Game"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { ClientID } from "../../../core/Schemas"; import { renderNumber } from "../../Utils"; import { Layer } from "./Layer"; interface TeamEntry { teamName: string; totalScoreStr: string; totalGold: string; totalTroops: string; players: PlayerView[]; } @customElement("team-stats") export class TeamStats extends LitElement implements Layer { public game: GameView; public clientID: ClientID; public eventBus: EventBus; teams: TeamEntry[] = []; @state() private _teamStatsHidden = true; private _shownOnInit = false; init() {} tick() { if (this.game.config().gameConfig().gameMode !== GameMode.Team) { return; } if (!this._shownOnInit && !this.game.inSpawnPhase()) { this._shownOnInit = true; this._teamStatsHidden = false; this.updateTeamStats(); } if (this._teamStatsHidden) 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; if (!grouped[team]) grouped[team] = []; grouped[team].push(player); } this.teams = Object.entries(grouped) .map(([teamStr, teamPlayers]) => { let totalGold = 0; let totalTroops = 0; let totalScoreSort = 0; for (const p of teamPlayers) { totalGold += p.gold(); if (p.isAlive()) { totalTroops += p.troops(); totalGold += p.gold(); totalScoreSort += p.numTilesOwned(); } } const totalScorePercent = totalScoreSort / this.game.numLandTiles(); return { teamName: teamStr, totalScoreStr: formatPercentage(totalScorePercent), totalScoreSort, totalGold: renderNumber(totalGold), totalTroops: renderNumber(totalTroops / 10), players: teamPlayers, }; }) .sort((a, b) => b.totalScoreSort - a.totalScoreSort); this.requestUpdate(); } renderLayer(context: CanvasRenderingContext2D) {} shouldTransform(): boolean { return false; } static styles = css` :host { display: block; } .team-stats { position: fixed; top: 10px; left: 450px; z-index: 9999; background-color: rgb(31 41 55 / 0.7); padding: 10px; padding-top: 0px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); border-radius: 10px; max-width: 250px; max-height: 30vh; overflow-y: auto; width: 400px; backdrop-filter: blur(5px); } .teamStats-close-button { background: none; border: none; color: white; cursor: pointer; } table { width: 100%; border-collapse: collapse; } th, td { padding: 5px; text-align: center; border-bottom: 1px solid rgba(51, 51, 51, 0.2); color: var(--text-color, white); } th { background-color: rgb(31 41 55 / 0.5); color: white; } .hidden { display: none !important; } .team-stats-button { position: fixed; left: 450px; top: 10px; z-index: 9999; background-color: rgb(31 41 55 / 0.7); color: white; border: none; border-radius: 4px; padding: 5px 10px; cursor: pointer; } `; render() { return html`
e.preventDefault()} > ${this.teams.map( (team) => html` `, )}
Team Owned Gold Troops
${team.teamName} ${team.totalScoreStr} ${team.totalGold} ${team.totalTroops}
`; } toggleTeamStats() { this._teamStatsHidden = !this._teamStatsHidden; } hideTeamStats() { this._teamStatsHidden = true; this.requestUpdate(); } showTeamStats() { this._teamStatsHidden = true; this.requestUpdate(); } get isVisible() { return !this._teamStatsHidden; } } function formatPercentage(value: number): string { const perc = value * 100; if (perc > 99.5) { return "100%"; } if (perc < 0.01) { return "0%"; } if (perc < 0.1) { return perc.toPrecision(1) + "%"; } return perc.toPrecision(2) + "%"; }