refactor cosmetics out of PlayerInfo (#1299)

## Description:

Remove Cosmetics from PlayerInfo. The game engine should have no
knowledge of cosmetics since they shouldn't affect game play at all.
Instead pass player cosmetics into the GameView.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
This commit is contained in:
evanpelle
2025-06-28 12:33:19 -07:00
committed by GitHub
parent 9dcceefc33
commit ca522a5937
23 changed files with 64 additions and 186 deletions
+2 -1
View File
@@ -150,9 +150,10 @@ export async function createClientGame(
const gameView = new GameView(
worker,
config,
gameMap.gameMap,
gameMap,
lobbyConfig.clientID,
lobbyConfig.gameStartInfo.gameID,
lobbyConfig.gameStartInfo.players,
);
console.log("going to init path finder");
+2 -2
View File
@@ -198,8 +198,8 @@ export class NameLayer implements Layer {
element.style.aspectRatio = "3/4";
};
if (player.flag()) {
const flag = player.flag();
if (player.cosmetics.flag) {
const flag = player.cosmetics.flag;
if (flag !== undefined && flag !== null && flag.startsWith("!")) {
const flagWrapper = document.createElement("div");
applyFlagStyles(flagWrapper);
@@ -207,21 +207,21 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
? "text-green-500"
: "text-white"}"
>
${player.flag()
? player.flag()!.startsWith("!")
${player.cosmetics.flag
? player.cosmetics.flag!.startsWith("!")
? html`<div
class="h-8 mr-1 aspect-[3/4] player-flag"
${ref((el) => {
if (el instanceof HTMLElement) {
requestAnimationFrame(() => {
renderPlayerFlag(player.flag()!, el);
renderPlayerFlag(player.cosmetics.flag!, el);
});
}
})}
></div>`
: html`<img
class="h-8 mr-1 aspect-[3/4]"
src=${"/flags/" + player.flag()! + ".svg"}
src=${"/flags/" + player.cosmetics.flag! + ".svg"}
/>`
: html``}
${player.name()}
+1 -1
View File
@@ -307,7 +307,7 @@ export class TerritoryLayer implements Layer {
this.paintTile(tile, useBorderColor, 255);
}
} else {
const pattern = owner.pattern();
const pattern = owner.cosmetics.pattern;
const patternsEnabled = this.cachedTerritoryPatternsEnabled ?? false;
if (pattern === undefined || patternsEnabled === false) {
this.paintTile(tile, this.theme.territoryColor(owner), 150);
+1 -10
View File
@@ -42,8 +42,6 @@ export async function createGameRunner(
const humans = gameStart.players.map(
(p) =>
new PlayerInfo(
p.pattern,
p.flag,
p.clientID === clientID
? sanitize(p.username)
: fixProfaneUsername(sanitize(p.username)),
@@ -60,14 +58,7 @@ export async function createGameRunner(
new Nation(
new Cell(n.coordinates[0], n.coordinates[1]),
n.strength,
new PlayerInfo(
undefined,
n.flag || "",
n.name,
PlayerType.FakeHuman,
null,
random.nextID(),
),
new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()),
),
);
+1 -8
View File
@@ -46,14 +46,7 @@ export class BotSpawner {
}
}
return new SpawnExecution(
new PlayerInfo(
undefined,
"",
botName,
PlayerType.Bot,
null,
this.random.nextID(),
),
new PlayerInfo(botName, PlayerType.Bot, null, this.random.nextID()),
tile,
);
}
-2
View File
@@ -350,8 +350,6 @@ export class PlayerInfo {
public readonly clan: string | null;
constructor(
public readonly pattern: string | undefined,
public readonly flag: string | undefined,
public readonly name: string,
public readonly playerType: PlayerType,
// null if bot.
-2
View File
@@ -133,8 +133,6 @@ export interface PlayerUpdate {
type: GameUpdateType.Player;
nameViewData?: NameViewData;
clientID: ClientID | null;
pattern: string | undefined;
flag: string | undefined;
name: string;
displayName: string;
id: PlayerID;
+43 -25
View File
@@ -1,6 +1,6 @@
import { Config } from "../configuration/Config";
import { PatternDecoder } from "../PatternDecoder";
import { ClientID, GameID } from "../Schemas";
import { ClientID, GameID, Player } from "../Schemas";
import { createRandomName } from "../Util";
import { WorkerClient } from "../worker/WorkerClient";
import {
@@ -9,11 +9,9 @@ import {
GameUpdates,
Gold,
NameViewData,
Player,
PlayerActions,
PlayerBorderTiles,
PlayerID,
PlayerInfo,
PlayerProfile,
PlayerType,
Team,
@@ -32,12 +30,18 @@ import {
PlayerUpdate,
UnitUpdate,
} from "./GameUpdates";
import { TerrainMapData } from "./TerrainMapLoader";
import { TerraNulliusImpl } from "./TerraNulliusImpl";
import { UnitGrid } from "./UnitGrid";
import { UserSettings } from "./UserSettings";
const userSettings: UserSettings = new UserSettings();
interface PlayerCosmetics {
pattern?: string | undefined;
flag?: string | undefined;
}
export class UnitView {
public _wasUpdated = true;
public lastPos: TileRef[] = [];
@@ -145,6 +149,7 @@ export class PlayerView {
private game: GameView,
public data: PlayerUpdate,
public nameData: NameViewData,
public cosmetics: PlayerCosmetics,
) {
if (data.clientID === game.myClientID()) {
this.anonymousName = this.data.name;
@@ -155,7 +160,9 @@ export class PlayerView {
);
}
this.decoder =
data.pattern === undefined ? undefined : new PatternDecoder(data.pattern);
this.cosmetics.pattern === undefined
? undefined
: new PatternDecoder(this.cosmetics.pattern);
}
patternDecoder(): PatternDecoder | undefined {
@@ -202,13 +209,6 @@ export class PlayerView {
smallID(): number {
return this.data.smallID;
}
flag(): string | undefined {
return this.data.flag;
}
pattern(): string | undefined {
return this.data.pattern;
}
name(): string {
return this.anonymousName !== null && userSettings.anonymousNames()
@@ -236,7 +236,7 @@ export class PlayerView {
isAlive(): boolean {
return this.data.isAlive;
}
isPlayer(): this is Player {
isPlayer(): this is PlayerView {
return true;
}
numTilesOwned(): number {
@@ -306,16 +306,7 @@ export class PlayerView {
outgoingEmojis(): EmojiMessage[] {
return this.data.outgoingEmojis;
}
info(): PlayerInfo {
return new PlayerInfo(
this.pattern(),
this.flag(),
this.name(),
this.type(),
this.clientID(),
this.id(),
);
}
hasSpawned(): boolean {
return this.data.hasSpawned;
}
@@ -338,16 +329,35 @@ export class GameView implements GameMap {
private toDelete = new Set<number>();
private _cosmetics: Map<string, PlayerCosmetics> = new Map();
private _map: GameMap;
constructor(
public worker: WorkerClient,
private _config: Config,
private _map: GameMap,
private _mapData: TerrainMapData,
private _myClientID: ClientID,
private _gameID: GameID,
private _hunans: Player[],
) {
this._map = this._mapData.gameMap;
this.lastUpdate = null;
this.unitGrid = new UnitGrid(_map);
this.unitGrid = new UnitGrid(this._map);
this._cosmetics = new Map(
this._hunans.map((h) => [
h.clientID,
{ flag: h.flag, pattern: h.pattern } satisfies PlayerCosmetics,
]),
);
for (const nation of this._mapData.manifest.nations) {
// Nations don't have client ids, so we use their name as the key instead.
this._cosmetics.set(nation.name, {
flag: nation.flag,
});
}
}
isOnEdgeOfMap(ref: TileRef): boolean {
return this._map.isOnEdgeOfMap(ref);
}
@@ -379,7 +389,15 @@ export class GameView implements GameMap {
} else {
this._players.set(
pu.id,
new PlayerView(this, pu, gu.playerNameViewData[pu.id]),
new PlayerView(
this,
pu,
gu.playerNameViewData[pu.id],
// First check human by clientID, then check nation by name.
this._cosmetics.get(pu.clientID ?? "") ??
this._cosmetics.get(pu.name) ??
{},
),
);
}
});
-10
View File
@@ -128,8 +128,6 @@ export class PlayerImpl implements Player {
return {
type: GameUpdateType.Player,
clientID: this.clientID(),
pattern: this.pattern(),
flag: this.flag(),
name: this.name(),
displayName: this.displayName(),
id: this.id(),
@@ -177,14 +175,6 @@ export class PlayerImpl implements Player {
return this._smallID;
}
pattern(): string | undefined {
return this.playerInfo.pattern;
}
flag(): string | undefined {
return this.playerInfo.flag;
}
name(): string {
return this._name;
}