Enable various eslint rules (#1773)

## Description:

Enable various eslint rules.

## 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-11 22:14:00 -04:00
committed by GitHub
parent 8a41919ed7
commit ce49599229
109 changed files with 471 additions and 460 deletions
+13 -3
View File
@@ -48,10 +48,20 @@ export default [
{
rules: {
// Enable rules
"@typescript-eslint/consistent-type-assertions": [
"warn", // TODO: Raise this to error, https://github.com/openfrontio/OpenFrontIO/issues/1033
{ assertionStyle: "never" },
"@typescript-eslint/consistent-type-definitions": [
"error",
"type",
// TODO: { assertionStyle: "never" }, https://github.com/openfrontio/OpenFrontIO/issues/1033
],
"@typescript-eslint/no-duplicate-enum-values": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-mixed-enums": "error",
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/prefer-as-const": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-literal-enum-member": "error",
"@typescript-eslint/prefer-nullish-coalescing": "error",
eqeqeq: "error",
"sort-keys": "error",
+7 -7
View File
@@ -47,7 +47,7 @@ import {
import { createCanvas } from "./Utils";
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
export interface LobbyConfig {
export type LobbyConfig = {
serverConfig: ServerConfig;
pattern: string | undefined;
flag: string;
@@ -59,7 +59,7 @@ export interface LobbyConfig {
gameStartInfo?: GameStartInfo;
// GameRecord exists when replaying an archived game.
gameRecord?: GameRecord;
}
};
export function joinLobby(
eventBus: EventBus,
@@ -192,7 +192,7 @@ export class ClientGameRunner {
private lastMousePosition: { x: number; y: number } | null = null;
private lastMessageTime: number = 0;
private lastMessageTime = 0;
private connectionCheckInterval: NodeJS.Timeout | null = null;
constructor(
@@ -364,7 +364,7 @@ export class ClientGameRunner {
this.transport.connect(onconnect, onmessage);
}
public stop(saveFullGame: boolean = false) {
public stop(saveFullGame = false) {
if (!this.isActive) return;
this.isActive = false;
@@ -420,7 +420,7 @@ export class ClientGameRunner {
const owner = this.gameView.owner(tile);
if (owner.isPlayer()) {
this.gameView.setFocusedPlayer(owner as PlayerView);
this.gameView.setFocusedPlayer(owner);
} else {
this.gameView.setFocusedPlayer(null);
}
@@ -623,7 +623,7 @@ export class ClientGameRunner {
if (this.gameView.isLand(tile)) {
const owner = this.gameView.owner(tile);
if (owner.isPlayer()) {
this.gameView.setFocusedPlayer(owner as PlayerView);
this.gameView.setFocusedPlayer(owner);
} else {
this.gameView.setFocusedPlayer(null);
}
@@ -637,7 +637,7 @@ export class ClientGameRunner {
.sort((a, b) => a.distSquared - b.distSquared);
if (units.length > 0) {
this.gameView.setFocusedPlayer(units[0].unit.owner() as PlayerView);
this.gameView.setFocusedPlayer(units[0].unit.owner());
} else {
this.gameView.setFocusedPlayer(null);
}
+2 -2
View File
@@ -1,11 +1,11 @@
import { LitElement, css, html } from "lit";
import { customElement, state } from "lit/decorators.js";
import { renderPlayerFlag } from "../core/CustomFlag";
const flagKey: string = "flag";
const flagKey = "flag";
@customElement("flag-input")
export class FlagInput extends LitElement {
@state() public flag: string = "";
@state() public flag = "";
static styles = css`
@media (max-width: 768px) {
+1 -1
View File
@@ -9,7 +9,7 @@ export class FlagInputModal extends LitElement {
close: () => void;
};
@state() private search: string = "";
@state() private search = "";
createRenderRoot() {
return this;
+2 -1
View File
@@ -2,6 +2,7 @@ import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface Window {
adsbygoogle: unknown[];
}
@@ -108,7 +109,7 @@ const isElectron = () => {
if (
typeof navigator === "object" &&
typeof navigator.userAgent === "string" &&
navigator.userAgent.indexOf("Electron") >= 0
navigator.userAgent.includes("Electron")
) {
return true;
}
+9 -9
View File
@@ -40,19 +40,19 @@ export class HostLobbyModal extends LitElement {
@state() private disableNPCs = false;
@state() private gameMode: GameMode = GameMode.FFA;
@state() private teamCount: TeamCountConfig = 2;
@state() private bots: number = 400;
@state() private infiniteGold: boolean = false;
@state() private donateGold: boolean = false;
@state() private infiniteTroops: boolean = false;
@state() private donateTroops: boolean = false;
@state() private instantBuild: boolean = false;
@state() private bots = 400;
@state() private infiniteGold = false;
@state() private donateGold = false;
@state() private infiniteTroops = false;
@state() private donateTroops = false;
@state() private instantBuild = false;
@state() private lobbyId = "";
@state() private copySuccess = false;
@state() private clients: ClientInfo[] = [];
@state() private useRandomMap: boolean = false;
@state() private useRandomMap = false;
@state() private disabledUnits: UnitType[] = [];
@state() private lobbyCreatorClientID: string = "";
@state() private lobbyIdVisible: boolean = true;
@state() private lobbyCreatorClientID = "";
@state() private lobbyIdVisible = true;
private playersInterval: NodeJS.Timeout | null = null;
// Add a new timer for debouncing bot changes
+7 -7
View File
@@ -115,17 +115,17 @@ export class AutoUpgradeEvent implements GameEvent {
}
export class InputHandler {
private lastPointerX: number = 0;
private lastPointerY: number = 0;
private lastPointerX = 0;
private lastPointerY = 0;
private lastPointerDownX: number = 0;
private lastPointerDownY: number = 0;
private lastPointerDownX = 0;
private lastPointerDownY = 0;
private pointers: Map<number, PointerEvent> = new Map();
private lastPinchDistance: number = 0;
private lastPinchDistance = 0;
private pointerDown: boolean = false;
private pointerDown = false;
private alternateView = false;
@@ -163,7 +163,7 @@ export class InputHandler {
};
// Mac users might have different keybinds
const isMac = /Mac/.test(navigator.userAgent);
const isMac = navigator.userAgent.includes("Mac");
if (isMac) {
this.keybinds.modifierKey = "MetaLeft"; // Use Command key on Mac
}
+2 -2
View File
@@ -19,7 +19,7 @@ export class JoinPrivateLobbyModal extends LitElement {
close: () => void;
};
@query("#lobbyIdInput") private lobbyIdInput!: HTMLInputElement;
@state() private message: string = "";
@state() private message = "";
@state() private hasJoined = false;
@state() private players: string[] = [];
@@ -110,7 +110,7 @@ export class JoinPrivateLobbyModal extends LitElement {
return this; // light DOM
}
public open(id: string = "") {
public open(id = "") {
this.modalEl?.open();
if (id) {
this.setLobbyId(id);
+4 -4
View File
@@ -35,12 +35,12 @@ import zh_CN from "../../resources/lang/zh-CN.json";
export class LangSelector extends LitElement {
@state() public translations: Record<string, string> | undefined;
@state() public defaultTranslations: Record<string, string> | undefined;
@state() public currentLang: string = "en";
@state() public currentLang = "en";
@state() private languageList: any[] = [];
@state() private showModal: boolean = false;
@state() private debugMode: boolean = false;
@state() private showModal = false;
@state() private debugMode = false;
private debugKeyPressed: boolean = false;
private debugKeyPressed = false;
private languageMap: Record<string, any> = {
ar,
+1 -1
View File
@@ -169,7 +169,7 @@ export class LocalServer {
});
}
public endGame(saveFullGame: boolean = false) {
public endGame(saveFullGame = false) {
console.log("local server ending game");
clearInterval(this.turnCheckInterval);
if (this.isReplay) {
+10 -4
View File
@@ -43,6 +43,7 @@ import { discordLogin, getUserMe, isLoggedIn, logOut } from "./jwt";
import "./styles.css";
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface Window {
PageOS: {
session: {
@@ -62,13 +63,14 @@ declare global {
}
// Extend the global interfaces to include your custom events
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface DocumentEventMap {
"join-lobby": CustomEvent<JoinLobbyEvent>;
"kick-player": CustomEvent;
"kick-player": CustomEvent<KickPlayerEvent>;
}
}
export interface JoinLobbyEvent {
export type JoinLobbyEvent = {
clientID: string;
// Multiplayer games only have gameID, gameConfig is not known until game starts.
gameID: string;
@@ -76,7 +78,11 @@ export interface JoinLobbyEvent {
gameStartInfo?: GameStartInfo;
// GameRecord exists when replaying an archived game.
gameRecord?: GameRecord;
}
};
export type KickPlayerEvent = {
target: string;
};
class Client {
private gameStop: (() => void) | null = null;
@@ -548,7 +554,7 @@ class Client {
this.publicLobby.leaveLobby();
}
private handleKickPlayer(event: CustomEvent) {
private handleKickPlayer(event: CustomEvent<KickPlayerEvent>) {
const { target } = event.detail;
// Forward to eventBus if available
+1 -1
View File
@@ -32,7 +32,7 @@ export class NewsModal extends LitElement {
@property({ type: String }) markdown = "Loading...";
private initialized: boolean = false;
private initialized = false;
static styles = css`
:host {
+3 -3
View File
@@ -11,12 +11,12 @@ import { terrainMapFileLoader } from "./TerrainMapFileLoader";
@customElement("public-lobby")
export class PublicLobby extends LitElement {
@state() private lobbies: GameInfo[] = [];
@state() public isLobbyHighlighted: boolean = false;
@state() private isButtonDebounced: boolean = false;
@state() public isLobbyHighlighted = false;
@state() private isButtonDebounced = false;
@state() private mapImages: Map<GameID, string> = new Map();
private lobbiesInterval: number | null = null;
private currLobby: GameInfo | null = null;
private debounceDelay: number = 750;
private debounceDelay = 750;
private lobbyIDToStart = new Map<GameID, number>();
createRenderRoot() {
+8 -8
View File
@@ -34,14 +34,14 @@ export class SinglePlayerModal extends LitElement {
};
@state() private selectedMap: GameMapType = GameMapType.World;
@state() private selectedDifficulty: Difficulty = Difficulty.Medium;
@state() private disableNPCs: boolean = false;
@state() private bots: number = 400;
@state() private infiniteGold: boolean = false;
@state() private donateGold: boolean = false;
@state() private infiniteTroops: boolean = false;
@state() private donateTroops: boolean = false;
@state() private instantBuild: boolean = false;
@state() private useRandomMap: boolean = false;
@state() private disableNPCs = false;
@state() private bots = 400;
@state() private infiniteGold = false;
@state() private donateGold = false;
@state() private infiniteTroops = false;
@state() private donateTroops = false;
@state() private instantBuild = false;
@state() private useRandomMap = false;
@state() private gameMode: GameMode = GameMode.FFA;
@state() private teamCount: TeamCountConfig = 2;
+2 -2
View File
@@ -19,7 +19,7 @@ export class TerritoryPatternsModal extends LitElement {
};
public previewButton: HTMLElement | null = null;
public buttonWidth: number = 150;
public buttonWidth = 150;
@state() private selectedPattern: string | undefined;
@@ -172,7 +172,7 @@ export class TerritoryPatternsModal extends LitElement {
}}
>
${translateText("territory_patterns.purchase")}
(${pattern.product!.price})
(${pattern.product.price})
</button>
`
: null}
+1 -1
View File
@@ -383,7 +383,7 @@ export class Transport {
} satisfies ClientJoinMessage);
}
leaveGame(saveFullGame: boolean = false) {
leaveGame(saveFullGame = false) {
if (this.isLocal) {
this.localServer.endGame(saveFullGame);
return;
+4 -4
View File
@@ -8,13 +8,13 @@ import {
validateUsername,
} from "../core/validations/username";
const usernameKey: string = "username";
const usernameKey = "username";
@customElement("username-input")
export class UsernameInput extends LitElement {
@state() private username: string = "";
@property({ type: String }) validationError: string = "";
private _isValid: boolean = true;
@state() private username = "";
@property({ type: String }) validationError = "";
private _isValid = true;
private userSettings: UserSettings = new UserSettings();
// Remove static styles since we're using Tailwind
+2 -2
View File
@@ -192,7 +192,7 @@ export function getMessageTypeClasses(type: MessageType): string {
}
export function getModifierKey(): string {
const isMac = /Mac/.test(navigator.userAgent);
const isMac = navigator.userAgent.includes("Mac");
if (isMac) {
return "⌘"; // Command key
} else {
@@ -201,7 +201,7 @@ export function getModifierKey(): string {
}
export function getAltKey(): string {
const isMac = /Mac/.test(navigator.userAgent);
const isMac = navigator.userAgent.includes("Mac");
if (isMac) {
return "⌥"; // Option key
} else {
+1 -1
View File
@@ -41,7 +41,7 @@ export const MapDescription: Record<keyof typeof GameMapType, string> = {
export class MapDisplay extends LitElement {
@property({ type: String }) mapKey = "";
@property({ type: Boolean }) selected = false;
@property({ type: String }) translation: string = "";
@property({ type: String }) translation = "";
@state() private mapWebpPath: string | null = null;
@state() private mapName: string | null = null;
@state() private isLoading = true;
+1 -1
View File
@@ -3,7 +3,7 @@ import { customElement, property } from "lit/decorators";
@customElement("modal-overlay")
export class ModalOverlay extends LitElement {
@property({ reflect: true }) public visible: boolean = false;
@property({ reflect: true }) public visible = false;
static styles = css`
.overlay {
+4 -4
View File
@@ -1,15 +1,15 @@
export class AnimatedSprite {
private frameHeight: number;
private currentFrame: number = 0;
private elapsedTime: number = 0;
private active: boolean = true;
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: boolean = false,
private looping = false,
private originX: number,
private originY: number,
) {
+1 -1
View File
@@ -246,7 +246,7 @@ export function createRenderer(
eventBus,
game,
transformHandler,
emojiTable as EmojiTable,
emojiTable,
buildMenu,
uiState,
playerPanel,
+4 -4
View File
@@ -1,17 +1,17 @@
import { Cell, Game, NameViewData, Player } from "../../core/game/Game";
import { calculateBoundingBox } from "../../core/Util";
export interface Point {
export type Point = {
x: number;
y: number;
}
};
export interface Rectangle {
export type Rectangle = {
x: number;
y: number;
width: number;
height: number;
}
};
export function placeName(game: Game, player: Player): NameViewData {
const boundingBox =
+1 -1
View File
@@ -7,7 +7,7 @@ export class ProgressBar {
private y: number,
private w: number,
private h: number,
private progress: number = 0, // Progress from 0 to 1
private progress = 0, // Progress from 0 to 1
) {
this.setProgress(progress);
}
+5 -5
View File
@@ -13,10 +13,10 @@ export const CAMERA_MAX_SPEED = 15;
export const CAMERA_SMOOTHING = 0.03;
export class TransformHandler {
public scale: number = 1.8;
public scale = 1.8;
private _boundingRect: DOMRect;
private offsetX: number = -350;
private offsetY: number = -200;
private offsetX = -350;
private offsetY = -200;
private lastGoToCallTime: number | null = null;
private target: Cell | null;
@@ -267,7 +267,7 @@ export class TransformHandler {
this.target = null;
}
override(x: number = 0, y: number = 0, s: number = 1) {
override(x = 0, y = 0, s = 1) {
//hardset view position
this.clearTarget();
this.offsetX = x;
@@ -276,7 +276,7 @@ export class TransformHandler {
this.changed = true;
}
centerAll(fit: number = 1) {
centerAll(fit = 1) {
//position entire map centered on the screen
const vpWidth = this.boundingRect().width;
+2 -2
View File
@@ -1,3 +1,3 @@
export interface UIState {
export type UIState = {
attackRatio: number;
}
};
+2 -2
View File
@@ -1,6 +1,6 @@
export interface Fx {
export type Fx = {
renderTick(duration: number, ctx: CanvasRenderingContext2D): boolean;
}
};
export enum FxType {
MiniFire = "MiniFire",
+1 -1
View File
@@ -7,7 +7,7 @@ import { FadeFx, SpriteFx } from "./SpriteFx";
* Shockwave effect: draw a growing 1px white circle
*/
export class ShockwaveFx implements Fx {
private lifeTime: number = 0;
private lifeTime = 0;
constructor(
private x: number,
private y: number,
+1 -5
View File
@@ -4,11 +4,7 @@ import { AnimatedSprite } from "../AnimatedSprite";
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
import { Fx, FxType } from "./Fx";
function fadeInOut(
t: number,
fadeIn: number = 0.3,
fadeOut: number = 0.7,
): number {
function fadeInOut(t: number, fadeIn = 0.3, fadeOut = 0.7): number {
if (t < fadeIn) {
const f = t / fadeIn; // Map to [0, 1]
return f * f;
+3 -3
View File
@@ -1,15 +1,15 @@
import { Fx } from "./Fx";
export class TextFx implements Fx {
private lifeTime: number = 0;
private lifeTime = 0;
constructor(
private text: string,
private x: number,
private y: number,
private duration: number,
private riseDistance: number = 30,
private font: string = "11px sans-serif",
private riseDistance = 30,
private font = "11px sans-serif",
private color: { r: number; g: number; b: number } = {
r: 255,
g: 255,
+1 -1
View File
@@ -80,7 +80,7 @@ export class AlertFrame extends LitElement implements Layer {
this.game
.updatesSinceLastTick()
?.[GameUpdateType.BrokeAlliance]?.forEach((update) => {
this.onBrokeAllianceUpdate(update as BrokeAllianceUpdate);
this.onBrokeAllianceUpdate(update);
});
}
+2 -2
View File
@@ -35,13 +35,13 @@ import { renderNumber } from "../../Utils";
import { TransformHandler } from "../TransformHandler";
import { Layer } from "./Layer";
export interface BuildItemDisplay {
export type BuildItemDisplay = {
unitType: UnitType;
icon: string;
description?: string;
key?: string;
countable?: boolean;
}
};
export const buildTable: BuildItemDisplay[][] = [
[
+5 -5
View File
@@ -12,22 +12,22 @@ import { GameView } from "../../../core/game/GameView";
import { onlyImages } from "../../../core/Util";
import { Layer } from "./Layer";
interface ChatEvent {
type ChatEvent = {
description: string;
unsafeDescription?: boolean;
createdAt: number;
highlight?: boolean;
}
};
@customElement("chat-display")
export class ChatDisplay extends LitElement implements Layer {
public eventBus: EventBus;
public game: GameView;
private active: boolean = false;
private active = false;
@state() private _hidden: boolean = false;
@state() private newEvents: number = 0;
@state() private _hidden = false;
@state() private newEvents = 0;
@state() private chatEvents: ChatEvent[] = [];
private toggleHidden() {
+2 -2
View File
@@ -32,9 +32,9 @@ export class ChatModal extends LitElement {
private players: PlayerView[] = [];
private playerSearchQuery: string = "";
private playerSearchQuery = "";
private previewText: string | null = null;
private requiresPlayerSelection: boolean = false;
private requiresPlayerSelection = false;
private selectedCategory: string | null = null;
private selectedPhraseText: string | null = null;
private selectedPhraseTemplate: string | null = null;
+2 -2
View File
@@ -18,7 +18,7 @@ export class ControlPanel extends LitElement implements Layer {
public uiState: UIState;
@state()
private attackRatio: number = 0.2;
private attackRatio = 0.2;
@state()
private _maxTroops: number;
@@ -35,7 +35,7 @@ export class ControlPanel extends LitElement implements Layer {
@state()
private _gold: Gold;
private _troopRateIsIncreasing: boolean = true;
private _troopRateIsIncreasing = true;
private _lastTroopIncreaseRate: number;
+9 -9
View File
@@ -48,7 +48,7 @@ import {
import { getMessageTypeClasses, translateText } from "../../Utils";
interface GameEvent {
type GameEvent = {
description: string;
unsafeDescription?: boolean;
buttons?: {
@@ -66,14 +66,14 @@ interface GameEvent {
duration?: Tick;
focusID?: number;
unitView?: UnitView;
}
};
@customElement("events-display")
export class EventsDisplay extends LitElement implements Layer {
public eventBus: EventBus;
public game: GameView;
private active: boolean = false;
private active = false;
private events: GameEvent[] = [];
// allianceID -> last checked at tick
@@ -82,11 +82,11 @@ export class EventsDisplay extends LitElement implements Layer {
@state() private outgoingAttacks: AttackUpdate[] = [];
@state() private outgoingLandAttacks: AttackUpdate[] = [];
@state() private outgoingBoats: UnitView[] = [];
@state() private _hidden: boolean = false;
@state() private _isVisible: boolean = false;
@state() private newEvents: number = 0;
@state() private _hidden = false;
@state() private _isVisible = false;
@state() private newEvents = 0;
@state() private latestGoldAmount: bigint | null = null;
@state() private goldAmountAnimating: boolean = false;
@state() private goldAmountAnimating = false;
private goldAmountTimeoutId: ReturnType<typeof setTimeout> | null = null;
@state() private eventsFilters: Map<MessageCategory, boolean> = new Map([
[MessageCategory.ATTACK, false],
@@ -261,7 +261,7 @@ export class EventsDisplay extends LitElement implements Layer {
this.alliancesCheckedAt.set(alliance.id, this.game.ticks());
const other = this.game.player(alliance.other) as PlayerView;
const other = this.game.player(alliance.other);
if (!other.isAlive()) continue;
this.addEvent({
@@ -393,7 +393,7 @@ export class EventsDisplay extends LitElement implements Layer {
}
}
let otherPlayerDiplayName: string = "";
let otherPlayerDiplayName = "";
if (event.recipient !== null) {
//'recipient' parameter contains sender ID or recipient ID
const player = this.game.player(event.recipient);
+9 -9
View File
@@ -14,29 +14,29 @@ export class FPSDisplay extends LitElement implements Layer {
public userSettings!: UserSettings;
@state()
private currentFPS: number = 0;
private currentFPS = 0;
@state()
private averageFPS: number = 0;
private averageFPS = 0;
@state()
private frameTime: number = 0;
private frameTime = 0;
@state()
private isVisible: boolean = false;
private isVisible = false;
@state()
private isDragging: boolean = false;
private isDragging = false;
@state()
private position: { x: number; y: number } = { x: 50, y: 20 }; // Percentage values
private frameCount: number = 0;
private lastTime: number = 0;
private frameCount = 0;
private lastTime = 0;
private frameTimes: number[] = [];
private fpsHistory: number[] = [];
private lastSecondTime: number = 0;
private framesThisSecond: number = 0;
private lastSecondTime = 0;
private framesThisSecond = 0;
private dragStart: { x: number; y: number } = { x: 0, y: 0 };
static styles = css`
+2 -2
View File
@@ -20,8 +20,8 @@ export class FxLayer implements Layer {
private canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D;
private lastRefresh: number = 0;
private refreshRate: number = 10;
private lastRefresh = 0;
private refreshRate = 10;
private theme: Theme;
private animatedSpriteLoader: AnimatedSpriteLoader =
new AnimatedSpriteLoader();
@@ -22,19 +22,19 @@ export class GameRightSidebar extends LitElement implements Layer {
public eventBus: EventBus;
@state()
private _isSinglePlayer: boolean = false;
private _isSinglePlayer = false;
@state()
private _isReplayVisible: boolean = false;
private _isReplayVisible = false;
@state()
private _isVisible: boolean = true;
private _isVisible = true;
@state()
private isPaused: boolean = false;
private isPaused = false;
@state()
private timer: number = 0;
private timer = 0;
private hasWinner = false;
+7 -7
View File
@@ -13,16 +13,16 @@ export class GutterAdModal extends LitElement implements Layer {
public eventBus: EventBus;
@state()
private isVisible: boolean = false;
private isVisible = false;
@state()
private adLoaded: boolean = false;
private adLoaded = false;
private leftAdType: string = "left_rail";
private rightAdType: string = "right_rail";
private leftContainerId: string = "gutter-ad-container-left";
private rightContainerId: string = "gutter-ad-container-right";
private margin: string = "10px";
private leftAdType = "left_rail";
private rightAdType = "right_rail";
private leftContainerId = "gutter-ad-container-left";
private rightContainerId = "gutter-ad-container-right";
private margin = "10px";
// Override createRenderRoot to disable shadow DOM
createRenderRoot() {
+2 -2
View File
@@ -1,7 +1,7 @@
export interface Layer {
export type Layer = {
init?: () => void;
tick?: () => void;
renderLayer?: (context: CanvasRenderingContext2D) => void;
shouldTransform?: () => boolean;
redraw?: () => void;
}
};
+2 -2
View File
@@ -7,7 +7,7 @@ import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
import { renderNumber } from "../../Utils";
import { Layer } from "./Layer";
interface Entry {
type Entry = {
name: string;
position: number;
score: string;
@@ -15,7 +15,7 @@ interface Entry {
troops: string;
isMyPlayer: boolean;
player: PlayerView;
}
};
export class GoToPlayerEvent implements GameEvent {
constructor(public player: PlayerView) {}
+1 -1
View File
@@ -110,7 +110,7 @@ export class MainRadialMenu extends LitElement implements Layer {
this.buildMenu.playerActions = actions;
const tileOwner = this.game.owner(tile);
const recipient = tileOwner.isPlayer() ? (tileOwner as PlayerView) : null;
const recipient = tileOwner.isPlayer() ? tileOwner : null;
if (myPlayer && recipient) {
this.chatIntegration.setupChatModal(myPlayer, recipient);
+6 -6
View File
@@ -13,12 +13,12 @@ export class MultiTabModal extends LitElement implements Layer {
private detector: MultiTabDetector;
@property({ type: Number }) duration: number = 5000;
@state() private countdown: number = 5;
@state() private isVisible: boolean = false;
@state() private fakeIp: string = "";
@state() private deviceFingerprint: string = "";
@state() private reported: boolean = true;
@property({ type: Number }) duration = 5000;
@state() private countdown = 5;
@state() private isVisible = false;
@state() private fakeIp = "";
@state() private deviceFingerprint = "";
@state() private reported = true;
private intervalId?: number;
+2 -2
View File
@@ -59,7 +59,7 @@ export class NameLayer implements Layer {
private firstPlace: PlayerView | null = null;
private theme: Theme = this.game.config().theme();
private userSettings: UserSettings = new UserSettings();
private isVisible: boolean = true;
private isVisible = true;
constructor(
private game: GameView,
@@ -617,7 +617,7 @@ export class NameLayer implements Layer {
src: string,
size: number,
id: string,
center: boolean = false,
center = false,
): HTMLImageElement {
const icon = document.createElement("img");
icon.src = src;
+5 -5
View File
@@ -48,23 +48,23 @@ export class OptionsMenu extends LitElement implements Layer {
private userSettings: UserSettings = new UserSettings();
@state()
private showPauseButton: boolean = true;
private showPauseButton = true;
@state()
private isPaused: boolean = false;
private isPaused = false;
@state()
private timer: number = 0;
private timer = 0;
@state()
private showSettings: boolean = false;
private showSettings = false;
private isVisible = false;
private hasWinner = false;
@state()
private alternateView: boolean = false;
private alternateView = false;
private onTerrainButtonClick() {
this.alternateView = !this.alternateView;
@@ -60,7 +60,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
private unit: UnitView | null = null;
@state()
private _isInfoVisible: boolean = false;
private _isInfoVisible = false;
private _isActive = false;
@@ -107,7 +107,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
const owner = this.game.owner(tile);
if (owner && owner.isPlayer()) {
this.player = owner as PlayerView;
this.player = owner;
this.player.profile().then((p) => {
this.playerProfile = p;
});
@@ -231,7 +231,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
}}
>
${player.cosmetics.flag
? player.cosmetics.flag!.startsWith("!")
? player.cosmetics.flag.startsWith("!")
? html`<div
class="h-8 mr-1 aspect-[3/4] player-flag"
${ref((el) => {
@@ -244,7 +244,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
></div>`
: html`<img
class="h-8 mr-1 aspect-[3/4]"
src=${"/flags/" + player.cosmetics.flag! + ".svg"}
src=${"/flags/" + player.cosmetics.flag + ".svg"}
/>`
: html``}
${player.name()}
+2 -3
View File
@@ -40,7 +40,7 @@ export class PlayerPanel extends LitElement implements Layer {
private tile: TileRef | null = null;
@state()
public isVisible: boolean = false;
public isVisible = false;
@state()
private allianceExpiryText: string | null = null;
@@ -224,13 +224,12 @@ export class PlayerPanel extends LitElement implements Layer {
const myPlayer = this.g.myPlayer();
if (myPlayer === null) return;
if (this.tile === null) return;
let other = this.g.owner(this.tile);
const other = this.g.owner(this.tile);
if (!other.isPlayer()) {
this.hide();
console.warn("Tile is not owned by a player");
return;
}
other = other as PlayerView;
const canDonateGold = this.actions?.interaction?.canDonateGold;
const canDonateTroops = this.actions?.interaction?.canDonateTroops;
+11 -11
View File
@@ -15,12 +15,12 @@ export class CloseRadialMenuEvent implements GameEvent {
constructor() {}
}
export interface TooltipItem {
export type TooltipItem = {
text: string;
className: string;
}
};
export interface RadialMenuConfig {
export type RadialMenuConfig = {
menuSize?: number;
submenuScale?: number;
centerButtonSize?: number;
@@ -33,7 +33,7 @@ export interface RadialMenuConfig {
maxNestedLevels?: number;
innerRadiusIncrement?: number;
tooltipStyle?: string;
}
};
type CenterButtonState = "default" | "back";
@@ -42,9 +42,9 @@ type RequiredRadialMenuConfig = Required<RadialMenuConfig>;
export class RadialMenu implements Layer {
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
private tooltipElement: HTMLDivElement | null = null;
private isVisible: boolean = false;
private isVisible = false;
private currentLevel: number = 0; // Current menu level (0 = main menu, 1 = submenu, etc.)
private currentLevel = 0; // Current menu level (0 = main menu, 1 = submenu, etc.)
private menuStack: MenuElement[][] = []; // Stack to track menu navigation history
private currentMenuItems: MenuElement[] = []; // Current active menu items (changes based on level)
@@ -53,9 +53,9 @@ export class RadialMenu implements Layer {
private centerButtonState: CenterButtonState = "default";
private isTransitioning: boolean = false;
private lastHideTime: number = 0;
private reopenCooldownMs: number = 300;
private isTransitioning = false;
private lastHideTime = 0;
private reopenCooldownMs = 300;
private menuGroups: Map<
number,
@@ -73,8 +73,8 @@ export class RadialMenu implements Layer {
private selectedItemId: string | null = null;
private submenuHoverTimeout: number | null = null;
private backButtonHoverTimeout: number | null = null;
private navigationInProgress: boolean = false;
private originalCenterButtonIcon: string = "";
private navigationInProgress = false;
private originalCenterButtonIcon = "";
private params: MenuElementParams | null = null;
@@ -25,7 +25,7 @@ import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
import xIcon from "../../../../resources/images/XIcon.svg";
import { EventBus } from "../../../core/EventBus";
export interface MenuElementParams {
export type MenuElementParams = {
myPlayer: PlayerView;
selected: PlayerView | null;
tile: TileRef;
@@ -38,9 +38,9 @@ export interface MenuElementParams {
chatIntegration: ChatIntegration;
eventBus: EventBus;
closeMenu: () => void;
}
};
export interface MenuElement {
export type MenuElement = {
id: string;
name: string;
displayed?: boolean | ((params: MenuElementParams) => boolean);
@@ -54,18 +54,18 @@ export interface MenuElement {
disabled: (params: MenuElementParams) => boolean;
action?: (params: MenuElementParams) => void; // For leaf items that perform actions
subMenu?: (params: MenuElementParams) => MenuElement[]; // For non-leaf items that open submenus
}
};
export interface TooltipKey {
export type TooltipKey = {
key: string;
className: string;
params?: Record<string, string | number>;
}
};
export interface CenterButtonElement {
export type CenterButtonElement = {
disabled: (params: MenuElementParams) => boolean;
action: (params: MenuElementParams) => void;
}
};
export const COLORS = {
build: "#ebe250",
@@ -566,8 +566,7 @@ export const rootMenuElement: MenuElement = {
const tileOwner = params.game.owner(params.tile);
const isOwnTerritory =
tileOwner.isPlayer() &&
(tileOwner as PlayerView).id() === params.myPlayer.id();
tileOwner.isPlayer() && tileOwner.id() === params.myPlayer.id();
const menuItems: (MenuElement | null)[] = [
infoMenuElement,
+2 -2
View File
@@ -8,7 +8,7 @@ import {
RailTile,
RailType,
} from "../../../core/game/GameUpdates";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { GameView } from "../../../core/game/GameView";
import { Layer } from "./Layer";
import { getRailroadRects } from "./RailroadSprites";
@@ -151,7 +151,7 @@ export class RailroadLayer implements Layer {
const x = this.game.x(railRoad.tile);
const y = this.game.y(railRoad.tile);
const owner = this.game.owner(railRoad.tile);
const recipient = owner.isPlayer() ? (owner as PlayerView) : null;
const recipient = owner.isPlayer() ? owner : null;
const color = recipient
? this.theme.railroadColor(recipient)
: new Colord({ r: 255, g: 255, b: 255, a: 1 });
+3 -3
View File
@@ -12,8 +12,8 @@ import { Layer } from "./Layer";
export class ShowReplayPanelEvent {
constructor(
public visible: boolean = true,
public isSingleplayer: boolean = false,
public visible = true,
public isSingleplayer = false,
) {}
}
@@ -23,7 +23,7 @@ export class ReplayPanel extends LitElement implements Layer {
public eventBus: EventBus | undefined;
@property({ type: Boolean })
visible: boolean = false;
visible = false;
@state()
private _replaySpeedMultiplier: number = defaultReplaySpeedMultiplier;
+5 -5
View File
@@ -18,9 +18,9 @@ import { Layer } from "./Layer";
export class ShowSettingsModalEvent {
constructor(
public readonly isVisible: boolean = true,
public readonly shouldPause: boolean = false,
public readonly isPaused: boolean = false,
public readonly isVisible = true,
public readonly shouldPause = false,
public readonly isPaused = false,
) {}
}
@@ -30,10 +30,10 @@ export class SettingsModal extends LitElement implements Layer {
public userSettings: UserSettings;
@state()
private isVisible: boolean = false;
private isVisible = false;
@state()
private alternateView: boolean = false;
private alternateView = false;
@query(".modal-overlay")
private modalOverlay!: HTMLElement;
+3 -3
View File
@@ -13,12 +13,12 @@ export class SpawnAd extends LitElement implements Layer {
public g: GameView;
@state()
private isVisible: boolean = false;
private isVisible = false;
@state()
private adLoaded: boolean = false;
private adLoaded = false;
private gamesPlayed: number = 0;
private gamesPlayed = 0;
// Override createRenderRoot to disable shadow DOM
createRenderRoot() {
@@ -19,15 +19,15 @@ import { Layer } from "./Layer";
type ShapeType = "triangle" | "square" | "pentagon" | "octagon" | "circle";
class StructureRenderInfo {
public isOnScreen: boolean = false;
public isOnScreen = false;
constructor(
public unit: UnitView,
public owner: PlayerID,
public iconContainer: PIXI.Container,
public levelContainer: PIXI.Container,
public dotContainer: PIXI.Container,
public level: number = 0,
public underConstruction: boolean = true,
public level = 0,
public underConstruction = true,
) {}
}
@@ -58,7 +58,7 @@ export class StructureIconsLayer implements Layer {
private iconsStage: PIXI.Container;
private levelsStage: PIXI.Container;
private dotsStage: PIXI.Container;
private shouldRedraw: boolean = true;
private shouldRedraw = true;
private textureCache: Map<string, PIXI.Texture> = new Map();
private theme: Theme;
private renderer: PIXI.Renderer;
+2 -2
View File
@@ -23,11 +23,11 @@ const BASE_TERRITORY_RADIUS = 13.5;
const RADIUS_SCALE_FACTOR = 0.5;
const ZOOM_THRESHOLD = 4.3; // below this zoom level, structures are not rendered
interface UnitRenderConfig {
type UnitRenderConfig = {
icon: string;
borderRadius: number;
territoryRadius: number;
}
};
export class StructureLayer implements Layer {
private canvas: HTMLCanvasElement;
+2 -2
View File
@@ -6,14 +6,14 @@ import { GameView, PlayerView } from "../../../core/game/GameView";
import { renderNumber, translateText } from "../../Utils";
import { Layer } from "./Layer";
interface TeamEntry {
type TeamEntry = {
teamName: string;
totalScoreStr: string;
totalGold: string;
totalTroops: string;
totalScoreSort: number;
players: PlayerView[];
}
};
@customElement("team-stats")
export class TeamStats extends LitElement implements Layer {
+1 -1
View File
@@ -391,7 +391,7 @@ export class TerritoryLayer implements Layer {
}
}
paintTerritory(tile: TileRef, isBorder: boolean = false) {
paintTerritory(tile: TileRef, isBorder = false) {
if (isBorder && !this.game.hasOwner(tile)) {
return;
}
+1 -1
View File
@@ -77,7 +77,7 @@ export function getAuthHeader(): string {
return `Bearer ${token}`;
}
export async function logOut(allSessions: boolean = false) {
export async function logOut(allSessions = false) {
const token = getToken();
if (token === null) return;
clearToken();
@@ -3,10 +3,10 @@ import { html, TemplateResult } from "lit";
import { UnitType } from "../../core/game/Game";
import { translateText } from "../Utils";
export interface UnitTypeRenderContext {
export type UnitTypeRenderContext = {
disabledUnits: UnitType[];
toggleUnit: (unit: UnitType, checked: boolean) => void;
}
};
const unitOptions: { type: UnitType; translationKey: string }[] = [
{ type: UnitType.City, translationKey: "unit_type.city" },
+3 -3
View File
@@ -1,8 +1,8 @@
export type GameEvent = object;
export interface EventConstructor<T extends GameEvent = GameEvent> {
new (...args: any[]): T;
}
export type EventConstructor<T extends GameEvent = GameEvent> = new (
...args: any[]
) => T;
export class EventBus {
private listeners: Map<EventConstructor, Array<(event: GameEvent) => void>> =
+1 -1
View File
@@ -198,7 +198,7 @@ export class GameRunner {
canTarget: player.canTarget(other),
sharedBorder: player.sharesBorderWith(other),
};
const alliance = player.allianceWith(other as Player);
const alliance = player.allianceWith(other);
if (alliance) {
actions.interaction.allianceExpiresAt = alliance.expiresAt();
}
+3 -3
View File
@@ -4,9 +4,9 @@ export class PseudoRandom {
private state1: number;
// Keep these variables to maintain the exact same interface
private m: number = 0x80000000; // 2**31
private a: number = 1103515245;
private c: number = 12345;
private m = 0x80000000; // 2**31
private a = 1103515245;
private c = 12345;
private state: number;
private static readonly POW36_8 = Math.pow(36, 8); // Pre-compute 36^8
+8 -8
View File
@@ -26,7 +26,7 @@ export enum GameEnv {
Prod,
}
export interface ServerConfig {
export type ServerConfig = {
turnIntervalMs(): number;
gameCreationRate(): number;
lobbyMaxPlayers(
@@ -62,14 +62,14 @@ export interface ServerConfig {
cloudflareCredsPath(): string;
stripePublishableKey(): string;
allowedFlares(): string[] | undefined;
}
};
export interface NukeMagnitude {
export type NukeMagnitude = {
inner: number;
outer: number;
}
};
export interface Config {
export type Config = {
samHittingChance(): number;
samWarheadHittingChance(): number;
spawnImmunityDuration(): Tick;
@@ -170,9 +170,9 @@ export interface Config {
structureMinDist(): number;
isReplay(): boolean;
allianceExtensionPromptOffset(): number;
}
};
export interface Theme {
export type Theme = {
teamColor(team: Team): Colord;
territoryColor(playerInfo: PlayerView): Colord;
specialBuildingColor(playerInfo: PlayerView): Colord;
@@ -190,4 +190,4 @@ export interface Theme {
allyColor(): Colord;
enemyColor(): Colord;
spawnHighlightColor(): Colord;
}
};
+1 -1
View File
@@ -12,7 +12,7 @@ export let cachedSC: ServerConfig | null = null;
export async function getConfig(
gameConfig: GameConfig,
userSettings: UserSettings | null,
isReplay: boolean = false,
isReplay = false,
): Promise<Config> {
const sc = await getServerConfigFromClient();
switch (sc.env()) {
+3 -3
View File
@@ -18,7 +18,7 @@ const malusForRetreat = 25;
export class AttackExecution implements Execution {
private breakAlliance = false;
private wasAlliedAtInit = false; // Store alliance state at initialization
private active: boolean = true;
private active = true;
private toConquer = new FlatBinaryHeap();
private random = new PseudoRandom(123);
@@ -34,7 +34,7 @@ export class AttackExecution implements Execution {
private _owner: Player,
private _targetID: PlayerID | null,
private sourceTile: TileRef | null = null,
private removeTroops: boolean = true,
private removeTroops = true,
) {}
public targetID(): PlayerID | null {
@@ -69,7 +69,7 @@ export class AttackExecution implements Execution {
}
if (this.target && this.target.isPlayer()) {
const targetPlayer = this.target as Player;
const targetPlayer = this.target;
if (
targetPlayer.type() !== PlayerType.Bot &&
this._owner.type() !== PlayerType.Bot
+2 -2
View File
@@ -5,7 +5,7 @@ import { TrainStationExecution } from "./TrainStationExecution";
export class CityExecution implements Execution {
private mg: Game;
private city: Unit | null = null;
private active: boolean = true;
private active = true;
constructor(
private player: Player,
@@ -48,7 +48,7 @@ export class CityExecution implements Execution {
createStation(): void {
if (this.city !== null) {
const nearbyFactory = this.mg.hasUnitNearby(
this.city.tile()!,
this.city.tile(),
this.mg.config().trainStationMaxRange(),
UnitType.Factory,
this.player.id(),
+1 -1
View File
@@ -20,7 +20,7 @@ import { WarshipExecution } from "./WarshipExecution";
export class ConstructionExecution implements Execution {
private construction: Unit | null = null;
private active: boolean = true;
private active = true;
private mg: Game;
private ticksUntilComplete: Tick;
+1 -1
View File
@@ -5,7 +5,7 @@ import { ShellExecution } from "./ShellExecution";
export class DefensePostExecution implements Execution {
private mg: Game;
private post: Unit | null = null;
private active: boolean = true;
private active = true;
private target: Unit | null = null;
private lastShellAttack = 0;
+1 -1
View File
@@ -1,7 +1,7 @@
import { Execution, Game, MessageType, Player } from "../game/Game";
export class DeleteUnitExecution implements Execution {
private active: boolean = true;
private active = true;
private mg: Game;
constructor(
+2 -2
View File
@@ -4,7 +4,7 @@ import { TrainStationExecution } from "./TrainStationExecution";
export class FactoryExecution implements Execution {
private factory: Unit | null = null;
private active: boolean = true;
private active = true;
private game: Game;
constructor(
private player: Player,
@@ -47,7 +47,7 @@ export class FactoryExecution implements Execution {
createStation(): void {
if (this.factory !== null) {
const structures = this.game.nearbyUnits(
this.factory.tile()!,
this.factory.tile(),
this.game.config().trainStationMaxRange(),
[UnitType.City, UnitType.Port, UnitType.Factory],
);
+1 -1
View File
@@ -31,7 +31,7 @@ export class MirvExecution implements Execution {
private separateDst: TileRef;
private speed: number = -1;
private speed = -1;
constructor(
private player: Player,
+1 -1
View File
@@ -28,7 +28,7 @@ export class NukeExecution implements Execution {
private player: Player,
private dst: TileRef,
private src?: TileRef | null,
private speed: number = -1,
private speed = -1,
private waitTicks = 0,
) {}
+3 -3
View File
@@ -28,11 +28,11 @@ export class PlayerExecution implements Execution {
tick(ticks: number) {
this.player.decayRelations();
this.player.units().forEach((u) => {
const tileOwner = this.mg!.owner(u.tile());
const tileOwner = this.mg.owner(u.tile());
if (u.info().territoryBound) {
if (tileOwner.isPlayer()) {
if (tileOwner !== this.player) {
this.mg!.player(tileOwner.id()).captureUnit(u);
this.mg.player(tileOwner.id()).captureUnit(u);
}
} else {
u.delete();
@@ -218,7 +218,7 @@ export class PlayerExecution implements Execution {
}
let largestNeighborAttack: Player | null = null;
let largestTroopCount: number = 0;
let largestTroopCount = 0;
for (const id of neighborsIDs) {
const neighbor = this.mg.playerBySmallID(id);
if (!neighbor.isPlayer() || this.player.isFriendly(neighbor)) {
+1 -1
View File
@@ -90,7 +90,7 @@ export class PortExecution implements Execution {
createStation(): void {
if (this.port !== null) {
const nearbyFactory = this.mg.hasUnitNearby(
this.port.tile()!,
this.port.tile(),
this.mg.config().trainStationMaxRange(),
UnitType.Factory,
this.player.id(),
+4 -4
View File
@@ -5,10 +5,10 @@ import { Railroad } from "../game/Railroad";
export class RailroadExecution implements Execution {
private mg: Game;
private active: boolean = true;
private headIndex: number = 0;
private tailIndex: number = 0;
private increment: number = 3;
private active = true;
private headIndex = 0;
private tailIndex = 0;
private increment = 3;
private railTiles: RailTile[] = [];
constructor(private railRoad: Railroad) {
this.tailIndex = railRoad.tiles.length;
+1 -1
View File
@@ -128,7 +128,7 @@ class SAMTargetingSystem {
export class SAMLauncherExecution implements Execution {
private mg: Game;
private active: boolean = true;
private active = true;
// As MIRV go very fast we have to detect them very early but we only
// shoot the one targeting very close (MIRVWarheadProtectionRadius)
+1 -1
View File
@@ -16,7 +16,7 @@ export class SAMMissileExecution implements Execution {
private pathFinder: AirPathFinder;
private SAMMissile: Unit | undefined;
private mg: Game;
private speed: number = 0;
private speed = 0;
constructor(
private spawn: TileRef,
+1 -1
View File
@@ -8,7 +8,7 @@ export class ShellExecution implements Execution {
private pathFinder: AirPathFinder;
private shell: Unit | undefined;
private mg: Game;
private destroyAtTick: number = -1;
private destroyAtTick = -1;
private random: PseudoRandom;
constructor(
+1 -1
View File
@@ -5,7 +5,7 @@ import { PlayerExecution } from "./PlayerExecution";
import { getSpawnTiles } from "./Util";
export class SpawnExecution implements Execution {
active: boolean = true;
active = true;
private mg: Game;
constructor(
+3 -3
View File
@@ -16,13 +16,13 @@ export class TrainExecution implements Execution {
private mg: Game | null = null;
private train: Unit | null = null;
private cars: Unit[] = [];
private hasCargo: boolean = false;
private currentTile: number = 0;
private hasCargo = false;
private currentTile = 0;
private spacing = 2;
private usedTiles: TileRef[] = []; // used for cars behind
private stations: TrainStation[] = [];
private currentRailroad: OrientedRailroad | null = null;
private speed: number = 2;
private speed = 2;
constructor(
private railNetwork: RailNetwork,
+5 -5
View File
@@ -5,12 +5,12 @@ import { TrainExecution } from "./TrainExecution";
export class TrainStationExecution implements Execution {
private mg: Game;
private active: boolean = true;
private active = true;
private random: PseudoRandom;
private station: TrainStation | null = null;
private numCars: number = 5;
private lastSpawnTick: number = 0;
private ticksCooldown: number = 10; // Minimum cooldown between two trains
private numCars = 5;
private lastSpawnTick = 0;
private ticksCooldown = 10; // Minimum cooldown between two trains
constructor(
private unit: Unit,
private spawnTrains?: boolean, // If set, the station will spawn trains
@@ -50,7 +50,7 @@ export class TrainStationExecution implements Execution {
private shouldSpawnTrain(clusterSize: number): boolean {
const spawnRate = this.mg.config().trainSpawnRate(clusterSize);
for (let i = 0; i < this.unit!.level(); i++) {
for (let i = 0; i < this.unit.level(); i++) {
if (this.random.chance(spawnRate)) {
return true;
}
+5 -5
View File
@@ -79,7 +79,7 @@ export class WarshipExecution implements Execution {
const patrolRangeSquared = this.mg.config().warshipPatrolRange() ** 2;
const ships = this.mg.nearbyUnits(
this.warship.tile()!,
this.warship.tile(),
this.mg.config().warshipTargettingRange(),
[UnitType.TransportShip, UnitType.Warship, UnitType.TradeShip],
);
@@ -238,11 +238,11 @@ export class WarshipExecution implements Execution {
return false;
}
randomTile(allowShoreline: boolean = false): TileRef | undefined {
randomTile(allowShoreline = false): TileRef | undefined {
let warshipPatrolRange = this.mg.config().warshipPatrolRange();
const maxAttemptBeforeExpand: number = 500;
let attempts: number = 0;
let expandCount: number = 0;
const maxAttemptBeforeExpand = 500;
let attempts = 0;
let expandCount = 0;
while (expandCount < 3) {
const x =
this.mg.x(this.warship.patrolTile()!) +
+2 -2
View File
@@ -1,8 +1,8 @@
import { Game, MutableAlliance, Player, Tick } from "./Game";
export class AllianceImpl implements MutableAlliance {
private extensionRequestedRequestor_: boolean = false;
private extensionRequestedRecipient_: boolean = false;
private extensionRequestedRequestor_ = false;
private extensionRequestedRecipient_ = false;
private expiresAt_: Tick;
+4 -4
View File
@@ -2,13 +2,13 @@ import { GameMapType } from "./Game";
import { GameMapLoader, MapData } from "./GameMapLoader";
import { MapManifest } from "./TerrainMapLoader";
export interface BinModule {
export type BinModule = {
default: string;
}
};
interface NationMapModule {
type NationMapModule = {
default: MapManifest;
}
};
export class BinaryLoaderGameMapLoader implements GameMapLoader {
private maps: Map<GameMapType, MapData>;
+42 -42
View File
@@ -26,10 +26,10 @@ export type GameUpdates = {
[K in GameUpdateType]: UpdateTypeMap<K>[];
};
export interface MapPos {
export type MapPos = {
x: number;
y: number;
}
};
export enum Difficulty {
Easy = "Easy",
@@ -141,7 +141,7 @@ export enum GameMode {
Team = "Team",
}
export interface UnitInfo {
export type UnitInfo = {
cost: (player: Player) => Gold;
// Determines if its owner changes when its tile is conquered.
territoryBound: boolean;
@@ -151,7 +151,7 @@ export interface UnitInfo {
upgradable?: boolean;
canBuildTrainStation?: boolean;
experimental?: boolean;
}
};
export enum UnitType {
TransportShip = "Transport",
@@ -191,15 +191,15 @@ export function isStructureType(type: UnitType): boolean {
return _structureTypes.has(type);
}
export interface OwnerComp {
export type OwnerComp = {
owner: Player;
}
};
export type TrajectoryTile = {
tile: TileRef;
targetable: boolean;
};
export interface UnitParamsMap {
export type UnitParamsMap = {
[UnitType.TransportShip]: {
troops?: number;
destination?: TileRef;
@@ -253,7 +253,7 @@ export interface UnitParamsMap {
};
[UnitType.Construction]: Record<string, never>;
}
};
// Type helper to get params type for a specific unit type
export type UnitParams<T extends UnitType> = UnitParamsMap[T];
@@ -320,14 +320,14 @@ export enum PlayerType {
FakeHuman = "FAKEHUMAN",
}
export interface Execution {
export type Execution = {
isActive(): boolean;
activeDuringSpawnPhase(): boolean;
init(mg: Game, ticks: number): void;
tick(ticks: number): void;
}
};
export interface Attack {
export type Attack = {
id(): string;
retreating(): boolean;
retreated(): boolean;
@@ -346,25 +346,25 @@ export interface Attack {
clearBorder(): void;
borderSize(): number;
averagePosition(): Cell | null;
}
};
export interface AllianceRequest {
export type AllianceRequest = {
accept(): void;
reject(): void;
requestor(): Player;
recipient(): Player;
createdAt(): Tick;
}
};
export interface Alliance {
export type Alliance = {
requestor(): Player;
recipient(): Player;
createdAt(): Tick;
expiresAt(): Tick;
other(player: Player): Player;
}
};
export interface MutableAlliance extends Alliance {
export type MutableAlliance = {
expire(): void;
other(player: Player): Player;
bothAgreedToExtend(): boolean;
@@ -372,7 +372,7 @@ export interface MutableAlliance extends Alliance {
id(): number;
extend(): void;
onlyOneAgreedToExtend(): boolean;
}
} & Alliance;
export class PlayerInfo {
public readonly clan: string | null;
@@ -406,7 +406,7 @@ export function isUnit(unit: unknown): unit is Unit {
);
}
export interface Unit {
export type Unit = {
isUnit(): this is Unit;
// Common properties.
@@ -480,22 +480,22 @@ export interface Unit {
// Warships
setPatrolTile(tile: TileRef): void;
patrolTile(): TileRef | undefined;
}
};
export interface TerraNullius {
export type TerraNullius = {
isPlayer(): false;
id(): null;
clientID(): ClientID;
smallID(): number;
}
};
export interface Embargo {
export type Embargo = {
createdAt: Tick;
isTemporary: boolean;
target: PlayerID;
}
};
export interface Player {
export type Player = {
// Basic Info
smallID(): number;
info(): PlayerInfo;
@@ -629,9 +629,9 @@ export interface Player {
tradingPorts(port: Unit): Unit[];
// WARNING: this operation is expensive.
bestTransportShipSpawn(tile: TileRef): TileRef | false;
}
};
export interface Game extends GameMap {
export type Game = {
// Map & Dimensions
isOnMap(cell: Cell): boolean;
width(): number;
@@ -715,33 +715,33 @@ export interface Game extends GameMap {
addUpdate(update: GameUpdate): void;
railNetwork(): RailNetwork;
conquerPlayer(conqueror: Player, conquered: Player): void;
}
} & GameMap;
export interface PlayerActions {
export type PlayerActions = {
canAttack: boolean;
buildableUnits: BuildableUnit[];
canSendEmojiAllPlayers: boolean;
interaction?: PlayerInteraction;
}
};
export interface BuildableUnit {
export type BuildableUnit = {
canBuild: TileRef | false;
// unit id of the existing unit that can be upgraded, or false if it cannot be upgraded.
canUpgrade: number | false;
type: UnitType;
cost: Gold;
}
};
export interface PlayerProfile {
export type PlayerProfile = {
relations: Record<number, Relation>;
alliances: number[];
}
};
export interface PlayerBorderTiles {
export type PlayerBorderTiles = {
borderTiles: ReadonlySet<TileRef>;
}
};
export interface PlayerInteraction {
export type PlayerInteraction = {
sharedBorder: boolean;
canSendEmoji: boolean;
canSendAllianceRequest: boolean;
@@ -751,14 +751,14 @@ export interface PlayerInteraction {
canDonateTroops: boolean;
canEmbargo: boolean;
allianceExpiresAt?: Tick;
}
};
export interface EmojiMessage {
export type EmojiMessage = {
message: string;
senderID: number;
recipientID: number | typeof AllPlayers;
createdAt: Tick;
}
};
export enum MessageType {
ATTACK_FAILED,
@@ -832,8 +832,8 @@ export function getMessageCategory(messageType: MessageType): MessageCategory {
return MESSAGE_TYPE_CATEGORIES[messageType];
}
export interface NameViewData {
export type NameViewData = {
x: number;
y: number;
size: number;
}
};
+1 -1
View File
@@ -82,7 +82,7 @@ export class GameImpl implements Game {
private _railNetwork: RailNetwork = createRailNetwork(this);
// Used to assign unique IDs to each new alliance
private nextAllianceID: number = 0;
private nextAllianceID = 0;
constructor(
private _humans: PlayerInfo[],
+7 -7
View File
@@ -3,7 +3,7 @@ import { Cell, TerrainType } from "./Game";
export type TileRef = number;
export type TileUpdate = bigint;
export interface GameMap {
export type GameMap = {
ref(x: number, y: number): TileRef;
isValidRef(ref: TileRef): boolean;
x(ref: TileRef): number;
@@ -48,7 +48,7 @@ export interface GameMap {
updateTile(tu: TileUpdate): TileRef;
numTilesWithFallout(): number;
}
};
export class GameMapImpl implements GameMap {
private _numTilesWithFallout = 0;
@@ -341,7 +341,7 @@ export class GameMapImpl implements GameMap {
export function euclDistFN(
root: TileRef,
dist: number,
center: boolean = false,
center = false,
): (gm: GameMap, tile: TileRef) => boolean {
const dist2 = dist * dist;
if (!center) {
@@ -364,7 +364,7 @@ export function euclDistFN(
export function manhattanDistFN(
root: TileRef,
dist: number,
center: boolean = false,
center = false,
): (gm: GameMap, tile: TileRef) => boolean {
if (!center) {
return (gm: GameMap, n: TileRef) => gm.manhattanDist(root, n) <= dist;
@@ -382,7 +382,7 @@ export function manhattanDistFN(
export function rectDistFN(
root: TileRef,
dist: number,
center: boolean = false,
center = false,
): (gm: GameMap, tile: TileRef) => boolean {
if (!center) {
return (gm: GameMap, n: TileRef) => {
@@ -415,7 +415,7 @@ function isInIsometricTile(
export function isometricDistFN(
root: TileRef,
dist: number,
center: boolean = false,
center = false,
): (gm: GameMap, tile: TileRef) => boolean {
if (!center) {
return (gm: GameMap, n: TileRef) => gm.manhattanDist(root, n) <= dist;
@@ -437,7 +437,7 @@ export function isometricDistFN(
export function hexDistFN(
root: TileRef,
dist: number,
center: boolean = false,
center = false,
): (gm: GameMap, tile: TileRef) => boolean {
if (!center) {
return (gm: GameMap, n: TileRef) => {
+4 -4
View File
@@ -1,13 +1,13 @@
import { GameMapType } from "./Game";
import { MapManifest } from "./TerrainMapLoader";
export interface GameMapLoader {
export type GameMapLoader = {
getMapData(map: GameMapType): MapData;
}
};
export interface MapData {
export type MapData = {
mapBin: () => Promise<Uint8Array>;
miniMapBin: () => Promise<Uint8Array>;
manifest: () => Promise<MapManifest>;
webpPath: () => Promise<string>;
}
};
+44 -44
View File
@@ -14,17 +14,17 @@ import {
} from "./Game";
import { TileRef, TileUpdate } from "./GameMap";
export interface GameUpdateViewData {
export type GameUpdateViewData = {
tick: number;
updates: GameUpdates;
packedTileUpdates: BigUint64Array;
playerNameViewData: Record<string, NameViewData>;
}
};
export interface ErrorUpdate {
export type ErrorUpdate = {
errMsg: string;
stack?: string;
}
};
export enum GameUpdateType {
Tile,
@@ -67,13 +67,13 @@ export type GameUpdate =
| RailroadUpdate
| ConquestUpdate;
export interface BonusEventUpdate {
export type BonusEventUpdate = {
type: GameUpdateType.BonusEvent;
player: PlayerID;
tile: TileRef;
gold: number;
troops: number;
}
};
export enum RailType {
VERTICAL,
@@ -84,30 +84,30 @@ export enum RailType {
BOTTOM_RIGHT,
}
export interface RailTile {
export type RailTile = {
tile: TileRef;
railType: RailType;
}
};
export interface RailroadUpdate {
export type RailroadUpdate = {
type: GameUpdateType.RailroadEvent;
isActive: boolean;
railTiles: RailTile[];
}
};
export interface ConquestUpdate {
export type ConquestUpdate = {
type: GameUpdateType.ConquestEvent;
conquerorId: PlayerID;
conqueredId: PlayerID;
gold: Gold;
}
};
export interface TileUpdateWrapper {
export type TileUpdateWrapper = {
type: GameUpdateType.Tile;
update: TileUpdate;
}
};
export interface UnitUpdate {
export type UnitUpdate = {
type: GameUpdateType.Unit;
unitType: UnitType;
troops: number;
@@ -130,17 +130,17 @@ export interface UnitUpdate {
hasTrainStation: boolean;
trainType?: TrainType; // Only for trains
loaded?: boolean; // Only for trains
}
};
export interface AttackUpdate {
export type AttackUpdate = {
attackerID: number;
targetID: number;
troops: number;
id: string;
retreating: boolean;
}
};
export interface PlayerUpdate {
export type PlayerUpdate = {
type: GameUpdateType.Player;
nameViewData?: NameViewData;
clientID: ClientID | null;
@@ -166,65 +166,65 @@ export interface PlayerUpdate {
alliances: AllianceView[];
hasSpawned: boolean;
betrayals?: bigint;
}
};
export interface AllianceView {
export type AllianceView = {
id: number;
other: PlayerID;
createdAt: Tick;
expiresAt: Tick;
}
};
export interface AllianceRequestUpdate {
export type AllianceRequestUpdate = {
type: GameUpdateType.AllianceRequest;
requestorID: number;
recipientID: number;
createdAt: Tick;
}
};
export interface AllianceRequestReplyUpdate {
export type AllianceRequestReplyUpdate = {
type: GameUpdateType.AllianceRequestReply;
request: AllianceRequestUpdate;
accepted: boolean;
}
};
export interface BrokeAllianceUpdate {
export type BrokeAllianceUpdate = {
type: GameUpdateType.BrokeAlliance;
traitorID: number;
betrayedID: number;
}
};
export interface AllianceExpiredUpdate {
export type AllianceExpiredUpdate = {
type: GameUpdateType.AllianceExpired;
player1ID: number;
player2ID: number;
}
};
export interface AllianceExtensionUpdate {
export type AllianceExtensionUpdate = {
type: GameUpdateType.AllianceExtension;
playerID: number;
allianceID: number;
}
};
export interface TargetPlayerUpdate {
export type TargetPlayerUpdate = {
type: GameUpdateType.TargetPlayer;
playerID: number;
targetID: number;
}
};
export interface EmojiUpdate {
export type EmojiUpdate = {
type: GameUpdateType.Emoji;
emoji: EmojiMessage;
}
};
export interface DisplayMessageUpdate {
export type DisplayMessageUpdate = {
type: GameUpdateType.DisplayEvent;
message: string;
messageType: MessageType;
goldAmount?: bigint;
playerID: number | null;
params?: Record<string, string | number>;
}
};
export type DisplayChatMessageUpdate = {
type: GameUpdateType.DisplayChatEvent;
@@ -236,22 +236,22 @@ export type DisplayChatMessageUpdate = {
recipient: string;
};
export interface WinUpdate {
export type WinUpdate = {
type: GameUpdateType.Win;
allPlayersStats: AllPlayersStats;
winner: Winner;
}
};
export interface HashUpdate {
export type HashUpdate = {
type: GameUpdateType.Hash;
tick: Tick;
hash: number;
}
};
export interface UnitIncomingUpdate {
export type UnitIncomingUpdate = {
type: GameUpdateType.UnitIncoming;
unitID: number;
message: string;
messageType: MessageType;
playerID: number;
}
};
+3 -3
View File
@@ -39,10 +39,10 @@ import { UserSettings } from "./UserSettings";
const userSettings: UserSettings = new UserSettings();
interface PlayerCosmetics {
type PlayerCosmetics = {
pattern?: string | undefined;
flag?: string | undefined;
}
};
export class UnitView {
public _wasUpdated = true;
@@ -106,7 +106,7 @@ export class UnitView {
return this.data.pos;
}
owner(): PlayerView {
return this.gameView.playerBySmallID(this.data.ownerID)! as PlayerView;
return this.gameView.playerBySmallID(this.data.ownerID) as PlayerView;
}
isActive(): boolean {
return this.data.isActive;
+4 -4
View File
@@ -53,10 +53,10 @@ import {
} from "./TransportShipUtils";
import { UnitImpl } from "./UnitImpl";
interface Target {
type Target = {
tick: Tick;
target: Player;
}
};
class Donation {
constructor(
@@ -66,7 +66,7 @@ class Donation {
}
export class PlayerImpl implements Player {
public _lastTileChange: number = 0;
public _lastTileChange = 0;
public _pseudo_random: PseudoRandom;
private _gold: bigint;
@@ -278,7 +278,7 @@ export class PlayerImpl implements Player {
}
tiles(): ReadonlySet<TileRef> {
return new Set(this._tiles.values()) as Set<TileRef>;
return new Set(this._tiles.values());
}
borderTiles(): ReadonlySet<TileRef> {
+2 -2
View File
@@ -1,8 +1,8 @@
import { Unit } from "./Game";
import { TrainStation } from "./TrainStation";
export interface RailNetwork {
export type RailNetwork = {
connectStation(station: TrainStation): void;
removeStation(unit: Unit): void;
findStationsPath(from: TrainStation, to: TrainStation): TrainStation[];
}
};
+5 -5
View File
@@ -13,12 +13,12 @@ import { Cluster, TrainStation, TrainStationMapAdapter } from "./TrainStation";
* but it would be expensive to look through the graph to find a station.
* This class stores the existing stations for quick access
*/
export interface StationManager {
export type StationManager = {
addStation(station: TrainStation): void;
removeStation(station: TrainStation): void;
findStation(unit: Unit): TrainStation | null;
getAll(): Set<TrainStation>;
}
};
export class StationManagerImpl implements StationManager {
private stations: Set<TrainStation> = new Set();
@@ -43,10 +43,10 @@ export class StationManagerImpl implements StationManager {
}
}
export interface RailPathFinderService {
export type RailPathFinderService = {
findTilePath(from: TileRef, to: TileRef): TileRef[];
findStationsPath(from: TrainStation, to: TrainStation): TrainStation[];
}
};
class RailPathFinderServiceImpl implements RailPathFinderService {
constructor(private game: Game) {}
@@ -88,7 +88,7 @@ export function createRailNetwork(game: Game): RailNetwork {
}
export class RailNetworkImpl implements RailNetwork {
private maxConnectionDistance: number = 4;
private maxConnectionDistance = 4;
constructor(
private game: Game,
+2 -2
View File
@@ -2,7 +2,7 @@ import { AllPlayersStats } from "../Schemas";
import { NukeType, OtherUnitType, PlayerStats } from "../StatsSchemas";
import { Player, TerraNullius } from "./Game";
export interface Stats {
export type Stats = {
getPlayerStats(player: Player): PlayerStats | null;
stats(): AllPlayersStats;
@@ -93,4 +93,4 @@ export interface Stats {
// Player loses a unit of type
unitLose(player: Player, type: OtherUnitType): void;
}
};
+5 -5
View File
@@ -9,16 +9,16 @@ import { Railroad } from "./Railroad";
/**
* Handle train stops at various station types
*/
interface TrainStopHandler {
type TrainStopHandler = {
onStop(mg: Game, station: TrainStation, trainExecution: TrainExecution): void;
}
};
/**
* All stop handlers share the same logic for the time being
* Behavior to be defined
*/
class CityStopHandler implements TrainStopHandler {
private factor: bigint = BigInt(2);
private factor = BigInt(2);
onStop(
mg: Game,
station: TrainStation,
@@ -38,7 +38,7 @@ class CityStopHandler implements TrainStopHandler {
}
class PortStopHandler implements TrainStopHandler {
private factor: bigint = BigInt(2);
private factor = BigInt(2);
constructor(private random: PseudoRandom) {}
onStop(
mg: Game,
@@ -59,7 +59,7 @@ class PortStopHandler implements TrainStopHandler {
}
class FactoryStopHandler implements TrainStopHandler {
private factor: bigint = BigInt(2);
private factor = BigInt(2);
onStop(
mg: Game,
station: TrainStation,
+3 -3
View File
@@ -102,7 +102,7 @@ export function sourceDstOceanShore(
const srcTile = closestShoreFromPlayer(gm, src, tile);
let dstTile: TileRef | null = null;
if (dst.isPlayer()) {
dstTile = closestShoreFromPlayer(gm, dst as Player, tile);
dstTile = closestShoreFromPlayer(gm, dst, tile);
} else {
dstTile = closestShoreTN(gm, tile, 50);
}
@@ -113,7 +113,7 @@ export function targetTransportTile(gm: Game, tile: TileRef): TileRef | null {
const dst = gm.playerBySmallID(gm.ownerID(tile));
let dstTile: TileRef | null = null;
if (dst.isPlayer()) {
dstTile = closestShoreFromPlayer(gm, dst as Player, tile);
dstTile = closestShoreFromPlayer(gm, dst, tile);
} else {
dstTile = closestShoreTN(gm, tile, 50);
}
@@ -235,7 +235,7 @@ export function candidateShoreTiles(
extremumTiles.maxX,
extremumTiles.maxY,
...sampledTiles,
].filter(Boolean) as number[];
].filter(Boolean);
return candidates;
}
+5 -5
View File
@@ -21,7 +21,7 @@ export class UnitImpl implements Unit {
private _targetUnit: Unit | undefined;
private _health: bigint;
private _lastTile: TileRef;
private _retreating: boolean = false;
private _retreating = false;
private _targetedBySAM = false;
private _reachedTarget = false;
private _lastSetSafeFromPirates: number; // Only for trade ships
@@ -30,14 +30,14 @@ export class UnitImpl implements Unit {
private _troops: number;
// Number of missiles in cooldown, if empty all missiles are ready.
private _missileTimerQueue: number[] = [];
private _hasTrainStation: boolean = false;
private _hasTrainStation = false;
private _patrolTile: TileRef | undefined;
private _level: number = 1;
private _targetable: boolean = true;
private _level = 1;
private _targetable = true;
private _loaded: boolean | undefined;
private _trainType: TrainType | undefined;
// Nuke only
private _trajectoryIndex: number = 0;
private _trajectoryIndex = 0;
private _trajectory: TrajectoryTile[];
constructor(
+4 -4
View File
@@ -1,7 +1,7 @@
export interface AStar<NodeType> {
export type AStar<NodeType> = {
compute(): PathFindResultType;
reconstructPath(): NodeType[];
}
};
export enum PathFindResultType {
NextTile,
@@ -25,7 +25,7 @@ export type AStarResult<NodeType> =
type: PathFindResultType.PathNotFound;
};
export interface Point {
export type Point = {
x: number;
y: number;
}
};
+3 -3
View File
@@ -36,8 +36,8 @@ export class MiniAStar implements AStar<TileRef> {
private dst: TileRef,
iterations: number,
maxTries: number,
waterPath: boolean = true,
directionChangePenalty: number = 0,
waterPath = true,
directionChangePenalty = 0,
) {
const srcArray: TileRef[] = Array.isArray(src) ? src : [src];
const miniSrc = srcArray.map((srcPoint) =>
@@ -113,7 +113,7 @@ function fixExtremes(upscaled: Cell[], cellDst: Cell, cellSrc?: Cell): Cell[] {
return upscaled;
}
function upscalePath(path: Cell[], scaleFactor: number = 2): Cell[] {
function upscalePath(path: Cell[], scaleFactor = 2): Cell[] {
// Scale up each point
const scaledPath = path.map(
(point) => new Cell(point.x * scaleFactor, point.y * scaleFactor),

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