diff --git a/eslint.config.js b/eslint.config.js index b204b29c7..275c4d87d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -31,4 +31,10 @@ export default [ "no-useless-escape": "off", }, }, + { + rules: { + // Enable rules + eqeqeq: "error", + }, + }, ]; diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 1e89ff700..cd4233b0d 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -63,7 +63,7 @@ export function joinLobby( ); const userSettings: UserSettings = new UserSettings(); - startGame(lobbyConfig.gameID, lobbyConfig.gameStartInfo?.config); + startGame(lobbyConfig.gameID, lobbyConfig.gameStartInfo?.config ?? {}); const transport = new Transport(lobbyConfig, eventBus); @@ -74,12 +74,12 @@ export function joinLobby( let terrainLoad: Promise | null = null; const onmessage = (message: ServerMessage) => { - if (message.type == "prestart") { + if (message.type === "prestart") { consolex.log(`lobby: game prestarting: ${JSON.stringify(message)}`); terrainLoad = loadTerrainMap(message.gameMap); onPrestart(); } - if (message.type == "start") { + if (message.type === "start") { // Trigger prestart for singleplayer games onPrestart(); consolex.log(`lobby: game started: ${JSON.stringify(message, null, 2)}`); @@ -109,10 +109,13 @@ export async function createClientGame( userSettings: UserSettings, terrainLoad: Promise | null, ): Promise { + if (lobbyConfig.gameStartInfo === undefined) { + throw new Error("missing gameStartInfo"); + } const config = await getConfig( lobbyConfig.gameStartInfo.config, userSettings, - lobbyConfig.gameRecord != null, + lobbyConfig.gameRecord !== undefined, ); let gameMap: TerrainMapData | null = null; @@ -160,7 +163,7 @@ export async function createClientGame( } export class ClientGameRunner { - private myPlayer: PlayerView; + private myPlayer: PlayerView | null = null; private isActive = false; private turnsSeen = 0; @@ -193,7 +196,7 @@ export class ClientGameRunner { }, ]; let winner: ClientID | Team | null = null; - if (update.winnerType == "player") { + if (update.winnerType === "player") { winner = this.gameView .playerBySmallID(update.winner as number) .clientID(); @@ -201,6 +204,9 @@ export class ClientGameRunner { winner = update.winner as Team; } + if (this.lobby.gameStartInfo === undefined) { + throw new Error("missing gameStartInfo"); + } const record = createGameRecord( this.lobby.gameStartInfo.gameID, this.lobby.gameStartInfo, @@ -233,10 +239,13 @@ export class ClientGameRunner { this.renderer.initialize(); this.input.initialize(); this.worker.start((gu: GameUpdateViewData | ErrorUpdate) => { + if (this.lobby.gameStartInfo === undefined) { + throw new Error("missing gameStartInfo"); + } if ("errMsg" in gu) { showErrorModal( gu.errMsg, - gu.stack, + gu.stack ?? "missing", this.lobby.gameStartInfo.gameID, this.lobby.clientID, ); @@ -268,7 +277,7 @@ export class ClientGameRunner { }; const onmessage = (message: ServerMessage) => { this.lastMessageTime = Date.now(); - if (message.type == "start") { + if (message.type === "start") { this.hasJoined = true; consolex.log("starting game!"); for (const turn of message.turns) { @@ -286,7 +295,10 @@ export class ClientGameRunner { this.turnsSeen++; } } - if (message.type == "desync") { + if (message.type === "desync") { + if (this.lobby.gameStartInfo === undefined) { + throw new Error("missing gameStartInfo"); + } showErrorModal( `desync from server: ${JSON.stringify(message)}`, "", @@ -296,12 +308,12 @@ export class ClientGameRunner { "You are desynced from other players. What you see might differ from other players.", ); } - if (message.type == "turn") { + if (message.type === "turn") { if (!this.hasJoined) { this.transport.joinGame(0); return; } - if (this.turnsSeen != message.turn.turnNumber) { + if (this.turnsSeen !== message.turn.turnNumber) { consolex.error( `got wrong turn have turns ${this.turnsSeen}, received turn ${message.turn.turnNumber}`, ); @@ -348,17 +360,17 @@ export class ClientGameRunner { if (this.gameView.inSpawnPhase()) { return; } - if (this.myPlayer == null) { - this.myPlayer = this.gameView.playerByClientID(this.lobby.clientID); - if (this.myPlayer == null) { - return; - } + if (this.myPlayer === null) { + const myPlayer = this.gameView.playerByClientID(this.lobby.clientID); + if (myPlayer === null) return; + this.myPlayer = myPlayer; } this.myPlayer.actions(tile).then((actions) => { + if (this.myPlayer === null) return; const bu = actions.buildableUnits.find( - (bu) => bu.type == UnitType.TransportShip, + (bu) => bu.type === UnitType.TransportShip, ); - if (bu == null) { + if (bu === undefined) { console.warn(`no transport ship buildable units`); return; } @@ -377,7 +389,8 @@ export class ClientGameRunner { this.myPlayer .bestTransportShipSpawn(this.gameView.ref(cell.x, cell.y)) .then((spawn: number | false) => { - let spawnCell = null; + if (this.myPlayer === null) throw new Error("not initialized"); + let spawnCell: Cell | null = null; if (spawn !== false) { spawnCell = new Cell( this.gameView.x(spawn), diff --git a/src/client/FlagInput.ts b/src/client/FlagInput.ts index 672f07e63..08d7cad13 100644 --- a/src/client/FlagInput.ts +++ b/src/client/FlagInput.ts @@ -26,7 +26,7 @@ export class FlagInput extends LitElement { } private setFlag(flag: string) { - if (flag == "xx") { + if (flag === "xx") { flag = ""; } this.flag = flag; diff --git a/src/client/GoogleAdElement.ts b/src/client/GoogleAdElement.ts index 6ae2b0eaa..8d86e47fa 100644 --- a/src/client/GoogleAdElement.ts +++ b/src/client/GoogleAdElement.ts @@ -87,7 +87,7 @@ export class GoogleAdElement extends LitElement { const isElectron = () => { // Renderer process if ( - typeof window !== "undefined" && + window !== undefined && typeof window.process === "object" && // @ts-expect-error hidden window.process.type === "renderer" @@ -97,7 +97,7 @@ const isElectron = () => { // Main process if ( - typeof process !== "undefined" && + process !== undefined && typeof process.versions === "object" && !!process.versions.electron ) { diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index 0f86f05b8..d532ef3a4 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -31,7 +31,6 @@ export class HostLobbyModal extends LitElement { @state() private disableNPCs = false; @state() private gameMode: GameMode = GameMode.FFA; @state() private teamCount: number | typeof Duos = 2; - @state() private disableNukes: boolean = false; @state() private bots: number = 400; @state() private infiniteGold: boolean = false; @state() private infiniteTroops: boolean = false; @@ -40,9 +39,9 @@ export class HostLobbyModal extends LitElement { @state() private copySuccess = false; @state() private players: string[] = []; @state() private useRandomMap: boolean = false; - @state() private disabledUnits: string[] = []; + @state() private disabledUnits: UnitType[] = []; - private playersInterval = null; + private playersInterval: NodeJS.Timeout | null = null; // Add a new timer for debouncing bot changes private botsUpdateTimer: number | null = null; @@ -106,7 +105,7 @@ export class HostLobbyModal extends LitElement { .selected=${!this.useRandomMap && this.selectedMap === mapValue} .translation=${translateText( - `map.${mapKey.toLowerCase()}`, + `map.${mapKey?.toLowerCase()}`, )} > @@ -233,7 +232,7 @@ export class HostLobbyModal extends LitElement { />
${translateText("host_modal.bots")}${ - this.bots == 0 + this.bots === 0 ? translateText("host_modal.bots_disabled") : this.bots } @@ -326,7 +325,7 @@ export class HostLobbyModal extends LitElement { [UnitType.HydrogenBomb, "unit_type.hydrogen_bomb"], [UnitType.MIRV, "unit_type.mirv"], ].map( - ([unitType, translationKey]) => html` + ([unitType, translationKey]: [UnitType, string]) => html`
- ${lobby.gameConfig.gameMode == GameMode.Team - ? translateText("public_lobby.teams", { num: teamCount }) + ${lobby.gameConfig.gameMode === GameMode.Team + ? translateText("public_lobby.teams", { num: teamCount ?? 0 }) : translateText("game_mode.ffa")}
@@ -168,7 +165,7 @@ export class PublicLobby extends LitElement { this.isButtonDebounced = false; }, this.debounceDelay); - if (this.currLobby == null) { + if (this.currLobby === null) { this.isLobbyHighlighted = true; this.currLobby = lobby; this.dispatchEvent( diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index f6960e2c8..b1030c21f 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -73,7 +73,7 @@ export class SinglePlayerModal extends LitElement { .selected=${!this.useRandomMap && this.selectedMap === mapValue} .translation=${translateText( - `map.${mapKey.toLowerCase()}`, + `map.${mapKey?.toLowerCase()}`, )} > @@ -204,7 +204,7 @@ export class SinglePlayerModal extends LitElement { />
${translateText("single_modal.bots")}${this - .bots == 0 + .bots === 0 ? translateText("single_modal.bots_disabled") : this.bots}
@@ -444,7 +444,7 @@ export class SinglePlayerModal extends LitElement { clientID, username: usernameInput.getCurrentUsername(), flag: - flagInput.getCurrentFlag() == "xx" + flagInput.getCurrentFlag() === "xx" ? "" : flagInput.getCurrentFlag(), }, diff --git a/src/client/Transport.ts b/src/client/Transport.ts index a9659ba49..eba9b82b0 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -60,14 +60,14 @@ export class SendSpawnIntentEvent implements GameEvent { export class SendAttackIntentEvent implements GameEvent { constructor( - public readonly targetID: PlayerID, + public readonly targetID: PlayerID | null, public readonly troops: number, ) {} } export class SendBoatAttackIntentEvent implements GameEvent { constructor( - public readonly targetID: PlayerID, + public readonly targetID: PlayerID | null, public readonly dst: Cell, public readonly troops: number, public readonly src: Cell | null = null, @@ -158,7 +158,7 @@ export class MoveWarshipIntentEvent implements GameEvent { } export class Transport { - private socket: WebSocket; + private socket: WebSocket | null = null; private localServer: LocalServer; @@ -176,8 +176,8 @@ export class Transport { // If gameRecord is not null, we are replaying an archived game. // For multiplayer games, GameConfig is not known until game starts. this.isLocal = - lobbyConfig.gameRecord != null || - lobbyConfig.gameStartInfo?.config.gameType == GameType.Singleplayer; + lobbyConfig.gameRecord !== undefined || + lobbyConfig.gameStartInfo?.config.gameType === GameType.Singleplayer; this.eventBus.on(SendAllianceRequestIntentEvent, (e) => this.onSendAllianceRequest(e), @@ -228,9 +228,9 @@ export class Transport { private startPing() { if (this.isLocal || this.pingInterval) return; - if (this.pingInterval == null) { + if (this.pingInterval === null) { this.pingInterval = window.setInterval(() => { - if (this.socket != null && this.socket.readyState === WebSocket.OPEN) { + if (this.socket !== null && this.socket.readyState === WebSocket.OPEN) { this.sendMsg( JSON.stringify({ type: "ping", @@ -267,7 +267,7 @@ export class Transport { this.lobbyConfig, onconnect, onmessage, - this.lobbyConfig.gameRecord != null, + this.lobbyConfig.gameRecord !== undefined, ); this.localServer.start(); } @@ -290,7 +290,12 @@ export class Transport { console.log("Connected to game server!"); while (this.buffer.length > 0) { console.log("sending dropped message"); - this.sendMsg(this.buffer.pop()); + const msg = this.buffer.pop(); + if (msg === undefined) { + console.warn("msg is undefined"); + continue; + } + this.sendMsg(msg); } onconnect(); }; @@ -306,13 +311,14 @@ export class Transport { }; this.socket.onerror = (err) => { console.error("Socket encountered error: ", err, "Closing socket"); + if (this.socket === null) return; this.socket.close(); }; this.socket.onclose = (event: CloseEvent) => { console.log( `WebSocket closed. Code: ${event.code}, Reason: ${event.reason}`, ); - if (event.code != 1000) { + if (event.code !== 1000) { console.log(`reconnecting`); this.reconnect(); } @@ -359,6 +365,7 @@ export class Transport { return; } this.stopPing(); + if (this.socket === null) return; if (this.socket.readyState === WebSocket.OPEN) { console.log("on stop: leaving game"); this.socket.close(); @@ -426,8 +433,8 @@ export class Transport { troops: event.troops, dstX: event.dst.x, dstY: event.dst.y, - srcX: event.src?.x, - srcY: event.src?.y, + srcX: event.src?.x ?? null, + srcY: event.src?.y ?? null, }); } @@ -444,7 +451,7 @@ export class Transport { type: "emoji", clientID: this.lobbyConfig.clientID, recipient: - event.recipient == AllPlayers ? AllPlayers : event.recipient.id(), + event.recipient === AllPlayers ? AllPlayers : event.recipient.id(), emoji: event.emoji, }); } @@ -517,7 +524,7 @@ export class Transport { } private onSendWinnerEvent(event: SendWinnerEvent) { - if (this.isLocal || this.socket.readyState === WebSocket.OPEN) { + if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { const msg = { type: "winner", winner: event.winner, @@ -528,13 +535,14 @@ export class Transport { } else { console.log( "WebSocket is not open. Current state:", - this.socket.readyState, + this.socket?.readyState, ); console.log("attempting reconnect"); } } private onSendHashEvent(event: SendHashEvent) { + if (this.socket === null) return; if (this.isLocal || this.socket.readyState === WebSocket.OPEN) { this.sendMsg( JSON.stringify({ @@ -570,7 +578,7 @@ export class Transport { } private sendIntent(intent: Intent) { - if (this.isLocal || this.socket.readyState === WebSocket.OPEN) { + if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { const msg = { type: "intent", intent: intent, @@ -579,7 +587,7 @@ export class Transport { } else { console.log( "WebSocket is not open. Current state:", - this.socket.readyState, + this.socket?.readyState, ); console.log("attempting reconnect"); } @@ -589,9 +597,10 @@ export class Transport { if (this.isLocal) { this.localServer.onMessage(msg); } else { + if (this.socket === null) return; if ( - this.socket.readyState == WebSocket.CLOSED || - this.socket.readyState == WebSocket.CLOSED + this.socket.readyState === WebSocket.CLOSED || + this.socket.readyState === WebSocket.CLOSED ) { console.warn("socket not ready, closing and trying later"); this.socket.close(); @@ -605,7 +614,7 @@ export class Transport { } private killExistingSocket(): void { - if (this.socket == null) { + if (this.socket === null) { return; } // Remove all event listeners diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index 45d911905..f94f0591a 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -281,7 +281,7 @@ export class UserSettingModal extends LitElement { easter="true" @change=${(e: CustomEvent) => { const value = e.detail?.value; - if (typeof value !== "undefined") { + if (value !== undefined) { console.log("Changed:", value); } else { console.warn("Slider event missing detail.value", e); @@ -300,7 +300,7 @@ export class UserSettingModal extends LitElement { easter="true" @change=${(e: CustomEvent) => { const value = e.detail?.value; - if (typeof value !== "undefined") { + if (value !== undefined) { console.log("Changed:", value); } else { console.warn("Slider event missing detail.value", e); diff --git a/src/client/UsernameInput.ts b/src/client/UsernameInput.ts index f185fb931..39b94d73d 100644 --- a/src/client/UsernameInput.ts +++ b/src/client/UsernameInput.ts @@ -64,7 +64,7 @@ export class UsernameInput extends LitElement { this.storeUsername(this.username); this.validationError = ""; } else { - this.validationError = result.error; + this.validationError = result.error ?? ""; } } diff --git a/src/client/Utils.ts b/src/client/Utils.ts index a0d3ff912..a6c90191f 100644 --- a/src/client/Utils.ts +++ b/src/client/Utils.ts @@ -45,12 +45,12 @@ export function createCanvas(): HTMLCanvasElement { */ export function generateCryptoRandomUUID(): string { // Type guard to check if randomUUID is available - if (typeof crypto !== "undefined" && "randomUUID" in crypto) { + if (crypto !== undefined && "randomUUID" in crypto) { return crypto.randomUUID(); } // Fallback using crypto.getRandomValues - if (typeof crypto !== "undefined" && "getRandomValues" in crypto) { + if (crypto !== undefined && "getRandomValues" in crypto) { return (([1e7] as any) + -1e3 + -4e3 + -8e3 + -1e11).replace( /[018]/g, (c: number): string => diff --git a/src/client/components/baseComponents/setting/SettingSlider.ts b/src/client/components/baseComponents/setting/SettingSlider.ts index 989756b57..87927d467 100644 --- a/src/client/components/baseComponents/setting/SettingSlider.ts +++ b/src/client/components/baseComponents/setting/SettingSlider.ts @@ -30,7 +30,7 @@ export class SettingSlider extends LitElement { private handleSliderChange(e: Event) { const detail = (e as CustomEvent)?.detail; - if (!detail || typeof detail.value === "undefined") { + if (!detail || detail.value === undefined) { console.warn("Invalid slider change event", e); return; } diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index b778aea1d..3f83a1c41 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -214,7 +214,9 @@ export class GameRenderer { public uiState: UIState, private layers: Layer[], ) { - this.context = canvas.getContext("2d"); + const context = canvas.getContext("2d"); + if (context === null) throw new Error("2d context not supported"); + this.context = context; } initialize() { diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index 873affd3a..49abd3c1e 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -90,6 +90,9 @@ export const getColoredSprite = ( } const sprite = getSpriteForUnit(unit.type()); + if (sprite === null) { + throw new Error(`Failed to load sprite for ${unit.type()}`); + } const territoryRgb = territoryColor.toRgb(); const borderRgb = borderColor.toRgb(); diff --git a/src/client/graphics/TransformHandler.ts b/src/client/graphics/TransformHandler.ts index cdf0d316f..5a7be6c84 100644 --- a/src/client/graphics/TransformHandler.ts +++ b/src/client/graphics/TransformHandler.ts @@ -9,8 +9,8 @@ export class TransformHandler { private offsetX: number = -350; private offsetY: number = -200; - private target: Cell; - private intervalID = null; + private target: Cell | null; + private intervalID: NodeJS.Timeout | null = null; private changed = false; constructor( @@ -170,6 +170,8 @@ export class TransformHandler { const { screenX, screenY } = this.screenCenter(); const screenMapCenter = new Cell(screenX, screenY); + if (this.target === null) throw new Error("null target"); + if ( this.game.manhattanDist( this.game.ref(screenX, screenY), @@ -234,7 +236,7 @@ export class TransformHandler { } private clearTarget() { - if (this.intervalID != null) { + if (this.intervalID !== null) { clearInterval(this.intervalID); this.intervalID = null; } diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts index e051f6519..f048c2271 100644 --- a/src/client/graphics/layers/BuildMenu.ts +++ b/src/client/graphics/layers/BuildMenu.ts @@ -303,13 +303,12 @@ export class BuildMenu extends LitElement implements Layer { private _hidden = true; private canBuild(item: BuildItemDisplay): boolean { - if (this.game?.myPlayer() == null || this.playerActions == null) { + if (this.game?.myPlayer() === null || this.playerActions === null) { return false; } - const unit = this.playerActions.buildableUnits.filter( - (u) => u.type == item.unitType, - ); - if (!unit) { + const buildableUnits = this.playerActions?.buildableUnits ?? []; + const unit = buildableUnits.filter((u) => u.type === item.unitType); + if (unit.length === 0) { return false; } return unit[0].canBuild !== false; @@ -317,7 +316,7 @@ export class BuildMenu extends LitElement implements Layer { private cost(item: BuildItemDisplay): number { for (const bu of this.playerActions?.buildableUnits ?? []) { - if (bu.type == item.unitType) { + if (bu.type === item.unitType) { return bu.cost; } } @@ -368,9 +367,12 @@ export class BuildMenu extends LitElement implements Layer { width="40" height="40" /> - ${translateText(item.key)} + ${item.key && translateText(item.key)} ${translateText(item.description)}${item.description && + translateText(item.description)} ${renderNumber( @@ -413,7 +415,7 @@ export class BuildMenu extends LitElement implements Layer { private refresh() { this.game .myPlayer() - .actions(this.clickedTile) + ?.actions(this.clickedTile) .then((actions) => { this.playerActions = actions; this.requestUpdate(); diff --git a/src/client/graphics/layers/ChatDisplay.ts b/src/client/graphics/layers/ChatDisplay.ts index 847dd35d0..e4f376e18 100644 --- a/src/client/graphics/layers/ChatDisplay.ts +++ b/src/client/graphics/layers/ChatDisplay.ts @@ -63,7 +63,7 @@ export class ChatDisplay extends LitElement implements Layer { if (event.messageType !== MessageType.CHAT) return; const myPlayer = this.game.playerByClientID(this.clientID); if ( - event.playerID != null && + event.playerID !== null && (!myPlayer || myPlayer.smallID() !== event.playerID) ) { return; @@ -82,6 +82,7 @@ export class ChatDisplay extends LitElement implements Layer { tick() { // this.active = true; const updates = this.game.updatesSinceLastTick(); + if (updates === null) throw new Error("null updates"); const messages = updates[GameUpdateType.DisplayEvent] as | DisplayMessageUpdate[] | undefined; @@ -91,7 +92,7 @@ export class ChatDisplay extends LitElement implements Layer { if (msg.messageType === MessageType.CHAT) { const myPlayer = this.game.playerByClientID(this.clientID); if ( - msg.playerID != null && + msg.playerID !== null && (!myPlayer || myPlayer.smallID() !== msg.playerID) ) { continue; diff --git a/src/client/graphics/layers/ChatModal.ts b/src/client/graphics/layers/ChatModal.ts index 84162fd33..13827eb7c 100644 --- a/src/client/graphics/layers/ChatModal.ts +++ b/src/client/graphics/layers/ChatModal.ts @@ -214,7 +214,8 @@ export class ChatModal extends LitElement { private selectPlayer(player: string) { if (this.previewText) { - this.previewText = this.selectedPhraseTemplate.replace("[P1]", player); + this.previewText = + this.selectedPhraseTemplate?.replace("[P1]", player) ?? null; this.selectedPlayer = player; this.requiresPlayerSelection = false; this.requestUpdate(); @@ -228,7 +229,9 @@ export class ChatModal extends LitElement { console.log("Key:", this.selectedQuickChatKey); if (this.sender && this.recipient && this.selectedQuickChatKey) { - const variables = this.selectedPlayer ? { P1: this.selectedPlayer } : {}; + const variables: Record = this.selectedPlayer + ? { P1: this.selectedPlayer } + : {}; this.eventBus.emit( new SendQuickChatEvent( diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index 1fa6e1185..0ec35f9af 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -85,7 +85,7 @@ export class ControlPanel extends LitElement implements Layer { newAttackRatio = 1; } - if (newAttackRatio == 0.11 && this.attackRatio == 0.01) { + if (newAttackRatio === 0.11 && this.attackRatio === 0.01) { // If we're changing the ratio from 1%, then set it to 10% instead of 11% to keep a consistency newAttackRatio = 0.1; } @@ -108,13 +108,13 @@ export class ControlPanel extends LitElement implements Layer { } const player = this.game.myPlayer(); - if (player == null || !player.isAlive()) { + if (player === null || !player.isAlive()) { this.setVisibile(false); return; } const popIncreaseRate = player.population() - this._population; - if (this.game.ticks() % 5 == 0) { + if (this.game.ticks() % 5 === 0) { this._popRateIsIncreasing = popIncreaseRate >= this._lastPopulationIncreaseRate; this._lastPopulationIncreaseRate = popIncreaseRate; @@ -275,7 +275,7 @@ export class ControlPanel extends LitElement implements Layer { >${translateText("control_panel.attack_ratio")}: ${(this.attackRatio * 100).toFixed(0)}% (${renderTroops( - this.game?.myPlayer()?.troops() * this.attackRatio, + (this.game?.myPlayer()?.troops() ?? 0) * this.attackRatio, )})
diff --git a/src/client/graphics/layers/EmojiTable.ts b/src/client/graphics/layers/EmojiTable.ts index b8a9e4bd6..938340672 100644 --- a/src/client/graphics/layers/EmojiTable.ts +++ b/src/client/graphics/layers/EmojiTable.ts @@ -114,7 +114,7 @@ export class EmojiTable extends LitElement { this.showTable((emoji) => { const recipient = - targetPlayer == this.game.myPlayer() + targetPlayer === this.game.myPlayer() ? AllPlayers : (targetPlayer as PlayerView); this.eventBus.emit( diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index 34eb7ee02..70630d894 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -107,8 +107,10 @@ export class EventsDisplay extends LitElement implements Layer { tick() { this.active = true; const updates = this.game.updatesSinceLastTick(); - for (const [ut, fn] of this.updateMap) { - updates[ut]?.forEach((u) => fn(u)); + if (updates) { + for (const [ut, fn] of this.updateMap) { + updates[ut]?.forEach(fn); + } } let remainingEvents = this.events.filter((event) => { @@ -137,16 +139,16 @@ export class EventsDisplay extends LitElement implements Layer { // Update attacks this.incomingAttacks = myPlayer.incomingAttacks().filter((a) => { const t = (this.game.playerBySmallID(a.attackerID) as PlayerView).type(); - return t != PlayerType.Bot; + return t !== PlayerType.Bot; }); this.outgoingAttacks = myPlayer .outgoingAttacks() - .filter((a) => a.targetID != 0); + .filter((a) => a.targetID !== 0); this.outgoingLandAttacks = myPlayer .outgoingAttacks() - .filter((a) => a.targetID == 0); + .filter((a) => a.targetID === 0); this.outgoingBoats = myPlayer .units() @@ -157,7 +159,7 @@ export class EventsDisplay extends LitElement implements Layer { private addEvent(event: Event) { this.events = [...this.events, event]; - if (this._hidden == true) { + if (this._hidden === true) { this.newEvents++; } this.requestUpdate(); @@ -179,7 +181,7 @@ export class EventsDisplay extends LitElement implements Layer { onDisplayMessageEvent(event: DisplayMessageUpdate) { const myPlayer = this.game.playerByClientID(this.clientID); if ( - event.playerID != null && + event.playerID !== null && (!myPlayer || myPlayer.smallID() !== event.playerID) ) { return; @@ -345,6 +347,7 @@ export class EventsDisplay extends LitElement implements Layer { : update.player2ID === myPlayer.smallID() ? update.player1ID : null; + if (otherID === null) return; const other = this.game.playerBySmallID(otherID) as PlayerView; if (!other || !myPlayer.isAlive() || !other.isAlive()) return; @@ -394,14 +397,14 @@ export class EventsDisplay extends LitElement implements Layer { if (!myPlayer) return; const recipient = - update.emoji.recipientID == AllPlayers + update.emoji.recipientID === AllPlayers ? AllPlayers : this.game.playerBySmallID(update.emoji.recipientID); const sender = this.game.playerBySmallID( update.emoji.senderID, ) as PlayerView; - if (recipient == myPlayer) { + if (recipient === myPlayer) { this.addEvent({ description: `${sender.displayName()}:${update.emoji.message}`, unsafeDescription: true, @@ -427,10 +430,7 @@ export class EventsDisplay extends LitElement implements Layer { onUnitIncomingEvent(event: UnitIncomingUpdate) { const myPlayer = this.game.playerByClientID(this.clientID); - if ( - event.playerID != null && - (!myPlayer || myPlayer.smallID() !== event.playerID) - ) { + if (!myPlayer || myPlayer.smallID() !== event.playerID) { return; } @@ -482,8 +482,10 @@ export class EventsDisplay extends LitElement implements Layer {
- ${player.team() != null + ${player.team() !== null ? html`
${translateText("player_info_overlay.team")}: ${player.team()}
` @@ -271,7 +271,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer { private renderUnitInfo(unit: UnitView) { const isAlly = - (unit.owner() == this.myPlayer() || + (unit.owner() === this.myPlayer() || this.myPlayer()?.isFriendly(unit.owner())) ?? false; @@ -312,8 +312,8 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
- ${this.player != null ? this.renderPlayerInfo(this.player) : ""} - ${this.unit != null ? this.renderUnitInfo(this.unit) : ""} + ${this.player !== null ? this.renderPlayerInfo(this.player) : ""} + ${this.unit !== null ? this.renderUnitInfo(this.unit) : ""}
`; diff --git a/src/client/graphics/layers/PlayerPanel.ts b/src/client/graphics/layers/PlayerPanel.ts index bfe293ad9..547bd54fd 100644 --- a/src/client/graphics/layers/PlayerPanel.ts +++ b/src/client/graphics/layers/PlayerPanel.ts @@ -39,8 +39,8 @@ export class PlayerPanel extends LitElement implements Layer { public eventBus: EventBus; public emojiTable: EmojiTable; - private actions: PlayerActions = null; - private tile: TileRef = null; + private actions: PlayerActions | null = null; + private tile: TileRef | null = null; @state() private isVisible: boolean = false; @@ -125,7 +125,7 @@ export class PlayerPanel extends LitElement implements Layer { private handleEmojiClick(e: Event, myPlayer: PlayerView, other: PlayerView) { e.stopPropagation(); this.emojiTable.showTable((emoji: string) => { - if (myPlayer == other) { + if (myPlayer === other) { this.eventBus.emit( new SendEmojiIntentEvent( AllPlayers, @@ -181,12 +181,16 @@ export class PlayerPanel extends LitElement implements Layer { return 0; } let sum = 0; - const nukes = stats.sentNukes[this.g.myPlayer().id()]; + const player = this.g.myPlayer(); + if (player === null) { + return 0; + } + const nukes = stats.sentNukes[player.id()]; if (!nukes) { return 0; } for (const nukeType in nukes) { - if (nukeType != UnitType.MIRVWarhead) { + if (nukeType !== UnitType.MIRVWarhead) { sum += nukes[nukeType]; } } @@ -198,10 +202,8 @@ export class PlayerPanel extends LitElement implements Layer { return html``; } const myPlayer = this.g.myPlayer(); - if (myPlayer == null) { - return; - } - + if (myPlayer === null) return; + if (this.tile === null) return; let other = this.g.owner(this.tile); if (!other.isPlayer()) { this.hide(); @@ -210,16 +212,16 @@ export class PlayerPanel extends LitElement implements Layer { } other = other as PlayerView; - const canDonate = this.actions.interaction?.canDonate; + const canDonate = this.actions?.interaction?.canDonate; const canSendAllianceRequest = - this.actions.interaction?.canSendAllianceRequest; + this.actions?.interaction?.canSendAllianceRequest; const canSendEmoji = - other == myPlayer - ? this.actions.canSendEmojiAllPlayers - : this.actions.interaction?.canSendEmoji; - const canBreakAlliance = this.actions.interaction?.canBreakAlliance; - const canTarget = this.actions.interaction?.canTarget; - const canEmbargo = this.actions.interaction?.canEmbargo; + other === myPlayer + ? this.actions?.canSendEmojiAllPlayers + : this.actions?.interaction?.canSendEmoji; + const canBreakAlliance = this.actions?.interaction?.canBreakAlliance; + const canTarget = this.actions?.interaction?.canTarget; + const canEmbargo = this.actions?.interaction?.canEmbargo; return html`
` : ""}
- ${canEmbargo && other != myPlayer + ${canEmbargo && other !== myPlayer ? html`` : ""} - ${!canEmbargo && other != myPlayer + ${!canEmbargo && other !== myPlayer ? html`