controllers push to the WebGL view directly, drop ClientGameRunner relays

BuildPreviewController and WarshipSelectionController now take the WebGL
view in their constructor and call view.updateGhostPreview /
view.setSelectedUnits themselves instead of emitting bus events that
ClientGameRunner forwarded. Splits the old mountWebGLDebugRenderer in
two — createWebGLView builds the view up front so the renderer can wire
controllers to it, mountWebGLDebugRenderer does the per-frame plumbing
after the transformHandler exists. GhostPreviewUpdatedEvent had no
remaining consumers and is removed.
This commit is contained in:
evanpelle
2026-05-16 22:58:31 -07:00
parent a708a8c984
commit 7b1557b886
6 changed files with 81 additions and 65 deletions
@@ -4,7 +4,7 @@
* All rendering for the build ghost (outline, range circle, rail snap,
* crosshair) lives in the WebGL renderer. This controller owns the state:
* it queries buildables for the cursor tile, tracks whether the placement
* is valid, and emits GhostPreviewUpdatedEvent to feed the renderer.
* is valid, and pushes preview data straight to the WebGL view.
*/
import { EventBus } from "../../core/EventBus";
@@ -21,11 +21,11 @@ import { TransformHandler } from "../graphics/TransformHandler";
import { UIState } from "../graphics/UIState";
import {
ConfirmGhostStructureEvent,
GhostPreviewUpdatedEvent,
GhostStructureChangedEvent,
MouseMoveEvent,
MouseUpEvent,
} from "../InputHandler";
import { GameView as WebGLGameView } from "../render/gl";
import type { GhostPreviewData } from "../render/types";
import {
BuildUnitIntentEvent,
@@ -50,6 +50,7 @@ export class BuildPreviewController implements Controller {
private eventBus: EventBus,
public uiState: UIState,
private transformHandler: TransformHandler,
private view: WebGLGameView,
) {}
init() {
@@ -183,15 +184,12 @@ export class BuildPreviewController implements Controller {
}
/**
* Build a GhostPreviewData snapshot from the current ghost state and emit
* it for the WebGL renderer to consume (StructurePass / RangeCirclePass /
* RailroadPass / CrosshairPass all read it via view.updateGhostPreview).
* Emits null when the ghost can't be placed.
* Push a GhostPreviewData snapshot to the WebGL view (StructurePass /
* RangeCirclePass / RailroadPass / CrosshairPass all read it). null when
* the ghost can't be placed.
*/
private emitGhostPreview(tileRef: TileRef | undefined): void {
this.eventBus.emit(
new GhostPreviewUpdatedEvent(this.buildGhostPreviewData(tileRef)),
);
this.view.updateGhostPreview(this.buildGhostPreviewData(tileRef));
}
private buildGhostPreviewData(
@@ -310,7 +308,7 @@ export class BuildPreviewController implements Controller {
this.pendingConfirm = null;
this.ghostUnit = null;
this.uiState.ghostRailPaths = [];
this.eventBus.emit(new GhostPreviewUpdatedEvent(null));
this.view.updateGhostPreview(null);
}
private removeGhostStructure() {
@@ -16,6 +16,7 @@ import {
WarshipSelectionBoxCompleteEvent,
WarshipSelectionBoxUpdateEvent,
} from "../InputHandler";
import { GameView as WebGLGameView } from "../render/gl";
import { MoveWarshipIntentEvent } from "../Transport";
const WARSHIP_SELECTION_RADIUS = 10;
@@ -47,6 +48,7 @@ export class WarshipSelectionController implements Controller {
private game: GameView,
private eventBus: EventBus,
private transformHandler: TransformHandler,
private view: WebGLGameView,
) {}
tick() {
@@ -295,17 +297,20 @@ export class WarshipSelectionController implements Controller {
* When event.isSelected is false it clears all selection state.
*/
private onUnitSelection(event: UnitSelectionEvent) {
// Selection box visuals are drawn by the WebGL SelectionBoxPass; this
// method just tracks selection state for the click-handler logic.
this.multiSelectedWarships = [];
this.selectedUnit = null;
if (!event.isSelected) return;
if (!event.isSelected) {
this.view.setSelectedUnits([]);
return;
}
if ((event.units ?? []).length > 0) {
this.multiSelectedWarships = event.units;
this.view.setSelectedUnits(event.units.map((u) => u.id()));
} else {
this.selectedUnit = event.unit;
this.view.setSelectedUnits(event.unit ? [event.unit.id()] : []);
}
}
}