From bac29448c212e2ee525ff99ba4d75433efc7430a Mon Sep 17 00:00:00 2001 From: evanpelle Date: Sat, 16 May 2026 22:35:14 -0700 Subject: [PATCH] =?UTF-8?q?rename=20Layer=20=E2=86=92=20Controller;=20drop?= =?UTF-8?q?=20canvas2D-era=20interface=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Layer interface dates to the canvas2D era when each entry drew to the shared 2D context via renderLayer(ctx). With canvas2D gone, nothing draws there and the renderLayer hook is dead. Rename the interface ("main-thread analog of the worker's Execution") and trim it: interface Controller { init?: () => void; tick?: () => void; getTickIntervalMs?: () => number; } renderLayer / shouldTransform / redraw are gone. Sweep across 28 files: from "./Layer" → "./Controller", implements Layer → implements Controller, Layer[] / Map → Controller[] / Map. Delete the no-op renderLayer + shouldTransform method bodies that every layer had inherited. GameRenderer drops the RedrawGraphicsEvent listener + redraw() fanout (nothing implements redraw anymore) and the now-unused eventBus constructor field. One real case: AttackingTroopsOverlay.renderLayer wasn't a no-op — it updates DOM label transforms each frame so labels track the WebGL camera during pan/zoom. Rename to private updateLabelDOM() and start a self-driven RAF in init() so the per-frame updates keep running. Class names ending in "Layer" (UILayer, StructureIconsLayer, NameLayer, etc.) intentionally left as-is — those are separate identifiers and the class-rename / file-move is a follow-up. 407 tests pass. --- src/client/graphics/GameRenderer.ts | 20 +++----------- src/client/graphics/layers/AlertFrame.ts | 7 ++--- .../graphics/layers/AttackingTroopsOverlay.ts | 19 ++++++++----- src/client/graphics/layers/AttacksDisplay.ts | 10 ++----- src/client/graphics/layers/BuildMenu.ts | 4 +-- src/client/graphics/layers/ChatDisplay.ts | 4 +-- src/client/graphics/layers/ControlPanel.ts | 12 ++------- src/client/graphics/layers/Controller.ts | 27 +++++++++++++++++++ src/client/graphics/layers/EventsDisplay.ts | 10 ++----- src/client/graphics/layers/GameLeftSidebar.ts | 4 +-- .../graphics/layers/GameRightSidebar.ts | 4 +-- src/client/graphics/layers/HeadsUpMessage.ts | 4 +-- src/client/graphics/layers/ImmunityTimer.ts | 8 ++---- src/client/graphics/layers/InGamePromo.ts | 8 ++---- src/client/graphics/layers/Layer.ts | 10 ------- src/client/graphics/layers/Leaderboard.ts | 10 ++----- src/client/graphics/layers/MainRadialMenu.ts | 12 ++------- src/client/graphics/layers/MultiTabModal.ts | 4 +-- .../graphics/layers/PerformanceOverlay.ts | 8 ++---- .../graphics/layers/PlayerInfoOverlay.ts | 12 ++------- src/client/graphics/layers/PlayerPanel.ts | 4 +-- src/client/graphics/layers/RadialMenu.ts | 12 ++------- src/client/graphics/layers/ReplayPanel.ts | 9 ++----- src/client/graphics/layers/SettingsModal.ts | 4 +-- src/client/graphics/layers/SpawnTimer.ts | 8 ++---- .../graphics/layers/StructureIconsLayer.ts | 4 +-- src/client/graphics/layers/TeamStats.ts | 10 ++----- src/client/graphics/layers/UILayer.ts | 4 +-- src/client/graphics/layers/UnitDisplay.ts | 4 +-- src/client/graphics/layers/WinModal.ts | 10 ++----- 30 files changed, 95 insertions(+), 171 deletions(-) create mode 100644 src/client/graphics/layers/Controller.ts delete mode 100644 src/client/graphics/layers/Layer.ts diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index f3e3f2a3b..a1aad6d7f 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -2,7 +2,6 @@ import { EventBus } from "../../core/EventBus"; import { GameView } from "../../core/game/GameView"; import { UserSettings } from "../../core/game/UserSettings"; import { GameStartingModal } from "../GameStartingModal"; -import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler"; import { FrameProfiler } from "./FrameProfiler"; import { TransformHandler } from "./TransformHandler"; import { UIState } from "./UIState"; @@ -13,6 +12,7 @@ import { BuildMenu } from "./layers/BuildMenu"; import { ChatDisplay } from "./layers/ChatDisplay"; import { ChatModal } from "./layers/ChatModal"; import { ControlPanel } from "./layers/ControlPanel"; +import { Controller } from "./layers/Controller"; import { EmojiTable } from "./layers/EmojiTable"; import { EventsDisplay } from "./layers/EventsDisplay"; import { GameLeftSidebar } from "./layers/GameLeftSidebar"; @@ -20,7 +20,6 @@ import { GameRightSidebar } from "./layers/GameRightSidebar"; import { HeadsUpMessage } from "./layers/HeadsUpMessage"; import { ImmunityTimer } from "./layers/ImmunityTimer"; import { InGamePromo } from "./layers/InGamePromo"; -import { Layer } from "./layers/Layer"; import { Leaderboard } from "./layers/Leaderboard"; import { MainRadialMenu } from "./layers/MainRadialMenu"; import { MultiTabModal } from "./layers/MultiTabModal"; @@ -260,7 +259,7 @@ export function createRenderer( // When updating these layers please be mindful of the order. // Try to group layers by the return value of shouldTransform. // Not grouping the layers may cause excessive calls to context.save() and context.restore(). - const layers: Layer[] = [ + const layers: Controller[] = [ new UILayer(game, eventBus, transformHandler), new StructureIconsLayer(game, eventBus, uiState, transformHandler), new AttackingTroopsOverlay(game, transformHandler, eventBus, userSettings), @@ -298,7 +297,6 @@ export function createRenderer( ]; return new GameRenderer( - eventBus, transformHandler, uiState, layers, @@ -307,18 +305,16 @@ export function createRenderer( } export class GameRenderer { - private layerTickState = new Map(); + private layerTickState = new Map(); constructor( - private eventBus: EventBus, public transformHandler: TransformHandler, public uiState: UIState, - private layers: Layer[], + private layers: Controller[], private performanceOverlay: PerformanceOverlay, ) {} initialize() { - this.eventBus.on(RedrawGraphicsEvent, () => this.redraw()); this.layers.forEach((l) => l.init?.()); window.addEventListener("resize", () => @@ -329,14 +325,6 @@ export class GameRenderer { this.transformHandler.centerAll(0.9); } - redraw() { - this.layers.forEach((l) => { - if (l.redraw) { - l.redraw(); - } - }); - } - tick() { const nowMs = performance.now(); const shouldProfileTick = FrameProfiler.isEnabled(); diff --git a/src/client/graphics/layers/AlertFrame.ts b/src/client/graphics/layers/AlertFrame.ts index 2b0238f1f..168c9ee0a 100644 --- a/src/client/graphics/layers/AlertFrame.ts +++ b/src/client/graphics/layers/AlertFrame.ts @@ -7,7 +7,7 @@ import { } from "../../../core/game/GameUpdates"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { UserSettings } from "../../../core/game/UserSettings"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; // Parameters for the alert animation const ALERT_SPEED = 1.6; @@ -16,7 +16,7 @@ const RETALIATION_WINDOW_TICKS = 15 * 10; // 15 seconds const ALERT_COOLDOWN_TICKS = 15 * 10; // 15 seconds @customElement("alert-frame") -export class AlertFrame extends LitElement implements Layer { +export class AlertFrame extends LitElement implements Controller { public game: GameView; private userSettings: UserSettings = new UserSettings(); @@ -118,9 +118,6 @@ export class AlertFrame extends LitElement implements Layer { } // The alert frame is not affected by the camera transform - shouldTransform(): boolean { - return false; - } private onBrokeAllianceUpdate(update: BrokeAllianceUpdate) { const myPlayer = this.game.myPlayer(); diff --git a/src/client/graphics/layers/AttackingTroopsOverlay.ts b/src/client/graphics/layers/AttackingTroopsOverlay.ts index b279489a0..bc88f90da 100644 --- a/src/client/graphics/layers/AttackingTroopsOverlay.ts +++ b/src/client/graphics/layers/AttackingTroopsOverlay.ts @@ -5,7 +5,7 @@ import { UserSettings } from "../../../core/game/UserSettings"; import { AlternateViewEvent } from "../../InputHandler"; import { renderTroops } from "../../Utils"; import { TransformHandler } from "../TransformHandler"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; // Match AttacksDisplay: aquarius for outgoing, red-400 for incoming. const OUTGOING_COLOR = "var(--color-aquarius)"; @@ -51,7 +51,7 @@ interface AttackLabel { attackerTroops: number; } -export class AttackingTroopsOverlay implements Layer { +export class AttackingTroopsOverlay implements Controller { private container: HTMLDivElement; private labelTemplate: HTMLDivElement; private labels = new Map(); @@ -70,10 +70,6 @@ export class AttackingTroopsOverlay implements Layer { private readonly userSettings: UserSettings, ) {} - shouldTransform(): boolean { - return false; - } - init() { this.container = document.createElement("div"); this.container.style.position = "fixed"; @@ -91,6 +87,15 @@ export class AttackingTroopsOverlay implements Layer { this.container.style.display = this.isVisible ? "" : "none"; }; this.eventBus.on(AlternateViewEvent, this.onAlternateView); + + // Self-driven RAF: DOM label positions must update every frame so the + // labels track the WebGL camera as the user pans/zooms. (Previously this + // ran via the now-deleted canvas2D RAF loop.) + const drive = () => { + this.updateLabelDOM(); + requestAnimationFrame(drive); + }; + requestAnimationFrame(drive); } destroy() { @@ -200,7 +205,7 @@ export class AttackingTroopsOverlay implements Layer { } } - renderLayer(_context: CanvasRenderingContext2D) { + private updateLabelDOM() { const screenPosOld = this.transformHandler.worldToScreenCoordinates( new Cell(0, 0), ); diff --git a/src/client/graphics/layers/AttacksDisplay.ts b/src/client/graphics/layers/AttacksDisplay.ts index d3889ddf8..82d0e1c92 100644 --- a/src/client/graphics/layers/AttacksDisplay.ts +++ b/src/client/graphics/layers/AttacksDisplay.ts @@ -22,12 +22,12 @@ import { GoToUnitEvent, } from "../TransformHandler"; import { UIState } from "../UIState"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const soldierIcon = assetUrl("images/SoldierIcon.svg"); const swordIcon = assetUrl("images/SwordIcon.svg"); @customElement("attacks-display") -export class AttacksDisplay extends LitElement implements Layer { +export class AttacksDisplay extends LitElement implements Controller { public eventBus: EventBus; public game: GameView; public uiState: UIState; @@ -110,12 +110,6 @@ export class AttacksDisplay extends LitElement implements Layer { this.requestUpdate(); } - shouldTransform(): boolean { - return false; - } - - renderLayer(): void {} - private renderButton(options: { content: any; onClick?: () => void; diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts index ad0c9efee..e9cb0c5cb 100644 --- a/src/client/graphics/layers/BuildMenu.ts +++ b/src/client/graphics/layers/BuildMenu.ts @@ -25,7 +25,7 @@ import { import { renderNumber } from "../../Utils"; import { TransformHandler } from "../TransformHandler"; import { UIState } from "../UIState"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const warshipIcon = assetUrl("images/BattleshipIconWhite.svg"); const cityIcon = assetUrl("images/CityIconWhite.svg"); const factoryIcon = assetUrl("images/FactoryIconWhite.svg"); @@ -124,7 +124,7 @@ export const buildTable: BuildItemDisplay[][] = [ export const flattenedBuildTable = buildTable.flat(); @customElement("build-menu") -export class BuildMenu extends LitElement implements Layer { +export class BuildMenu extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; public uiState: UIState; diff --git a/src/client/graphics/layers/ChatDisplay.ts b/src/client/graphics/layers/ChatDisplay.ts index 4704e0f51..2a7514c23 100644 --- a/src/client/graphics/layers/ChatDisplay.ts +++ b/src/client/graphics/layers/ChatDisplay.ts @@ -10,7 +10,7 @@ import { } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; import { onlyImages } from "../../../core/Util"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; interface ChatEvent { description: string; @@ -20,7 +20,7 @@ interface ChatEvent { } @customElement("chat-display") -export class ChatDisplay extends LitElement implements Layer { +export class ChatDisplay extends LitElement implements Controller { public eventBus: EventBus; public game: GameView; diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index 1bc72d3f5..480be247c 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -9,13 +9,13 @@ import { ClientID } from "../../../core/Schemas"; import { AttackRatioEvent } from "../../InputHandler"; import { renderNumber, renderTroops } from "../../Utils"; import { UIState } from "../UIState"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const goldCoinIcon = assetUrl("images/GoldCoinIcon.svg"); const soldierIcon = assetUrl("images/SoldierIcon.svg"); const swordIcon = assetUrl("images/SwordIcon.svg"); @customElement("control-panel") -export class ControlPanel extends LitElement implements Layer { +export class ControlPanel extends LitElement implements Controller { public game: GameView; public clientID: ClientID; public eventBus: EventBus; @@ -111,14 +111,6 @@ export class ControlPanel extends LitElement implements Layer { this.uiState.attackRatio = newRatio; } - renderLayer(context: CanvasRenderingContext2D) { - // Render any necessary canvas elements - } - - shouldTransform(): boolean { - return false; - } - setVisibile(visible: boolean) { this._isVisible = visible; this.requestUpdate(); diff --git a/src/client/graphics/layers/Controller.ts b/src/client/graphics/layers/Controller.ts new file mode 100644 index 000000000..e9151afe1 --- /dev/null +++ b/src/client/graphics/layers/Controller.ts @@ -0,0 +1,27 @@ +/** + * Controller — the main-thread analog of the worker's Execution. + * + * A Controller subscribes to events / game state and drives some slice of + * the client side (input, UI state, view updates). The interface is just + * lifecycle hooks; all coordination happens via the EventBus and direct + * references the controller is given at construction time. + * + * Naming: previously "Layer" — the name was a leftover from the canvas2D + * era when each entry in the array drew to the same 2D context. Now nothing + * draws to a shared canvas, so they're plain controllers. + */ +export interface Controller { + /** Called once at game start. Subscribe to events / set up state here. */ + init?: () => void; + + /** + * Called per game tick (10Hz). Optional — pure event subscribers can omit. + * + * If `getTickIntervalMs()` returns > 0, the controller is throttled to that + * wall-clock interval instead of running every tick. + */ + tick?: () => void; + + /** Optional throttle on tick frequency, in milliseconds. */ + getTickIntervalMs?: () => number; +} diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index cb6eb6211..3516e1ca4 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -29,7 +29,7 @@ import { SendAllianceRejectIntentEvent, SendAllianceRequestIntentEvent, } from "../../Transport"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; import { onlyImages } from "../../../core/Util"; @@ -68,7 +68,7 @@ interface GameEvent { } @customElement("events-display") -export class EventsDisplay extends LitElement implements Layer { +export class EventsDisplay extends LitElement implements Controller { public eventBus: EventBus; public game: GameView; public uiState: UIState; @@ -359,12 +359,6 @@ export class EventsDisplay extends LitElement implements Layer { ]; } - shouldTransform(): boolean { - return false; - } - - renderLayer(): void {} - private removeAllianceRenewalEvents(allianceID: number) { this.events = this.events.filter( (event) => diff --git a/src/client/graphics/layers/GameLeftSidebar.ts b/src/client/graphics/layers/GameLeftSidebar.ts index 97cad8cc3..296e057d4 100644 --- a/src/client/graphics/layers/GameLeftSidebar.ts +++ b/src/client/graphics/layers/GameLeftSidebar.ts @@ -7,8 +7,8 @@ import { GameMode, Team } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { Platform } from "../../Platform"; import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils"; +import { Controller } from "./Controller"; import { ImmunityBarVisibleEvent } from "./ImmunityTimer"; -import { Layer } from "./Layer"; import { SpawnBarVisibleEvent } from "./SpawnTimer"; const leaderboardRegularIcon = assetUrl( "images/LeaderboardIconRegularWhite.svg", @@ -18,7 +18,7 @@ const teamRegularIcon = assetUrl("images/TeamIconRegularWhite.svg"); const teamSolidIcon = assetUrl("images/TeamIconSolidWhite.svg"); @customElement("game-left-sidebar") -export class GameLeftSidebar extends LitElement implements Layer { +export class GameLeftSidebar extends LitElement implements Controller { @state() private isLeaderboardShow = false; @state() diff --git a/src/client/graphics/layers/GameRightSidebar.ts b/src/client/graphics/layers/GameRightSidebar.ts index 6770b0658..02c2d2968 100644 --- a/src/client/graphics/layers/GameRightSidebar.ts +++ b/src/client/graphics/layers/GameRightSidebar.ts @@ -8,8 +8,8 @@ import { crazyGamesSDK } from "../../CrazyGamesSDK"; import { TogglePauseIntentEvent } from "../../InputHandler"; import { PauseGameIntentEvent, SendWinnerEvent } from "../../Transport"; import { translateText } from "../../Utils"; +import { Controller } from "./Controller"; import { ImmunityBarVisibleEvent } from "./ImmunityTimer"; -import { Layer } from "./Layer"; import { ShowReplayPanelEvent } from "./ReplayPanel"; import { ShowSettingsModalEvent } from "./SettingsModal"; import { SpawnBarVisibleEvent } from "./SpawnTimer"; @@ -22,7 +22,7 @@ const fullscreenIcon = assetUrl("images/FullscreenIconWhite.svg"); const exitFullscreenIcon = assetUrl("images/ExitFullscreenIconWhite.svg"); @customElement("game-right-sidebar") -export class GameRightSidebar extends LitElement implements Layer { +export class GameRightSidebar extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; diff --git a/src/client/graphics/layers/HeadsUpMessage.ts b/src/client/graphics/layers/HeadsUpMessage.ts index 832e5871a..612cc2022 100644 --- a/src/client/graphics/layers/HeadsUpMessage.ts +++ b/src/client/graphics/layers/HeadsUpMessage.ts @@ -4,10 +4,10 @@ import { GameType } from "../../../core/game/Game"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; import { translateText } from "../../Utils"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; @customElement("heads-up-message") -export class HeadsUpMessage extends LitElement implements Layer { +export class HeadsUpMessage extends LitElement implements Controller { public game: GameView; @state() diff --git a/src/client/graphics/layers/ImmunityTimer.ts b/src/client/graphics/layers/ImmunityTimer.ts index d52baa8de..9f0070e55 100644 --- a/src/client/graphics/layers/ImmunityTimer.ts +++ b/src/client/graphics/layers/ImmunityTimer.ts @@ -3,14 +3,14 @@ import { customElement } from "lit/decorators.js"; import { EventBus, GameEvent } from "../../../core/EventBus"; import { GameMode } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; export class ImmunityBarVisibleEvent implements GameEvent { constructor(public readonly visible: boolean) {} } @customElement("immunity-timer") -export class ImmunityTimer extends LitElement implements Layer { +export class ImmunityTimer extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; @@ -88,10 +88,6 @@ export class ImmunityTimer extends LitElement implements Layer { } } - shouldTransform(): boolean { - return false; - } - render() { if (!this.isVisible || !this.isActive) { return html``; diff --git a/src/client/graphics/layers/InGamePromo.ts b/src/client/graphics/layers/InGamePromo.ts index cc179ca90..08d0a97e5 100644 --- a/src/client/graphics/layers/InGamePromo.ts +++ b/src/client/graphics/layers/InGamePromo.ts @@ -2,7 +2,7 @@ import { LitElement, html } from "lit"; import { customElement } from "lit/decorators.js"; import { GameView } from "../../../core/game/GameView"; import { crazyGamesSDK } from "../../CrazyGamesSDK"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const AD_TYPES = [ { type: "standard_iab_left1", selectorId: "in-game-bottom-left-ad" }, @@ -11,7 +11,7 @@ const AD_TYPES = [ ]; @customElement("in-game-promo") -export class InGamePromo extends LitElement implements Layer { +export class InGamePromo extends LitElement implements Controller { public game: GameView; private shouldShow: boolean = false; @@ -169,10 +169,6 @@ export class InGamePromo extends LitElement implements Layer { this.requestUpdate(); } - shouldTransform(): boolean { - return false; - } - render() { if (!this.shouldShow) { return html``; diff --git a/src/client/graphics/layers/Layer.ts b/src/client/graphics/layers/Layer.ts deleted file mode 100644 index 456648f79..000000000 --- a/src/client/graphics/layers/Layer.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface Layer { - init?: () => void; - tick?: () => void; - // Optional hint to throttle expensive ticks by wall-clock. - // If omitted or <= 0, the layer ticks whenever GameRenderer ticks. - getTickIntervalMs?: () => number; - renderLayer?: (context: CanvasRenderingContext2D) => void; - shouldTransform?: () => boolean; - redraw?: () => void; -} diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index 3100e88d9..4520e1b7c 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -6,7 +6,7 @@ import { EventBus } from "../../../core/EventBus"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { formatPercentage, renderNumber } from "../../Utils"; import { GoToPlayerEvent } from "../TransformHandler"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; interface Entry { name: string; @@ -20,7 +20,7 @@ interface Entry { } @customElement("leader-board") -export class Leaderboard extends LitElement implements Layer { +export class Leaderboard extends LitElement implements Controller { public game: GameView | null = null; public eventBus: EventBus | null = null; @@ -157,12 +157,6 @@ export class Leaderboard extends LitElement implements Layer { this.eventBus.emit(new GoToPlayerEvent(player)); } - renderLayer(context: CanvasRenderingContext2D) {} - - shouldTransform(): boolean { - return false; - } - render() { if (!this.visible) { return html``; diff --git a/src/client/graphics/layers/MainRadialMenu.ts b/src/client/graphics/layers/MainRadialMenu.ts index b6adba929..0d55e2da8 100644 --- a/src/client/graphics/layers/MainRadialMenu.ts +++ b/src/client/graphics/layers/MainRadialMenu.ts @@ -9,8 +9,8 @@ import { TransformHandler } from "../TransformHandler"; import { UIState } from "../UIState"; import { BuildMenu } from "./BuildMenu"; import { ChatIntegration } from "./ChatIntegration"; +import { Controller } from "./Controller"; import { EmojiTable } from "./EmojiTable"; -import { Layer } from "./Layer"; import { PlayerActionHandler } from "./PlayerActionHandler"; import { PlayerPanel } from "./PlayerPanel"; import { RadialMenu, RadialMenuConfig } from "./RadialMenu"; @@ -26,7 +26,7 @@ const swordIcon = assetUrl("images/SwordIconWhite.svg"); import { ContextMenuEvent } from "../../InputHandler"; @customElement("main-radial-menu") -export class MainRadialMenu extends LitElement implements Layer { +export class MainRadialMenu extends LitElement implements Controller { private radialMenu: RadialMenu; private playerActionHandler: PlayerActionHandler; @@ -173,14 +173,6 @@ export class MainRadialMenu extends LitElement implements Layer { }); } - renderLayer(context: CanvasRenderingContext2D) { - this.radialMenu.renderLayer(context); - } - - shouldTransform(): boolean { - return this.radialMenu.shouldTransform(); - } - closeMenu() { if (this.radialMenu.isMenuVisible()) { this.radialMenu.hideRadialMenu(); diff --git a/src/client/graphics/layers/MultiTabModal.ts b/src/client/graphics/layers/MultiTabModal.ts index f2b5f498e..90721c00b 100644 --- a/src/client/graphics/layers/MultiTabModal.ts +++ b/src/client/graphics/layers/MultiTabModal.ts @@ -6,10 +6,10 @@ import { GameType } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { MultiTabDetector } from "../../MultiTabDetector"; import { translateText } from "../../Utils"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; @customElement("multi-tab-modal") -export class MultiTabModal extends LitElement implements Layer { +export class MultiTabModal extends LitElement implements Controller { public game: GameView; private detector: MultiTabDetector; diff --git a/src/client/graphics/layers/PerformanceOverlay.ts b/src/client/graphics/layers/PerformanceOverlay.ts index 9f7fdbe5f..3686e499c 100644 --- a/src/client/graphics/layers/PerformanceOverlay.ts +++ b/src/client/graphics/layers/PerformanceOverlay.ts @@ -13,10 +13,10 @@ import { import type { LangSelector } from "../../LangSelector"; import { translateText } from "../../Utils"; import { FrameProfiler } from "../FrameProfiler"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; @customElement("performance-overlay") -export class PerformanceOverlay extends LitElement implements Layer { +export class PerformanceOverlay extends LitElement implements Controller { @property({ type: Object }) public eventBus!: EventBus; @@ -969,10 +969,6 @@ export class PerformanceOverlay extends LitElement implements Layer { } } - shouldTransform(): boolean { - return false; - } - private getPerformanceColor(fps: number): string { if (fps >= 55) return "performance-good"; if (fps >= 30) return "performance-warning"; diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index e720738f4..7c07d8d61 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -31,8 +31,8 @@ import { IMAGE_ICON_KIND, } from "../PlayerIcons"; import { TransformHandler } from "../TransformHandler"; +import { Controller } from "./Controller"; import { ImmunityBarVisibleEvent } from "./ImmunityTimer"; -import { Layer } from "./Layer"; import { CloseRadialMenuEvent } from "./RadialMenu"; import "./RelationSmiley"; import { SpawnBarVisibleEvent } from "./SpawnTimer"; @@ -68,7 +68,7 @@ function distSortUnitWorld(coord: { x: number; y: number }, game: GameView) { } @customElement("player-info-overlay") -export class PlayerInfoOverlay extends LitElement implements Layer { +export class PlayerInfoOverlay extends LitElement implements Controller { @property({ type: Object }) public game!: GameView; @@ -171,14 +171,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer { this.requestUpdate(); } - renderLayer(context: CanvasRenderingContext2D) { - // Implementation for Layer interface - } - - shouldTransform(): boolean { - return false; - } - setVisible(visible: boolean) { this._isInfoVisible = visible; this.requestUpdate(); diff --git a/src/client/graphics/layers/PlayerPanel.ts b/src/client/graphics/layers/PlayerPanel.ts index abd86b74b..0a01dd95b 100644 --- a/src/client/graphics/layers/PlayerPanel.ts +++ b/src/client/graphics/layers/PlayerPanel.ts @@ -36,8 +36,8 @@ import { } from "../../Utils"; import { UIState } from "../UIState"; import { ChatModal } from "./ChatModal"; +import { Controller } from "./Controller"; import { EmojiTable } from "./EmojiTable"; -import { Layer } from "./Layer"; import "./PlayerModerationModal"; import "./SendResourceModal"; const allianceIcon = assetUrl("images/AllianceIconWhite.svg"); @@ -53,7 +53,7 @@ const traitorIcon = assetUrl("images/TraitorIconLightRed.svg"); const breakAllianceIcon = assetUrl("images/TraitorIconWhite.svg"); @customElement("player-panel") -export class PlayerPanel extends LitElement implements Layer { +export class PlayerPanel extends LitElement implements Controller { public g: GameView; public eventBus: EventBus; public emojiTable: EmojiTable; diff --git a/src/client/graphics/layers/RadialMenu.ts b/src/client/graphics/layers/RadialMenu.ts index 8e65357e6..fa6a31015 100644 --- a/src/client/graphics/layers/RadialMenu.ts +++ b/src/client/graphics/layers/RadialMenu.ts @@ -4,7 +4,7 @@ import { EventBus, GameEvent } from "../../../core/EventBus"; import { CloseViewEvent } from "../../InputHandler"; import { PlaySoundEffectEvent } from "../../sound/Sounds"; import { getSvgAspectRatio, translateText } from "../../Utils"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; import { CenterButtonElement, MenuElement, @@ -51,7 +51,7 @@ type CenterButtonState = "default" | "back"; type RequiredRadialMenuConfig = Required; -export class RadialMenu implements Layer { +export class RadialMenu implements Controller { private menuElement: d3.Selection; private tooltipElement: HTMLDivElement | null = null; private isVisible: boolean = false; @@ -1341,14 +1341,6 @@ export class RadialMenu implements Layer { }); } - renderLayer(context: CanvasRenderingContext2D) { - // No need to render anything on the canvas - } - - shouldTransform(): boolean { - return false; - } - private isReopeningAllowed(): boolean { const now = Date.now(); const timeSinceHide = now - this.lastHideTime; diff --git a/src/client/graphics/layers/ReplayPanel.ts b/src/client/graphics/layers/ReplayPanel.ts index ea48bbb60..79e7b82c5 100644 --- a/src/client/graphics/layers/ReplayPanel.ts +++ b/src/client/graphics/layers/ReplayPanel.ts @@ -8,7 +8,7 @@ import { ReplaySpeedMultiplier, } from "../../utilities/ReplaySpeedMultiplier"; import { translateText } from "../../Utils"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; export class ShowReplayPanelEvent { constructor( @@ -18,7 +18,7 @@ export class ShowReplayPanelEvent { } @customElement("replay-panel") -export class ReplayPanel extends LitElement implements Layer { +export class ReplayPanel extends LitElement implements Controller { public game: GameView | undefined; public eventBus: EventBus | undefined; @@ -65,11 +65,6 @@ export class ReplayPanel extends LitElement implements Layer { this.eventBus?.emit(new ReplaySpeedChangeEvent(value)); } - renderLayer(_ctx: CanvasRenderingContext2D) {} - shouldTransform() { - return false; - } - render() { if (!this.visible) return html``; diff --git a/src/client/graphics/layers/SettingsModal.ts b/src/client/graphics/layers/SettingsModal.ts index 5f747c6ba..22a6cd7c5 100644 --- a/src/client/graphics/layers/SettingsModal.ts +++ b/src/client/graphics/layers/SettingsModal.ts @@ -11,7 +11,7 @@ import { SetBackgroundMusicVolumeEvent, SetSoundEffectsVolumeEvent, } from "../../sound/Sounds"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const structureIcon = assetUrl("images/CityIconWhite.svg"); const cursorPriceIcon = assetUrl("images/CursorPriceIconWhite.svg"); const darkModeIcon = assetUrl("images/DarkModeIconWhite.svg"); @@ -35,7 +35,7 @@ export class ShowSettingsModalEvent { } @customElement("settings-modal") -export class SettingsModal extends LitElement implements Layer { +export class SettingsModal extends LitElement implements Controller { public eventBus: EventBus; public userSettings: UserSettings; diff --git a/src/client/graphics/layers/SpawnTimer.ts b/src/client/graphics/layers/SpawnTimer.ts index ada193201..e4e025052 100644 --- a/src/client/graphics/layers/SpawnTimer.ts +++ b/src/client/graphics/layers/SpawnTimer.ts @@ -4,14 +4,14 @@ import { EventBus, GameEvent } from "../../../core/EventBus"; import { GameMode, GameType, Team } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { TransformHandler } from "../TransformHandler"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; export class SpawnBarVisibleEvent implements GameEvent { constructor(public readonly visible: boolean) {} } @customElement("spawn-timer") -export class SpawnTimer extends LitElement implements Layer { +export class SpawnTimer extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; public transformHandler: TransformHandler; @@ -95,10 +95,6 @@ export class SpawnTimer extends LitElement implements Layer { } } - shouldTransform(): boolean { - return false; - } - render() { if (!this.isVisible) { return html``; diff --git a/src/client/graphics/layers/StructureIconsLayer.ts b/src/client/graphics/layers/StructureIconsLayer.ts index 9c9cbfadb..e6df5c49b 100644 --- a/src/client/graphics/layers/StructureIconsLayer.ts +++ b/src/client/graphics/layers/StructureIconsLayer.ts @@ -30,14 +30,14 @@ import { } from "../../Transport"; import { TransformHandler } from "../TransformHandler"; import { UIState } from "../UIState"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; /** True for nuke types (AtomBomb, HydrogenBomb): ghost is preserved after placement so user can place multiple or keep selection (Enter/key confirm). */ export function shouldPreserveGhostAfterBuild(unitType: UnitType): boolean { return unitType === UnitType.AtomBomb || unitType === UnitType.HydrogenBomb; } -export class StructureIconsLayer implements Layer { +export class StructureIconsLayer implements Controller { /** Current ghost (null when no build type is active). */ private ghostUnit: { buildableUnit: BuildableUnit } | null = null; private readonly connectedAllySmallIds: Set = new Set(); diff --git a/src/client/graphics/layers/TeamStats.ts b/src/client/graphics/layers/TeamStats.ts index 6e846bcdf..6ce2c50f5 100644 --- a/src/client/graphics/layers/TeamStats.ts +++ b/src/client/graphics/layers/TeamStats.ts @@ -9,7 +9,7 @@ import { renderTroops, translateText, } from "../../Utils"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; interface TeamEntry { teamName: string; @@ -26,7 +26,7 @@ interface TeamEntry { } @customElement("team-stats") -export class TeamStats extends LitElement implements Layer { +export class TeamStats extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; @@ -125,12 +125,6 @@ export class TeamStats extends LitElement implements Layer { this.requestUpdate(); } - renderLayer(context: CanvasRenderingContext2D) {} - - shouldTransform(): boolean { - return false; - } - render() { if (!this.visible) return html``; diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index d6072d02c..f8b67eef7 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -16,7 +16,7 @@ import { } from "../../InputHandler"; import { MoveWarshipIntentEvent } from "../../Transport"; import { TransformHandler } from "../TransformHandler"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const WARSHIP_SELECTION_RADIUS = 10; @@ -29,7 +29,7 @@ const WARSHIP_SELECTION_RADIUS = 10; * it stays in the Layer list for lifecycle hooks (init / tick / event * subscriptions). */ -export class UILayer implements Layer { +export class UILayer implements Controller { // Currently selected single warship (game-logic readers use this; the // visual is drawn by WebGL SelectionBoxPass). private selectedUnit: UnitView | null = null; diff --git a/src/client/graphics/layers/UnitDisplay.ts b/src/client/graphics/layers/UnitDisplay.ts index 69c504cb5..456f16530 100644 --- a/src/client/graphics/layers/UnitDisplay.ts +++ b/src/client/graphics/layers/UnitDisplay.ts @@ -17,7 +17,7 @@ import { } from "../../InputHandler"; import { renderNumber, translateText } from "../../Utils"; import { UIState } from "../UIState"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; const warshipIcon = assetUrl("images/BattleshipIconWhite.svg"); const cityIcon = assetUrl("images/CityIconWhite.svg"); const factoryIcon = assetUrl("images/FactoryIconWhite.svg"); @@ -31,7 +31,7 @@ const samLauncherIcon = assetUrl("images/SamLauncherIconWhite.svg"); const defensePostIcon = assetUrl("images/ShieldIconWhite.svg"); @customElement("unit-display") -export class UnitDisplay extends LitElement implements Layer { +export class UnitDisplay extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; public uiState: UIState; diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index 2c42b5679..7907ab317 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -20,10 +20,10 @@ import { import { crazyGamesSDK } from "../../CrazyGamesSDK"; import { Platform } from "../../Platform"; import { SendWinnerEvent } from "../../Transport"; -import { Layer } from "./Layer"; +import { Controller } from "./Controller"; @customElement("win-modal") -export class WinModal extends LitElement implements Layer { +export class WinModal extends LitElement implements Controller { public game: GameView; public eventBus: EventBus; @@ -321,10 +321,4 @@ export class WinModal extends LitElement implements Layer { } }); } - - renderLayer(/* context: CanvasRenderingContext2D */) {} - - shouldTransform(): boolean { - return false; - } }