Enable the @typescript-eslint/prefer-readonly eslint rule (#1859)

## Description:

Enable the `@typescript-eslint/prefer-readonly` eslint rule.

## 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
This commit is contained in:
Scott Anderson
2025-08-18 20:09:21 -04:00
committed by GitHub
parent ed090b88b5
commit 9163f0d710
128 changed files with 526 additions and 525 deletions
+1
View File
@@ -86,6 +86,7 @@ export default [
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-literal-enum-member": "error",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-readonly": "error",
"eqeqeq": "error",
"indent": "off", // @stylistic/indent
"sort-keys": "error",
+7 -7
View File
@@ -196,13 +196,13 @@ export class ClientGameRunner {
private connectionCheckInterval: ReturnType<typeof setTimeout> | null = null;
constructor(
private lobby: LobbyConfig,
private eventBus: EventBus,
private renderer: GameRenderer,
private input: InputHandler,
private transport: Transport,
private worker: WorkerClient,
private gameView: GameView,
private readonly lobby: LobbyConfig,
private readonly eventBus: EventBus,
private readonly renderer: GameRenderer,
private readonly input: InputHandler,
private readonly transport: Transport,
private readonly worker: WorkerClient,
private readonly gameView: GameView,
) {
this.lastMessageTime = Date.now();
}
+2 -2
View File
@@ -4,7 +4,7 @@ import { UserSettings } from "../core/game/UserSettings";
@customElement("dark-mode-button")
export class DarkModeButton extends LitElement {
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
@state() private darkMode: boolean = this.userSettings.darkMode();
createRenderRoot() {
@@ -21,7 +21,7 @@ export class DarkModeButton extends LitElement {
window.removeEventListener("dark-mode-changed", this.handleDarkModeChanged);
}
private handleDarkModeChanged = (e: Event) => {
private readonly handleDarkModeChanged = (e: Event) => {
const event = e as CustomEvent<{ darkMode: boolean }>;
this.darkMode = event.detail.darkMode;
};
+1 -1
View File
@@ -43,7 +43,7 @@ export class FlagInput extends LitElement {
);
}
private updateFlag = (ev: Event) => {
private readonly updateFlag = (ev: Event) => {
const e = ev as CustomEvent<{ flag: string }>;
if (!FlagSchema.safeParse(e.detail.flag).success) return;
if (this.flag !== e.detail.flag) {
+2 -2
View File
@@ -4,7 +4,7 @@ import Countries from "./data/countries.json";
@customElement("flag-input-modal")
export class FlagInputModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@@ -108,7 +108,7 @@ export class FlagInputModal extends LitElement {
super.disconnectedCallback();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+2 -2
View File
@@ -6,7 +6,7 @@ import { getAltKey, getModifierKey, translateText } from "../client/Utils";
@customElement("help-modal")
export class HelpModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@@ -25,7 +25,7 @@ export class HelpModal extends LitElement {
super.disconnectedCallback();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+3 -3
View File
@@ -32,7 +32,7 @@ import { translateText } from "../client/Utils";
@customElement("host-lobby-modal")
export class HostLobbyModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@@ -58,7 +58,7 @@ export class HostLobbyModal extends LitElement {
private playersInterval: ReturnType<typeof setTimeout> | null = null;
// Add a new timer for debouncing bot changes
private botsUpdateTimer: number | null = null;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
connectedCallback() {
super.connectedCallback();
@@ -70,7 +70,7 @@ export class HostLobbyModal extends LitElement {
super.disconnectedCallback();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+5 -5
View File
@@ -121,7 +121,7 @@ export class InputHandler {
private lastPointerDownX = 0;
private lastPointerDownY = 0;
private pointers: Map<number, PointerEvent> = new Map();
private readonly pointers: Map<number, PointerEvent> = new Map();
private lastPinchDistance = 0;
@@ -130,17 +130,17 @@ export class InputHandler {
private alternateView = false;
private moveInterval: ReturnType<typeof setTimeout> | null = null;
private activeKeys = new Set<string>();
private readonly activeKeys = new Set<string>();
private keybinds: Record<string, string> = {};
private readonly PAN_SPEED = 5;
private readonly ZOOM_SPEED = 10;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
constructor(
private canvas: HTMLCanvasElement,
private eventBus: EventBus,
private readonly canvas: HTMLCanvasElement,
private readonly eventBus: EventBus,
) {}
initialize() {
+3 -3
View File
@@ -14,11 +14,11 @@ import { translateText } from "../client/Utils";
@customElement("join-private-lobby-modal")
export class JoinPrivateLobbyModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@query("#lobbyIdInput") private lobbyIdInput!: HTMLInputElement;
@query("#lobbyIdInput") private readonly lobbyIdInput!: HTMLInputElement;
@state() private message = "";
@state() private hasJoined = false;
@state() private players: string[] = [];
@@ -35,7 +35,7 @@ export class JoinPrivateLobbyModal extends LitElement {
super.disconnectedCallback();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+1 -1
View File
@@ -47,7 +47,7 @@ export class LangSelector extends LitElement {
private debugKeyPressed = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private languageMap: Record<string, any> = {
private readonly languageMap: Record<string, any> = {
ar,
bg,
bn,
+3 -3
View File
@@ -14,7 +14,7 @@ export class LanguageModal extends LitElement {
return this; // Use Light DOM for TailwindCSS classes
}
private close = () => {
private readonly close = () => {
this.dispatchEvent(
new CustomEvent("close-modal", {
bubbles: true,
@@ -44,14 +44,14 @@ export class LanguageModal extends LitElement {
document.body.style.overflow = "auto";
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
}
};
private selectLanguage = (lang: string) => {
private readonly selectLanguage = (lang: string) => {
this.dispatchEvent(
new CustomEvent("language-selected", {
detail: { lang },
+6 -6
View File
@@ -21,7 +21,7 @@ export class LocalServer {
// All turns from the game record on replay.
private replayTurns: Turn[] = [];
private turns: Turn[] = [];
private readonly turns: Turn[] = [];
private intents: Intent[] = [];
private startedAt: number;
@@ -38,11 +38,11 @@ export class LocalServer {
private turnCheckInterval: ReturnType<typeof setTimeout>;
constructor(
private lobbyConfig: LobbyConfig,
private clientConnect: () => void,
private clientMessage: (message: ServerMessage) => void,
private isReplay: boolean,
private eventBus: EventBus,
private readonly lobbyConfig: LobbyConfig,
private readonly clientConnect: () => void,
private readonly clientMessage: (message: ServerMessage) => void,
private readonly isReplay: boolean,
private readonly eventBus: EventBus,
) {}
start() {
+2 -2
View File
@@ -83,7 +83,7 @@ export type KickPlayerEvent = {
class Client {
private gameStop: (() => void) | null = null;
private eventBus: EventBus = new EventBus();
private readonly eventBus: EventBus = new EventBus();
private usernameInput: UsernameInput | null = null;
private flagInput: FlagInput | null = null;
@@ -91,7 +91,7 @@ class Client {
private joinModal: JoinPrivateLobbyModal;
private publicLobby: PublicLobby;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
constructor() {}
+2 -2
View File
@@ -8,7 +8,7 @@ import { translateText } from "../client/Utils";
@customElement("news-modal")
export class NewsModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@@ -23,7 +23,7 @@ export class NewsModal extends LitElement {
super.disconnectedCallback();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+3 -3
View File
@@ -13,11 +13,11 @@ export class PublicLobby extends LitElement {
@state() private lobbies: GameInfo[] = [];
@state() public isLobbyHighlighted = false;
@state() private isButtonDebounced = false;
@state() private mapImages: Map<GameID, string> = new Map();
@state() private readonly mapImages: Map<GameID, string> = new Map();
private lobbiesInterval: number | null = null;
private currLobby: GameInfo | null = null;
private debounceDelay = 750;
private lobbyIDToStart = new Map<GameID, number>();
private readonly debounceDelay = 750;
private readonly lobbyIDToStart = new Map<GameID, number>();
createRenderRoot() {
return this;
+5 -5
View File
@@ -28,7 +28,7 @@ import { translateText } from "../client/Utils";
@customElement("single-player-modal")
export class SinglePlayerModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@@ -37,9 +37,9 @@ export class SinglePlayerModal extends LitElement {
@state() private disableNPCs = false;
@state() private bots = 400;
@state() private infiniteGold = false;
@state() private donateGold = false;
@state() private readonly donateGold = false;
@state() private infiniteTroops = false;
@state() private donateTroops = false;
@state() private readonly donateTroops = false;
@state() private instantBuild = false;
@state() private useRandomMap = false;
@state() private gameMode: GameMode = GameMode.FFA;
@@ -47,7 +47,7 @@ export class SinglePlayerModal extends LitElement {
@state() private disabledUnits: UnitType[] = [];
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
connectedCallback() {
super.connectedCallback();
@@ -59,7 +59,7 @@ export class SinglePlayerModal extends LitElement {
super.disconnectedCallback();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+3 -3
View File
@@ -13,7 +13,7 @@ import { translateText } from "./Utils";
@customElement("territory-patterns-modal")
export class TerritoryPatternsModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
@@ -36,7 +36,7 @@ export class TerritoryPatternsModal extends LitElement {
public resizeObserver: ResizeObserver;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
private isActive = false;
@@ -71,7 +71,7 @@ export class TerritoryPatternsModal extends LitElement {
this.requestUpdate();
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (e.code === "Escape") {
e.preventDefault();
this.close();
+3 -3
View File
@@ -174,7 +174,7 @@ export class Transport {
private localServer: LocalServer;
private buffer: string[] = [];
private readonly buffer: string[] = [];
private onconnect: () => void;
private onmessage: (msg: ServerMessage) => void;
@@ -182,8 +182,8 @@ export class Transport {
private pingInterval: number | null = null;
public readonly isLocal: boolean;
constructor(
private lobbyConfig: LobbyConfig,
private eventBus: EventBus,
private readonly lobbyConfig: LobbyConfig,
private readonly eventBus: EventBus,
) {
// If gameRecord is not null, we are replaying an archived game.
// For multiplayer games, GameConfig is not known until game starts.
+3 -3
View File
@@ -13,7 +13,7 @@ const KeybindSchema = z.record(z.string(), z.string());
@customElement("user-setting")
export class UserSettingModal extends LitElement {
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
@state() private settingsMode: "basic" | "keybinds" = "basic";
@state() private keybinds: Record<string, string> = {};
@@ -35,7 +35,7 @@ export class UserSettingModal extends LitElement {
}
}
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
isModalOpen: boolean;
@@ -51,7 +51,7 @@ export class UserSettingModal extends LitElement {
document.body.style.overflow = "auto";
}
private handleKeyDown = (e: KeyboardEvent) => {
private readonly handleKeyDown = (e: KeyboardEvent) => {
if (!this.modalEl?.isModalOpen || this.showEasterEggSettings) return;
if (e.code === "Escape") {
+1 -1
View File
@@ -15,7 +15,7 @@ export class UsernameInput extends LitElement {
@state() private username = "";
@property({ type: String }) validationError = "";
private _isValid = true;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
// Remove static styles since we're using Tailwind
+6 -6
View File
@@ -1,15 +1,15 @@
export class AnimatedSprite {
private frameHeight: number;
private readonly frameHeight: number;
private currentFrame = 0;
private elapsedTime = 0;
private active = true;
constructor(
private image: CanvasImageSource,
private frameWidth: number,
private frameCount: number,
private frameDuration: number, // in milliseconds
private looping = false,
private readonly image: CanvasImageSource,
private readonly frameWidth: number,
private readonly frameCount: number,
private readonly frameDuration: number, // in milliseconds
private readonly looping = false,
private originX: number,
private originY: number,
) {
+2 -2
View File
@@ -128,9 +128,9 @@ const ANIMATED_SPRITE_CONFIG: Partial<Record<FxType, AnimatedSpriteConfig>> = {
};
export class AnimatedSpriteLoader {
private animatedSpriteImageMap: Map<FxType, HTMLCanvasElement> = new Map();
private readonly animatedSpriteImageMap: Map<FxType, HTMLCanvasElement> = new Map();
// Do not color the same sprite twice
private coloredAnimatedSpriteCache: Map<string, HTMLCanvasElement> =
private readonly coloredAnimatedSpriteCache: Map<string, HTMLCanvasElement> =
new Map();
public async loadAllAnimatedSpriteImages(): Promise<void> {
+6 -6
View File
@@ -283,16 +283,16 @@ export function createRenderer(
}
export class GameRenderer {
private context: CanvasRenderingContext2D;
private readonly context: CanvasRenderingContext2D;
constructor(
private game: GameView,
private eventBus: EventBus,
private canvas: HTMLCanvasElement,
private readonly game: GameView,
private readonly eventBus: EventBus,
private readonly canvas: HTMLCanvasElement,
public transformHandler: TransformHandler,
public uiState: UIState,
private layers: Layer[],
private fpsDisplay: FPSDisplay,
private readonly layers: Layer[],
private readonly fpsDisplay: FPSDisplay,
) {
const context = canvas.getContext("2d");
if (context === null) throw new Error("2d context not supported");
+6 -6
View File
@@ -1,12 +1,12 @@
export class ProgressBar {
private static readonly CLEAR_PADDING = 2;
constructor(
private colors: string[] = [],
private ctx: CanvasRenderingContext2D,
private x: number,
private y: number,
private w: number,
private h: number,
private readonly colors: string[] = [],
private readonly ctx: CanvasRenderingContext2D,
private readonly x: number,
private readonly y: number,
private readonly w: number,
private readonly h: number,
private progress = 0, // Progress from 0 to 1
) {
this.setProgress(progress);
+3 -3
View File
@@ -24,9 +24,9 @@ export class TransformHandler {
private changed = false;
constructor(
private game: GameView,
private eventBus: EventBus,
private canvas: HTMLCanvasElement,
private readonly game: GameView,
private readonly eventBus: EventBus,
private readonly canvas: HTMLCanvasElement,
) {
this._boundingRect = this.canvas.getBoundingClientRect();
this.eventBus.on(ZoomEvent, (e) => this.onZoom(e));
+4 -4
View File
@@ -9,10 +9,10 @@ import { GameView } from "../../../core/game/GameView";
export class ShockwaveFx implements Fx {
private lifeTime = 0;
constructor(
private x: number,
private y: number,
private duration: number,
private maxRadius: number,
private readonly x: number,
private readonly y: number,
private readonly duration: number,
private readonly maxRadius: number,
) {}
renderTick(frameTime: number, ctx: CanvasRenderingContext2D): boolean {
+3 -3
View File
@@ -20,9 +20,9 @@ function fadeInOut(t: number, fadeIn = 0.3, fadeOut = 0.7): number {
*/
export class FadeFx implements Fx {
constructor(
private fxToFade: SpriteFx,
private fadeIn: number,
private fadeOut: number,
private readonly fxToFade: SpriteFx,
private readonly fadeIn: number,
private readonly fadeOut: number,
) {}
renderTick(duration: number, ctx: CanvasRenderingContext2D): boolean {
+7 -7
View File
@@ -4,13 +4,13 @@ export class TextFx implements Fx {
private lifeTime = 0;
constructor(
private text: string,
private x: number,
private y: number,
private duration: number,
private riseDistance = 30,
private font = "11px sans-serif",
private color: { r: number; g: number; b: number } = {
private readonly text: string,
private readonly x: number,
private readonly y: number,
private readonly duration: number,
private readonly riseDistance = 30,
private readonly font = "11px sans-serif",
private readonly color: { r: number; g: number; b: number } = {
r: 255,
g: 255,
b: 255,
+1 -1
View File
@@ -8,7 +8,7 @@ type TimedTask = {
* Basic timeline to chain actions
*/
export class Timeline {
private tasks: TimedTask[] = [];
private readonly tasks: TimedTask[] = [];
private timeElapsed = 0;
add(delay: number, action: () => void): Timeline {
+4 -4
View File
@@ -8,13 +8,13 @@ import { Timeline } from "./Timeline";
* Explosion Effect: a few timed explosions
*/
export class UnitExplosionFx implements Fx {
private timeline = new Timeline();
private explosions: Fx[] = [];
private readonly timeline = new Timeline();
private readonly explosions: Fx[] = [];
constructor(
animatedSpriteLoader: AnimatedSpriteLoader,
private x: number,
private y: number,
private readonly x: number,
private readonly y: number,
game: GameView,
) {
const config = [
+1 -1
View File
@@ -15,7 +15,7 @@ const ALERT_COUNT = 2;
@customElement("alert-frame")
export class AlertFrame extends LitElement implements Layer {
public game: GameView;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
@state()
private isActive = false;
+1 -1
View File
@@ -24,7 +24,7 @@ export class ChatDisplay extends LitElement implements Layer {
public eventBus: EventBus;
public game: GameView;
private active = false;
private readonly active = false;
@state() private _hidden = false;
@state() private newEvents = 0;
@@ -6,11 +6,11 @@ import { SendQuickChatEvent } from "../../Transport";
import { translateText } from "../../Utils";
export class ChatIntegration {
private ctModal: ChatModal;
private readonly ctModal: ChatModal;
constructor(
private game: GameView,
private eventBus: EventBus,
private readonly game: GameView,
private readonly eventBus: EventBus,
) {
this.ctModal = document.querySelector("chat-modal") as ChatModal;
+1 -1
View File
@@ -19,7 +19,7 @@ export const quickChatPhrases: QuickChatPhrases = quickChatData;
@customElement("chat-modal")
export class ChatModal extends LitElement {
@query("o-modal") private modalEl!: HTMLElement & {
@query("o-modal") private readonly modalEl!: HTMLElement & {
open: () => void;
close: () => void;
};
+3 -3
View File
@@ -76,7 +76,7 @@ export class EventsDisplay extends LitElement implements Layer {
private events: GameEvent[] = [];
// allianceID -> last checked at tick
private alliancesCheckedAt = new Map<number, Tick>();
private readonly alliancesCheckedAt = new Map<number, Tick>();
@state() private incomingAttacks: AttackUpdate[] = [];
@state() private outgoingAttacks: AttackUpdate[] = [];
@state() private outgoingLandAttacks: AttackUpdate[] = [];
@@ -87,7 +87,7 @@ export class EventsDisplay extends LitElement implements Layer {
@state() private latestGoldAmount: bigint | null = null;
@state() private goldAmountAnimating = false;
private goldAmountTimeoutId: ReturnType<typeof setTimeout> | null = null;
@state() private eventsFilters: Map<MessageCategory, boolean> = new Map([
@state() private readonly eventsFilters: Map<MessageCategory, boolean> = new Map([
[MessageCategory.ATTACK, false],
[MessageCategory.TRADE, false],
[MessageCategory.ALLIANCE, false],
@@ -141,7 +141,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.requestUpdate();
}
private updateMap = [
private readonly updateMap = [
[GameUpdateType.DisplayEvent, this.onDisplayMessageEvent.bind(this)],
[GameUpdateType.DisplayChatEvent, this.onDisplayChatEvent.bind(this)],
[GameUpdateType.AllianceRequest, this.onAllianceRequestEvent.bind(this)],
+5 -5
View File
@@ -33,8 +33,8 @@ export class FPSDisplay extends LitElement implements Layer {
private frameCount = 0;
private lastTime = 0;
private frameTimes: number[] = [];
private fpsHistory: number[] = [];
private readonly frameTimes: number[] = [];
private readonly fpsHistory: number[] = [];
private lastSecondTime = 0;
private framesThisSecond = 0;
private dragStart: { x: number; y: number } = { x: 0, y: 0 };
@@ -118,7 +118,7 @@ export class FPSDisplay extends LitElement implements Layer {
this.userSettings.togglePerformanceOverlay();
}
private handleMouseDown = (e: MouseEvent) => {
private readonly handleMouseDown = (e: MouseEvent) => {
// Don't start dragging if clicking on close button
if ((e.target as HTMLElement).classList.contains("close-button")) {
return;
@@ -135,7 +135,7 @@ export class FPSDisplay extends LitElement implements Layer {
e.preventDefault();
};
private handleMouseMove = (e: MouseEvent) => {
private readonly handleMouseMove = (e: MouseEvent) => {
if (!this.isDragging) return;
const newX = e.clientX - this.dragStart.x;
@@ -153,7 +153,7 @@ export class FPSDisplay extends LitElement implements Layer {
this.requestUpdate();
};
private handleMouseUp = () => {
private readonly handleMouseUp = () => {
this.isDragging = false;
document.removeEventListener("mousemove", this.handleMouseMove);
document.removeEventListener("mouseup", this.handleMouseUp);
+4 -4
View File
@@ -22,14 +22,14 @@ export class FxLayer implements Layer {
private context: CanvasRenderingContext2D;
private lastRefresh = 0;
private refreshRate = 10;
private theme: Theme;
private animatedSpriteLoader: AnimatedSpriteLoader =
private readonly refreshRate = 10;
private readonly theme: Theme;
private readonly animatedSpriteLoader: AnimatedSpriteLoader =
new AnimatedSpriteLoader();
private allFx: Fx[] = [];
constructor(private game: GameView) {
constructor(private readonly game: GameView) {
this.theme = this.game.config().theme();
}
@@ -64,7 +64,7 @@ export class GameRightSidebar extends LitElement implements Layer {
}
}
private secondsToHms = (d: number): string => {
private readonly secondsToHms = (d: number): string => {
const h = Math.floor(d / 3600);
const m = Math.floor((d % 3600) / 60);
const s = Math.floor((d % 3600) % 60);
+5 -5
View File
@@ -18,11 +18,11 @@ export class GutterAdModal extends LitElement implements Layer {
@state()
private adLoaded = false;
private leftAdType = "left_rail";
private rightAdType = "right_rail";
private leftContainerId = "gutter-ad-container-left";
private rightContainerId = "gutter-ad-container-right";
private margin = "10px";
private readonly leftAdType = "left_rail";
private readonly rightAdType = "right_rail";
private readonly leftContainerId = "gutter-ad-container-left";
private readonly rightContainerId = "gutter-ad-container-right";
private readonly margin = "10px";
// Override createRenderRoot to disable shadow DOM
createRenderRoot() {
+10 -10
View File
@@ -24,21 +24,21 @@ import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
@customElement("main-radial-menu")
export class MainRadialMenu extends LitElement implements Layer {
private radialMenu: RadialMenu;
private readonly radialMenu: RadialMenu;
private playerActionHandler: PlayerActionHandler;
private chatIntegration: ChatIntegration;
private readonly playerActionHandler: PlayerActionHandler;
private readonly chatIntegration: ChatIntegration;
private clickedTile: TileRef | null = null;
constructor(
private eventBus: EventBus,
private game: GameView,
private transformHandler: TransformHandler,
private emojiTable: EmojiTable,
private buildMenu: BuildMenu,
private uiState: UIState,
private playerPanel: PlayerPanel,
private readonly eventBus: EventBus,
private readonly game: GameView,
private readonly transformHandler: TransformHandler,
private readonly emojiTable: EmojiTable,
private readonly buildMenu: BuildMenu,
private readonly uiState: UIState,
private readonly playerPanel: PlayerPanel,
) {
super();
+20 -20
View File
@@ -38,33 +38,33 @@ class RenderInfo {
export class NameLayer implements Layer {
private canvas: HTMLCanvasElement;
private lastChecked = 0;
private renderCheckRate = 100;
private renderRefreshRate = 500;
private rand = new PseudoRandom(10);
private readonly renderCheckRate = 100;
private readonly renderRefreshRate = 500;
private readonly rand = new PseudoRandom(10);
private renders: RenderInfo[] = [];
private seenPlayers: Set<PlayerView> = new Set();
private traitorIconImage: HTMLImageElement;
private disconnectedIconImage: HTMLImageElement;
private allianceRequestBlackIconImage: HTMLImageElement;
private allianceRequestWhiteIconImage: HTMLImageElement;
private allianceIconImage: HTMLImageElement;
private targetIconImage: HTMLImageElement;
private crownIconImage: HTMLImageElement;
private embargoBlackIconImage: HTMLImageElement;
private embargoWhiteIconImage: HTMLImageElement;
private nukeWhiteIconImage: HTMLImageElement;
private nukeRedIconImage: HTMLImageElement;
private shieldIconImage: HTMLImageElement;
private readonly seenPlayers: Set<PlayerView> = new Set();
private readonly traitorIconImage: HTMLImageElement;
private readonly disconnectedIconImage: HTMLImageElement;
private readonly allianceRequestBlackIconImage: HTMLImageElement;
private readonly allianceRequestWhiteIconImage: HTMLImageElement;
private readonly allianceIconImage: HTMLImageElement;
private readonly targetIconImage: HTMLImageElement;
private readonly crownIconImage: HTMLImageElement;
private readonly embargoBlackIconImage: HTMLImageElement;
private readonly embargoWhiteIconImage: HTMLImageElement;
private readonly nukeWhiteIconImage: HTMLImageElement;
private readonly nukeRedIconImage: HTMLImageElement;
private readonly shieldIconImage: HTMLImageElement;
private container: HTMLDivElement;
private firstPlace: PlayerView | null = null;
private theme: Theme = this.game.config().theme();
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
private isVisible = true;
constructor(
private game: GameView,
private transformHandler: TransformHandler,
private eventBus: EventBus,
private readonly game: GameView,
private readonly transformHandler: TransformHandler,
private readonly eventBus: EventBus,
) {
this.traitorIconImage = new Image();
this.traitorIconImage.src = traitorIcon;
+1 -1
View File
@@ -45,7 +45,7 @@ const secondsToHms = (d: number): string => {
export class OptionsMenu extends LitElement implements Layer {
public game: GameView;
public eventBus: EventBus;
private userSettings: UserSettings = new UserSettings();
private readonly userSettings: UserSettings = new UserSettings();
@state()
private showPauseButton = true;
@@ -20,8 +20,8 @@ import { UIState } from "../UIState";
export class PlayerActionHandler {
constructor(
private eventBus: EventBus,
private uiState: UIState,
private readonly eventBus: EventBus,
private readonly uiState: UIState,
) {}
async getPlayerActions(
+9 -9
View File
@@ -56,20 +56,20 @@ export class RadialMenu implements Layer {
private isTransitioning = false;
private lastHideTime = 0;
private reopenCooldownMs = 300;
private readonly reopenCooldownMs = 300;
private anchorX = 0;
private anchorY = 0;
private menuGroups: Map<
private readonly menuGroups: Map<
number,
d3.Selection<SVGGElement, unknown, null, undefined>
> = new Map();
private menuPaths: Map<
private readonly menuPaths: Map<
string,
d3.Selection<SVGPathElement, unknown, null, undefined>
> = new Map();
private menuIcons: Map<
private readonly menuIcons: Map<
string,
d3.Selection<SVGImageElement, unknown, null, undefined>
> = new Map();
@@ -78,14 +78,14 @@ export class RadialMenu implements Layer {
private submenuHoverTimeout: number | null = null;
private backButtonHoverTimeout: number | null = null;
private navigationInProgress = false;
private originalCenterButtonIcon = "";
private readonly originalCenterButtonIcon: string = "";
private params: MenuElementParams | null = null;
constructor(
private eventBus: EventBus,
private rootMenu: MenuElement,
private centerButtonElement: CenterButtonElement,
private readonly eventBus: EventBus,
private readonly rootMenu: MenuElement,
private readonly centerButtonElement: CenterButtonElement,
config: RadialMenuConfig = {},
) {
this.config = {
@@ -1069,7 +1069,7 @@ export class RadialMenu implements Layer {
.style("left", `${clampedX}px`);
}
private handleResize = () => {
private readonly handleResize = () => {
if (this.isVisible) this.clampAndSetMenuPositionForLevel(this.currentLevel);
};
}
+3 -3
View File
@@ -21,13 +21,13 @@ type RailRef = {
export class RailroadLayer implements Layer {
private canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D;
private theme: Theme;
private readonly theme: Theme;
// Save the number of railroads per tiles. Delete when it reaches 0
private existingRailroads = new Map<TileRef, RailRef>();
private readonly existingRailroads = new Map<TileRef, RailRef>();
private nextRailIndexToCheck = 0;
private railTileList: TileRef[] = [];
constructor(private game: GameView) {
constructor(private readonly game: GameView) {
this.theme = game.config().theme();
}
+3 -3
View File
@@ -36,7 +36,7 @@ export class SettingsModal extends LitElement implements Layer {
private alternateView = false;
@query(".modal-overlay")
private modalOverlay!: HTMLElement;
private readonly modalOverlay!: HTMLElement;
@property({ type: Boolean })
shouldPause = false;
@@ -69,7 +69,7 @@ export class SettingsModal extends LitElement implements Layer {
super.disconnectedCallback();
}
private handleOutsideClick = (event: MouseEvent) => {
private readonly handleOutsideClick = (event: MouseEvent) => {
if (
this.isVisible &&
this.modalOverlay &&
@@ -79,7 +79,7 @@ export class SettingsModal extends LitElement implements Layer {
}
};
private handleKeyDown = (event: KeyboardEvent) => {
private readonly handleKeyDown = (event: KeyboardEvent) => {
if (this.isVisible && event.key === "Escape") {
this.closeModal();
}
+2 -2
View File
@@ -8,8 +8,8 @@ export class SpawnTimer implements Layer {
private colors = ["rgba(0, 128, 255, 0.7)", "rgba(0, 0, 0, 0.5)"];
constructor(
private game: GameView,
private transformHandler: TransformHandler,
private readonly game: GameView,
private readonly transformHandler: TransformHandler,
) {}
init() {}
@@ -59,12 +59,12 @@ export class StructureIconsLayer implements Layer {
private levelsStage: PIXI.Container;
private dotsStage: PIXI.Container;
private shouldRedraw = true;
private textureCache: Map<string, PIXI.Texture> = new Map();
private theme: Theme;
private readonly textureCache: Map<string, PIXI.Texture> = new Map();
private readonly theme: Theme;
private renderer: PIXI.Renderer;
private renders: StructureRenderInfo[] = [];
private seenUnits: Set<UnitView> = new Set();
private structures: Map<
private readonly seenUnits: Set<UnitView> = new Set();
private readonly structures: Map<
UnitType,
{ visible: boolean; iconPath: string; image: HTMLImageElement | null }
> = new Map([
@@ -87,9 +87,9 @@ export class StructureIconsLayer implements Layer {
private renderSprites = true;
constructor(
private game: GameView,
private eventBus: EventBus,
private transformHandler: TransformHandler,
private readonly game: GameView,
private readonly eventBus: EventBus,
private readonly transformHandler: TransformHandler,
) {
this.theme = game.config().theme();
this.structures.forEach((u, unitType) => this.loadIcon(u, unitType));
+7 -7
View File
@@ -31,10 +31,10 @@ type UnitRenderConfig = {
export class StructureLayer implements Layer {
private canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D;
private unitIcons: Map<string, HTMLImageElement> = new Map();
private theme: Theme;
private tempCanvas: HTMLCanvasElement;
private tempContext: CanvasRenderingContext2D;
private readonly unitIcons: Map<string, HTMLImageElement> = new Map();
private readonly theme: Theme;
private readonly tempCanvas: HTMLCanvasElement;
private readonly tempContext: CanvasRenderingContext2D;
// Configuration for supported unit types only
private readonly unitConfigs: Partial<Record<UnitType, UnitRenderConfig>> = {
@@ -71,9 +71,9 @@ export class StructureLayer implements Layer {
};
constructor(
private game: GameView,
private eventBus: EventBus,
private transformHandler: TransformHandler,
private readonly game: GameView,
private readonly eventBus: EventBus,
private readonly transformHandler: TransformHandler,
) {
this.theme = game.config().theme();
this.tempCanvas = document.createElement("canvas");
+2 -2
View File
@@ -10,8 +10,8 @@ export class TerrainLayer implements Layer {
private theme: Theme;
constructor(
private game: GameView,
private transformHandler: TransformHandler,
private readonly game: GameView,
private readonly transformHandler: TransformHandler,
) {}
shouldTransform(): boolean {
return true;
+10 -10
View File
@@ -18,7 +18,7 @@ import { TransformHandler } from "../TransformHandler";
import { UserSettings } from "../../../core/game/UserSettings";
export class TerritoryLayer implements Layer {
private userSettings: UserSettings;
private readonly userSettings: UserSettings;
private canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D;
private imageData: ImageData;
@@ -26,14 +26,14 @@ export class TerritoryLayer implements Layer {
private cachedTerritoryPatternsEnabled: boolean | undefined;
private tileToRenderQueue: PriorityQueue<{
private readonly tileToRenderQueue: PriorityQueue<{
tile: TileRef;
lastUpdate: number;
}> = new PriorityQueue((a, b) => {
return a.lastUpdate - b.lastUpdate;
});
private random = new PseudoRandom(123);
private theme: Theme;
private readonly random = new PseudoRandom(123);
private readonly theme: Theme;
// Used for spawn highlighting
private highlightCanvas: HTMLCanvasElement;
@@ -42,19 +42,19 @@ export class TerritoryLayer implements Layer {
private highlightedTerritory: PlayerView | null = null;
private alternativeView = false;
private lastDragTime = 0;
private nodrawDragDuration = 200;
private readonly lastDragTime = 0;
private readonly nodrawDragDuration = 200;
private lastMousePosition: { x: number; y: number } | null = null;
private refreshRate = 10; //refresh every 10ms
private readonly refreshRate = 10; //refresh every 10ms
private lastRefresh = 0;
private lastFocusedPlayer: PlayerView | null = null;
constructor(
private game: GameView,
private eventBus: EventBus,
private transformHandler: TransformHandler,
private readonly game: GameView,
private readonly eventBus: EventBus,
private readonly transformHandler: TransformHandler,
userSettings: UserSettings,
) {
this.userSettings = userSettings;
+7 -7
View File
@@ -27,14 +27,14 @@ const PROGRESSBAR_HEIGHT = 3; // Height of a bar
export class UILayer implements Layer {
private canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D | null;
private theme: Theme | null = null;
private userSettings: UserSettings = new UserSettings();
private readonly theme: Theme | null = null;
private readonly userSettings: UserSettings = new UserSettings();
private selectionAnimTime = 0;
private allProgressBars: Map<
private readonly allProgressBars: Map<
number,
{ unit: UnitView; progressBar: ProgressBar }
> = new Map();
private allHealthBars: Map<number, ProgressBar> = new Map();
private readonly allHealthBars: Map<number, ProgressBar> = new Map();
// Keep track of currently selected unit
private selectedUnit: UnitView | null = null;
@@ -49,9 +49,9 @@ export class UILayer implements Layer {
private readonly SELECTION_BOX_SIZE = 6; // Size of the selection box (should be larger than the warship)
constructor(
private game: GameView,
private eventBus: EventBus,
private transformHandler: TransformHandler,
private readonly game: GameView,
private readonly eventBus: EventBus,
private readonly transformHandler: TransformHandler,
) {
this.theme = game.config().theme();
}
+1 -1
View File
@@ -17,7 +17,7 @@ import samLauncherIcon from "../../../../resources/non-commercial/svg/SamLaunche
export class UnitDisplay extends LitElement implements Layer {
public game: GameView;
public eventBus: EventBus;
private _selectedStructure: UnitType | null = null;
private readonly _selectedStructure: UnitType | null = null;
private _cities = 0;
private _factories = 0;
private _missileSilo = 0;
+6 -6
View File
@@ -32,15 +32,15 @@ export class UnitLayer implements Layer {
private transportShipTrailCanvas: HTMLCanvasElement;
private unitTrailContext: CanvasRenderingContext2D;
private unitToTrail = new Map<UnitView, TileRef[]>();
private readonly unitToTrail = new Map<UnitView, TileRef[]>();
private theme: Theme;
private readonly theme: Theme;
private alternateView = false;
private oldShellTile = new Map<UnitView, TileRef>();
private readonly oldShellTile = new Map<UnitView, TileRef>();
private transformHandler: TransformHandler;
private readonly transformHandler: TransformHandler;
// Selected unit property as suggested in the review comment
private selectedUnit: UnitView | null = null;
@@ -49,8 +49,8 @@ export class UnitLayer implements Layer {
private readonly WARSHIP_SELECTION_RADIUS = 10; // Radius in game cells for warship selection hit zone
constructor(
private game: GameView,
private eventBus: EventBus,
private readonly game: GameView,
private readonly eventBus: EventBus,
transformHandler: TransformHandler,
) {
this.theme = game.config().theme();
+1 -1
View File
@@ -6,7 +6,7 @@ export type EventConstructor<T extends GameEvent = GameEvent> = new (
) => T;
export class EventBus {
private listeners: Map<EventConstructor, Array<(event: GameEvent) => void>> =
private readonly listeners: Map<EventConstructor, Array<(event: GameEvent) => void>> =
new Map();
emit<T extends GameEvent>(event: T): void {
+3 -3
View File
@@ -83,7 +83,7 @@ export async function createGameRunner(
}
export class GameRunner {
private turns: Turn[] = [];
private readonly turns: Turn[] = [];
private currTurn = 0;
private isExecuting = false;
@@ -91,8 +91,8 @@ export class GameRunner {
constructor(
public game: Game,
private execManager: Executor,
private callBack: (gu: GameUpdateViewData | ErrorUpdate) => void,
private readonly execManager: Executor,
private readonly callBack: (gu: GameUpdateViewData | ErrorUpdate) => void,
) {}
init() {
+1 -1
View File
@@ -1,5 +1,5 @@
export class PatternDecoder {
private bytes: Uint8Array;
private readonly bytes: Uint8Array;
readonly height: number;
readonly width: number;
+1 -1
View File
@@ -1,7 +1,7 @@
import seedrandom from "seedrandom";
export class PseudoRandom {
private rng: seedrandom.PRNG;
private readonly rng: seedrandom.PRNG;
private static readonly POW36_8 = Math.pow(36, 8); // Pre-compute 36^8
+3 -3
View File
@@ -20,9 +20,9 @@ extend([labPlugin]);
export class ColorAllocator {
private availableColors: Colord[];
private fallbackColors: Colord[];
private assigned = new Map<string, Colord>();
private teamPlayerColors = new Map<string, Colord>();
private readonly fallbackColors: Colord[];
private readonly assigned = new Map<string, Colord>();
private readonly teamPlayerColors = new Map<string, Colord>();
constructor(colors: Colord[], fallback: Colord[]) {
this.availableColors = [...colors];
+6 -6
View File
@@ -209,13 +209,13 @@ export abstract class DefaultServerConfig implements ServerConfig {
}
export class DefaultConfig implements Config {
private pastelTheme: PastelTheme = new PastelTheme();
private pastelThemeDark: PastelThemeDark = new PastelThemeDark();
private readonly pastelTheme: PastelTheme = new PastelTheme();
private readonly pastelThemeDark: PastelThemeDark = new PastelThemeDark();
constructor(
private _serverConfig: ServerConfig,
private _gameConfig: GameConfig,
private _userSettings: UserSettings | null,
private _isReplay: boolean,
private readonly _serverConfig: ServerConfig,
private readonly _gameConfig: GameConfig,
private readonly _userSettings: UserSettings | null,
private readonly _isReplay: boolean,
) {}
stripePublishableKey(): string {
+16 -16
View File
@@ -10,32 +10,32 @@ import { Theme } from "./Config";
type ColorCache = Map<string, Colord>;
export class PastelTheme implements Theme {
private borderColorCache: ColorCache = new Map<string, Colord>();
private rand = new PseudoRandom(123);
private humanColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private botColorAllocator = new ColorAllocator(botColors, botColors);
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
private readonly borderColorCache: ColorCache = new Map<string, Colord>();
private readonly rand = new PseudoRandom(123);
private readonly humanColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private readonly botColorAllocator = new ColorAllocator(botColors, botColors);
private readonly teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private readonly nationColorAllocator = new ColorAllocator(nationColors, nationColors);
/* eslint-disable sort-keys */
private background = colord({ r: 60, g: 60, b: 60 });
private shore = colord({ r: 204, g: 203, b: 158 });
private falloutColors = [
private readonly background = colord({ r: 60, g: 60, b: 60 });
private readonly shore = colord({ r: 204, g: 203, b: 158 });
private readonly falloutColors = [
colord({ r: 120, g: 255, b: 71 }), // Original color
colord({ r: 130, g: 255, b: 85 }), // Slightly lighter
colord({ r: 110, g: 245, b: 65 }), // Slightly darker
colord({ r: 125, g: 255, b: 75 }), // Warmer tint
colord({ r: 115, g: 250, b: 68 }), // Cooler tint
];
private water = colord({ r: 70, g: 132, b: 180 });
private shorelineWater = colord({ r: 100, g: 143, b: 255 });
private readonly water = colord({ r: 70, g: 132, b: 180 });
private readonly shorelineWater = colord({ r: 100, g: 143, b: 255 });
private _selfColor = colord({ r: 0, g: 255, b: 0 });
private _allyColor = colord({ r: 255, g: 255, b: 0 });
private _neutralColor = colord({ r: 128, g: 128, b: 128 });
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
private readonly _selfColor = colord({ r: 0, g: 255, b: 0 });
private readonly _allyColor = colord({ r: 255, g: 255, b: 0 });
private readonly _neutralColor = colord({ r: 128, g: 128, b: 128 });
private readonly _enemyColor = colord({ r: 255, g: 0, b: 0 });
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
private readonly _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
/* eslint-enable sort-keys */
teamColor(team: Team): Colord {
+16 -16
View File
@@ -10,32 +10,32 @@ import { Theme } from "./Config";
type ColorCache = Map<string, Colord>;
export class PastelThemeDark implements Theme {
private borderColorCache: ColorCache = new Map<string, Colord>();
private rand = new PseudoRandom(123);
private humanColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private botColorAllocator = new ColorAllocator(botColors, botColors);
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
private readonly borderColorCache: ColorCache = new Map<string, Colord>();
private readonly rand = new PseudoRandom(123);
private readonly humanColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private readonly botColorAllocator = new ColorAllocator(botColors, botColors);
private readonly teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private readonly nationColorAllocator = new ColorAllocator(nationColors, nationColors);
/* eslint-disable sort-keys */
private background = colord({ r: 0, g: 0, b: 0 });
private shore = colord({ r: 134, g: 133, b: 88 });
private falloutColors = [
private readonly background = colord({ r: 0, g: 0, b: 0 });
private readonly shore = colord({ r: 134, g: 133, b: 88 });
private readonly falloutColors = [
colord({ r: 120, g: 255, b: 71 }), // Original color
colord({ r: 130, g: 255, b: 85 }), // Slightly lighter
colord({ r: 110, g: 245, b: 65 }), // Slightly darker
colord({ r: 125, g: 255, b: 75 }), // Warmer tint
colord({ r: 115, g: 250, b: 68 }), // Cooler tint
];
private water = colord({ r: 14, g: 11, b: 30 });
private shorelineWater = colord({ r: 50, g: 50, b: 50 });
private readonly water = colord({ r: 14, g: 11, b: 30 });
private readonly shorelineWater = colord({ r: 50, g: 50, b: 50 });
private _selfColor = colord({ r: 0, g: 255, b: 0 });
private _allyColor = colord({ r: 255, g: 255, b: 0 });
private _neutralColor = colord({ r: 128, g: 128, b: 128 });
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
private readonly _selfColor = colord({ r: 0, g: 255, b: 0 });
private readonly _allyColor = colord({ r: 255, g: 255, b: 0 });
private readonly _neutralColor = colord({ r: 128, g: 128, b: 128 });
private readonly _enemyColor = colord({ r: 255, g: 0, b: 0 });
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
private readonly _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
/* eslint-enable sort-keys */
teamColor(team: Team): Colord {
+6 -6
View File
@@ -19,9 +19,9 @@ export class AttackExecution implements Execution {
private breakAlliance = false;
private wasAlliedAtInit = false; // Store alliance state at initialization
private active = true;
private toConquer = new FlatBinaryHeap();
private readonly toConquer = new FlatBinaryHeap();
private random = new PseudoRandom(123);
private readonly random = new PseudoRandom(123);
private target: Player | TerraNullius;
@@ -31,10 +31,10 @@ export class AttackExecution implements Execution {
constructor(
private startTroops: number | null = null,
private _owner: Player,
private _targetID: PlayerID | null,
private sourceTile: TileRef | null = null,
private removeTroops = true,
private readonly _owner: Player,
private readonly _targetID: PlayerID | null,
private readonly sourceTile: TileRef | null = null,
private readonly removeTroops = true,
) {}
public targetID(): PlayerID | null {
+2 -2
View File
@@ -3,8 +3,8 @@ import { Execution, Game, Player, UnitType } from "../game/Game";
export class BoatRetreatExecution implements Execution {
private active = true;
constructor(
private player: Player,
private unitID: number,
private readonly player: Player,
private readonly unitID: number,
) {}
init(mg: Game, ticks: number): void {}
+7 -7
View File
@@ -5,18 +5,18 @@ import { simpleHash } from "../Util";
export class BotExecution implements Execution {
private active = true;
private random: PseudoRandom;
private readonly random: PseudoRandom;
private mg: Game;
private neighborsTerraNullius = true;
private behavior: BotBehavior | null = null;
private attackRate: number;
private attackTick: number;
private triggerRatio: number;
private reserveRatio: number;
private expandRatio: number;
private readonly attackRate: number;
private readonly attackTick: number;
private readonly triggerRatio: number;
private readonly reserveRatio: number;
private readonly expandRatio: number;
constructor(private bot: Player) {
constructor(private readonly bot: Player) {
this.random = new PseudoRandom(simpleHash(bot.id()));
this.attackRate = this.random.nextInt(40, 80);
this.attackTick = this.random.nextInt(0, this.attackRate);
+3 -3
View File
@@ -7,11 +7,11 @@ import { TileRef } from "../game/GameMap";
import { simpleHash } from "../Util";
export class BotSpawner {
private random: PseudoRandom;
private bots: SpawnExecution[] = [];
private readonly random: PseudoRandom;
private readonly bots: SpawnExecution[] = [];
constructor(
private gs: Game,
private readonly gs: Game,
gameID: GameID,
) {
this.random = new PseudoRandom(simpleHash(gameID));
+1 -1
View File
@@ -9,7 +9,7 @@ export class CityExecution implements Execution {
constructor(
private player: Player,
private tile: TileRef,
private readonly tile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+2 -2
View File
@@ -29,8 +29,8 @@ export class ConstructionExecution implements Execution {
constructor(
private player: Player,
private constructionType: UnitType,
private tile: TileRef,
private readonly constructionType: UnitType,
private readonly tile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+2 -2
View File
@@ -10,11 +10,11 @@ export class DefensePostExecution implements Execution {
private target: Unit | null = null;
private lastShellAttack = 0;
private alreadySentShell = new Set<Unit>();
private readonly alreadySentShell = new Set<Unit>();
constructor(
private player: Player,
private tile: TileRef,
private readonly tile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+2 -2
View File
@@ -5,8 +5,8 @@ export class DeleteUnitExecution implements Execution {
private mg: Game;
constructor(
private player: Player,
private unitId: number,
private readonly player: Player,
private readonly unitId: number,
) {}
activeDuringSpawnPhase(): boolean {
+2 -2
View File
@@ -6,8 +6,8 @@ export class DonateGoldExecution implements Execution {
private active = true;
constructor(
private sender: Player,
private recipientID: PlayerID,
private readonly sender: Player,
private readonly recipientID: PlayerID,
private gold: Gold | null,
) {}
+2 -2
View File
@@ -6,8 +6,8 @@ export class DonateTroopsExecution implements Execution {
private active = true;
constructor(
private sender: Player,
private recipientID: PlayerID,
private readonly sender: Player,
private readonly recipientID: PlayerID,
private troops: number | null,
) {}
+2 -2
View File
@@ -6,8 +6,8 @@ export class EmbargoExecution implements Execution {
private target: Player;
constructor(
private player: Player,
private targetID: PlayerID,
private readonly player: Player,
private readonly targetID: PlayerID,
private readonly action: "start" | "stop",
) {}
+3 -3
View File
@@ -14,9 +14,9 @@ export class EmojiExecution implements Execution {
private active = true;
constructor(
private requestor: Player,
private recipientID: PlayerID | typeof AllPlayers,
private emoji: number,
private readonly requestor: Player,
private readonly recipientID: PlayerID | typeof AllPlayers,
private readonly emoji: number,
) {}
init(mg: Game, ticks: number): void {
+4 -4
View File
@@ -28,12 +28,12 @@ import { simpleHash } from "../Util";
export class Executor {
// private random = new PseudoRandom(999)
private random: PseudoRandom;
private readonly random: PseudoRandom;
constructor(
private mg: Game,
private gameID: GameID,
private clientID: ClientID,
private readonly mg: Game,
private readonly gameID: GameID,
private readonly clientID: ClientID,
) {
// Add one to avoid id collisions with bots.
this.random = new PseudoRandom(simpleHash(gameID) + 1);
+1 -1
View File
@@ -8,7 +8,7 @@ export class FactoryExecution implements Execution {
private game: Game;
constructor(
private player: Player,
private tile: TileRef,
private readonly tile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+11 -11
View File
@@ -28,25 +28,25 @@ import { closestTwoTiles } from "./Util";
export class FakeHumanExecution implements Execution {
private active = true;
private random: PseudoRandom;
private readonly random: PseudoRandom;
private behavior: BotBehavior | null = null;
private mg: Game;
private player: Player | null = null;
private attackRate: number;
private attackTick: number;
private triggerRatio: number;
private reserveRatio: number;
private expandRatio: number;
private readonly attackRate: number;
private readonly attackTick: number;
private readonly triggerRatio: number;
private readonly reserveRatio: number;
private readonly expandRatio: number;
private lastEmojiSent = new Map<Player, Tick>();
private lastNukeSent: [Tick, TileRef][] = [];
private embargoMalusApplied = new Set<PlayerID>();
private heckleEmoji: number[];
private readonly lastEmojiSent = new Map<Player, Tick>();
private readonly lastNukeSent: [Tick, TileRef][] = [];
private readonly embargoMalusApplied = new Set<PlayerID>();
private readonly heckleEmoji: number[];
constructor(
gameID: GameID,
private nation: Nation,
private readonly nation: Nation,
) {
this.random = new PseudoRandom(
simpleHash(nation.playerInfo.id) + simpleHash(gameID),
+4 -4
View File
@@ -20,8 +20,8 @@ export class MirvExecution implements Execution {
private nuke: Unit | null = null;
private mirvRange = 1500;
private warheadCount = 350;
private readonly mirvRange = 1500;
private readonly warheadCount = 350;
private random: PseudoRandom;
@@ -34,8 +34,8 @@ export class MirvExecution implements Execution {
private speed = -1;
constructor(
private player: Player,
private dst: TileRef,
private readonly player: Player,
private readonly dst: TileRef,
) {}
init(mg: Game, ticks: number): void {
@@ -2,8 +2,8 @@ import { Execution, Game, Player } from "../game/Game";
export class MarkDisconnectedExecution implements Execution {
constructor(
private player: Player,
private isDisconnected: boolean,
private readonly player: Player,
private readonly isDisconnected: boolean,
) {}
init(mg: Game, ticks: number): void {
+1 -1
View File
@@ -8,7 +8,7 @@ export class MissileSiloExecution implements Execution {
constructor(
private player: Player,
private tile: TileRef,
private readonly tile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+3 -3
View File
@@ -24,9 +24,9 @@ export class NukeExecution implements Execution {
private pathFinder: ParabolaPathFinder;
constructor(
private nukeType: NukeType,
private player: Player,
private dst: TileRef,
private readonly nukeType: NukeType,
private readonly player: Player,
private readonly dst: TileRef,
private src?: TileRef | null,
private speed = -1,
private waitTicks = 0,
+1 -1
View File
@@ -12,7 +12,7 @@ export class PlayerExecution implements Execution {
private mg: Game;
private active = true;
constructor(private player: Player) {}
constructor(private readonly player: Player) {}
activeDuringSpawnPhase(): boolean {
return false;
+1 -1
View File
@@ -13,7 +13,7 @@ export class PortExecution implements Execution {
constructor(
private player: Player,
private tile: TileRef,
private readonly tile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+4 -4
View File
@@ -7,10 +7,10 @@ export class QuickChatExecution implements Execution {
private active = true;
constructor(
private sender: Player,
private recipientID: PlayerID,
private quickChatKey: string,
private target: PlayerID | undefined,
private readonly sender: Player,
private readonly recipientID: PlayerID,
private readonly quickChatKey: string,
private readonly target: PlayerID | undefined,
) {}
init(mg: Game, ticks: number): void {
+3 -3
View File
@@ -8,9 +8,9 @@ export class RailroadExecution implements Execution {
private active = true;
private headIndex = 0;
private tailIndex = 0;
private increment = 3;
private railTiles: RailTile[] = [];
constructor(private railRoad: Railroad) {
private readonly increment = 3;
private readonly railTiles: RailTile[] = [];
constructor(private readonly railRoad: Railroad) {
this.tailIndex = railRoad.tiles.length;
}
+2 -2
View File
@@ -8,8 +8,8 @@ export class RetreatExecution implements Execution {
private startTick: number;
private mg: Game;
constructor(
private player: Player,
private attackID: string,
private readonly player: Player,
private readonly attackID: string,
) {}
init(mg: Game, ticks: number): void {
+6 -6
View File
@@ -21,11 +21,11 @@ type Target = {
*/
class SAMTargetingSystem {
// Store unreachable nukes so the SAM won't compute an interception point for them every frame
private nukesToIgnore: Set<number> = new Set();
private readonly nukesToIgnore: Set<number> = new Set();
constructor(
private mg: Game,
private sam: Unit,
private readonly mg: Game,
private readonly sam: Unit,
) {}
updateUnreachableNukes(nearbyUnits: { unit: Unit; distSquared: number }[]) {
@@ -131,15 +131,15 @@ export class SAMLauncherExecution implements Execution {
// As MIRV go very fast we have to detect them very early but we only
// shoot the one targeting very close (MIRVWarheadProtectionRadius)
private MIRVWarheadSearchRadius = 400;
private MIRVWarheadProtectionRadius = 50;
private readonly MIRVWarheadSearchRadius = 400;
private readonly MIRVWarheadProtectionRadius = 50;
private targetingSystem: SAMTargetingSystem;
private pseudoRandom: PseudoRandom | undefined;
constructor(
private player: Player,
private tile: TileRef | null,
private readonly tile: TileRef | null,
private sam: Unit | null = null,
) {
if (sam !== null) {
+5 -5
View File
@@ -19,11 +19,11 @@ export class SAMMissileExecution implements Execution {
private speed = 0;
constructor(
private spawn: TileRef,
private _owner: Player,
private ownerUnit: Unit,
private target: Unit,
private targetTile: TileRef,
private readonly spawn: TileRef,
private readonly _owner: Player,
private readonly ownerUnit: Unit,
private readonly target: Unit,
private readonly targetTile: TileRef,
) {}
init(mg: Game, ticks: number): void {
+4 -4
View File
@@ -12,10 +12,10 @@ export class ShellExecution implements Execution {
private random: PseudoRandom;
constructor(
private spawn: TileRef,
private _owner: Player,
private ownerUnit: Unit,
private target: Unit,
private readonly spawn: TileRef,
private readonly _owner: Player,
private readonly ownerUnit: Unit,
private readonly target: Unit,
) {}
init(mg: Game, ticks: number): void {
+1 -1
View File
@@ -9,7 +9,7 @@ export class SpawnExecution implements Execution {
private mg: Game;
constructor(
private playerInfo: PlayerInfo,
private readonly playerInfo: PlayerInfo,
public readonly tile: TileRef,
) {}
+2 -2
View File
@@ -6,8 +6,8 @@ export class TargetPlayerExecution implements Execution {
private active = true;
constructor(
private requestor: Player,
private targetID: PlayerID,
private readonly requestor: Player,
private readonly targetID: PlayerID,
) {}
init(mg: Game, ticks: number): void {
+2 -2
View File
@@ -21,8 +21,8 @@ export class TradeShipExecution implements Execution {
private tilesTraveled = 0;
constructor(
private origOwner: Player,
private srcPort: Unit,
private readonly origOwner: Player,
private readonly srcPort: Unit,
private _dstPort: Unit,
) {}
+9 -9
View File
@@ -15,21 +15,21 @@ export class TrainExecution implements Execution {
private active = true;
private mg: Game | null = null;
private train: Unit | null = null;
private cars: Unit[] = [];
private readonly cars: Unit[] = [];
private hasCargo = false;
private currentTile = 0;
private spacing = 2;
private usedTiles: TileRef[] = []; // used for cars behind
private readonly spacing = 2;
private readonly usedTiles: TileRef[] = []; // used for cars behind
private stations: TrainStation[] = [];
private currentRailroad: OrientedRailroad | null = null;
private speed = 2;
private readonly speed = 2;
constructor(
private railNetwork: RailNetwork,
private player: Player,
private source: TrainStation,
private destination: TrainStation,
private numCars: number,
private readonly railNetwork: RailNetwork,
private readonly player: Player,
private readonly source: TrainStation,
private readonly destination: TrainStation,
private readonly numCars: number,
) {}
public owner(): Player {
+4 -4
View File
@@ -8,12 +8,12 @@ export class TrainStationExecution implements Execution {
private active = true;
private random: PseudoRandom;
private station: TrainStation | null = null;
private numCars = 5;
private readonly numCars = 5;
private lastSpawnTick = 0;
private ticksCooldown = 10; // Minimum cooldown between two trains
private readonly ticksCooldown = 10; // Minimum cooldown between two trains
constructor(
private unit: Unit,
private spawnTrains?: boolean, // If set, the station will spawn trains
private readonly unit: Unit,
private readonly spawnTrains?: boolean, // If set, the station will spawn trains
) {
this.unit.setTrainStation(true);
}
+4 -4
View File
@@ -18,7 +18,7 @@ export class TransportShipExecution implements Execution {
private lastMove: number;
// TODO: make this configurable
private ticksPerMove = 1;
private readonly ticksPerMove = 1;
private active = true;
@@ -34,9 +34,9 @@ export class TransportShipExecution implements Execution {
private pathFinder: PathFinder;
constructor(
private attacker: Player,
private targetID: PlayerID | null,
private ref: TileRef,
private readonly attacker: Player,
private readonly targetID: PlayerID | null,
private readonly ref: TileRef,
private startTroops: number,
private src: TileRef | null,
) {}
@@ -2,11 +2,11 @@ import { Execution, Game, Player, Unit } from "../game/Game";
export class UpgradeStructureExecution implements Execution {
private structure: Unit | undefined;
private cost: bigint;
private readonly cost: bigint;
constructor(
private player: Player,
private unitId: number,
private readonly player: Player,
private readonly unitId: number,
) {}
init(mg: Game, ticks: number): void {
+2 -2
View File
@@ -19,10 +19,10 @@ export class WarshipExecution implements Execution {
private mg: Game;
private pathfinder: PathFinder;
private lastShellAttack = 0;
private alreadySentShell = new Set<Unit>();
private readonly alreadySentShell = new Set<Unit>();
constructor(
private input: (UnitParams<UnitType.Warship> & OwnerComp) | Unit,
private readonly input: (UnitParams<UnitType.Warship> & OwnerComp) | Unit,
) {}
init(mg: Game, ticks: number): void {
@@ -5,8 +5,8 @@ export class AllianceRequestExecution implements Execution {
private recipient: Player | null = null;
constructor(
private requestor: Player,
private recipientID: PlayerID,
private readonly requestor: Player,
private readonly recipientID: PlayerID,
) {}
init(mg: Game, ticks: number): void {
@@ -5,9 +5,9 @@ export class AllianceRequestReplyExecution implements Execution {
private requestor: Player | null = null;
constructor(
private requestorID: PlayerID,
private recipient: Player,
private accept: boolean,
private readonly requestorID: PlayerID,
private readonly recipient: Player,
private readonly accept: boolean,
) {}
init(mg: Game, ticks: number): void {
@@ -6,8 +6,8 @@ export class BreakAllianceExecution implements Execution {
private mg: Game | null = null;
constructor(
private requestor: Player,
private recipientID: PlayerID,
private readonly requestor: Player,
private readonly recipientID: PlayerID,
) {}
init(mg: Game, ticks: number): void {

Some files were not shown because too many files have changed in this diff Show More