Add dynamic sorting to leaderboard by tiles, gold, and troops (#961)

### Description:
This PR adds dynamic sorting to the in-game leaderboard. Players can now
sort the leaderboard by tiles owned (default), gold, or troops by
clicking the respective column headers. The currently selected sort is
indicated with a downward arrow (⬇️). This improves usability and makes
it easier to compare player stats mid-game.

### Please complete the following:
[] I have added screenshots for all UI updates


https://github.com/user-attachments/assets/d0238698-a187-4fc7-9407-04d11cccbc32

[] I process any text displayed to the user through translateText() and
I've added it to the en.json file

[] I have added relevant tests to the test directory

[] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

[] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

Closes #923

---------

Co-authored-by: evanpelle <openfrontio@gmail.com>
Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
Erwan Huon
2025-06-02 20:20:48 +02:00
committed by GitHub
parent 2c31d99521
commit 19bf71e024
+59 -6
View File
@@ -44,6 +44,12 @@ export class Leaderboard extends LitElement implements Layer {
private _shownOnInit = false;
private showTopFive = true;
@state()
private _sortKey: "tiles" | "gold" | "troops" = "tiles";
@state()
private _sortOrder: "asc" | "desc" = "desc";
init() {}
tick() {
@@ -62,13 +68,37 @@ export class Leaderboard extends LitElement implements Layer {
}
}
private setSort(key: "tiles" | "gold" | "troops") {
if (this._sortKey === key) {
this._sortOrder = this._sortOrder === "asc" ? "desc" : "asc";
} else {
this._sortKey = key;
this._sortOrder = "desc";
}
this.updateLeaderboard();
}
private updateLeaderboard() {
if (this.game === null) throw new Error("Not initialized");
const myPlayer = this.game.myPlayer();
const sorted = this.game
.playerViews()
.sort((a, b) => b.numTilesOwned() - a.numTilesOwned());
let sorted = this.game.playerViews();
const compare = (a: number, b: number) =>
this._sortOrder === "asc" ? a - b : b - a;
switch (this._sortKey) {
case "gold":
sorted = sorted.sort((a, b) => compare(a.gold(), b.gold()));
break;
case "troops":
sorted = sorted.sort((a, b) => compare(a.troops(), b.troops()));
break;
default:
sorted = sorted.sort((a, b) =>
compare(a.numTilesOwned(), b.numTilesOwned()),
);
}
const numTilesWithoutFallout =
this.game.numLandTiles() - this.game.numTilesWithFallout();
@@ -174,6 +204,8 @@ export class Leaderboard extends LitElement implements Layer {
th {
background-color: rgb(31 41 55 / 0.5);
color: white;
cursor: pointer;
user-select: none;
}
.myPlayer {
font-weight: bold;
@@ -275,9 +307,30 @@ export class Leaderboard extends LitElement implements Layer {
<tr>
<th>${translateText("leaderboard.rank")}</th>
<th>${translateText("leaderboard.player")}</th>
<th>${translateText("leaderboard.owned")}</th>
<th>${translateText("leaderboard.gold")}</th>
<th>${translateText("leaderboard.troops")}</th>
<th @click=${() => this.setSort("tiles")}>
${translateText("leaderboard.owned")}
${this._sortKey === "tiles"
? this._sortOrder === "asc"
? "⬆️"
: "⬇️"
: ""}
</th>
<th @click=${() => this.setSort("gold")}>
${translateText("leaderboard.gold")}
${this._sortKey === "gold"
? this._sortOrder === "asc"
? "⬆️"
: "⬇️"
: ""}
</th>
<th @click=${() => this.setSort("troops")}>
${translateText("leaderboard.troops")}
${this._sortKey === "troops"
? this._sortOrder === "asc"
? "⬆️"
: "⬇️"
: ""}
</th>
</tr>
</thead>
<tbody>