Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions src/components/game/WebGPUGameCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import { OverlayScene } from '@/phaser/scenes/OverlayScene';
import { debugInitialization, debugNetworking } from '@/utils/debugLogger';
import AssetManager from '@/assets/AssetManager';
import { useUIStore, UIState } from '@/store/uiStore';
import { getOverlayCoordinator, resetOverlayCoordinator } from '@/engine/overlay';

import { useWebGPURenderer, useGameInput, useCameraControl, usePostProcessing } from './hooks';

Expand Down Expand Up @@ -409,6 +410,14 @@ export function WebGPUGameCanvas() {
setLoadingStatus('Initializing overlay system');
setLoadingProgress(80);

// Initialize OverlayCoordinator with renderer and event bus
const overlayCoordinator = getOverlayCoordinator();
if (refs.overlayManager.current) {
overlayCoordinator.setOverlayManager(refs.overlayManager.current);
}
overlayCoordinator.setEventBus(bridge.eventBus);
overlayCoordinator.initializeFromStore();

// Initialize Phaser overlay
initializePhaserOverlay();

Expand Down Expand Up @@ -554,6 +563,8 @@ export function WebGPUGameCanvas() {
WorkerBridge.resetInstance();
workerBridgeRef.current = null;
}
// Reset overlay coordinator
resetOverlayCoordinator();
RenderStateWorldAdapter.resetInstance();
worldProviderRef.current = null;
eventBusRef.current = null;
Expand Down Expand Up @@ -621,34 +632,36 @@ export function WebGPUGameCanvas() {
return () => unsubscribe();
}, []);

// Subscribe to overlay settings changes
// Subscribe to overlay settings changes from UI (e.g., HUD overlay menu)
// The OverlayCoordinator handles forwarding to TSLGameOverlayManager
useEffect(() => {
const unsubscribe = useUIStore.subscribe((state: UIState, prevState: UIState) => {
const overlaySettings = state.overlaySettings;
const prevOverlaySettings = prevState.overlaySettings;

if (overlaySettings === prevOverlaySettings) return;

if (refs.overlayManager.current) {
refs.overlayManager.current.setActiveOverlay(overlaySettings.activeOverlay);
// Use coordinator to handle all overlay changes
const coordinator = getOverlayCoordinator();

const opacityKey = `${overlaySettings.activeOverlay}OverlayOpacity` as keyof typeof overlaySettings;
if (opacityKey in overlaySettings && typeof overlaySettings[opacityKey] === 'number') {
refs.overlayManager.current.setOpacity(overlaySettings[opacityKey] as number);
}
// Active overlay changed
if (overlaySettings.activeOverlay !== prevOverlaySettings.activeOverlay) {
coordinator.setActiveOverlay(overlaySettings.activeOverlay);
}

// Wire up range overlay toggles (Alt+A, Alt+V shortcuts)
if (overlaySettings.showAttackRange !== prevOverlaySettings.showAttackRange) {
refs.overlayManager.current.setShowAttackRange(overlaySettings.showAttackRange);
}
if (overlaySettings.showVisionRange !== prevOverlaySettings.showVisionRange) {
refs.overlayManager.current.setShowVisionRange(overlaySettings.showVisionRange);
}
// Attack range visibility changed (from UI, not keyboard)
if (overlaySettings.showAttackRange !== prevOverlaySettings.showAttackRange) {
coordinator.setShowAttackRange(overlaySettings.showAttackRange);
}

// Vision range visibility changed (from UI, not keyboard)
if (overlaySettings.showVisionRange !== prevOverlaySettings.showVisionRange) {
coordinator.setShowVisionRange(overlaySettings.showVisionRange);
}
});

return () => unsubscribe();
}, [refs.overlayManager]);
}, []);

return (
<div
Expand Down
79 changes: 66 additions & 13 deletions src/engine/input/handlers/GameplayInputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import type {
import { MouseButton } from '../types';
import { InputManager } from '../InputManager';
import { useGameStore } from '@/store/gameStore';
import { useUIStore, GameOverlayType } from '@/store/uiStore';
import { useUIStore } from '@/store/uiStore';
import { Transform } from '@/engine/components/Transform';
import { Unit } from '@/engine/components/Unit';
import { Health } from '@/engine/components/Health';
import { Resource } from '@/engine/components/Resource';
import { Selectable } from '@/engine/components/Selectable';
import { Building } from '@/engine/components/Building';
import { isBattleSimulatorMode, isMultiplayerMode } from '@/store/gameSetupStore';
import { getOverlayCoordinator } from '@/engine/overlay';

// =============================================================================
// CONSTANTS
Expand Down Expand Up @@ -111,6 +112,38 @@ export class GameplayInputHandler implements InputHandler {
return false;
}

// =============================================================================
// KEYBOARD INPUT - KEY UP (for hold-to-show overlays)
// =============================================================================

onKeyUp(
event: KeyboardInputEvent,
state: InputState,
deps: InputHandlerDependencies
): boolean {
const { key } = event;

// Hide attack range when 'a' is released (regardless of alt state)
// or when alt is released while showing
if (key === 'a' || key === 'alt') {
const coordinator = getOverlayCoordinator();
if (coordinator.isShowingAttackRange()) {
return this.handleAttackRangeOverlayRelease();
}
}

// Hide vision range when 'v' is released (regardless of alt state)
// or when alt is released while showing
if (key === 'v' || key === 'alt') {
const coordinator = getOverlayCoordinator();
if (coordinator.isShowingVisionRange()) {
return this.handleVisionRangeOverlayRelease();
}
}

return false;
}

// =============================================================================
// MOUSE INPUT
// =============================================================================
Expand Down Expand Up @@ -529,12 +562,8 @@ export class GameplayInputHandler implements InputHandler {
}

private handleOverlayToggle(): boolean {
const uiStore = useUIStore.getState();
const currentOverlay = uiStore.overlaySettings.activeOverlay;
const overlayOrder: GameOverlayType[] = ['none', 'elevation', 'threat', 'navmesh', 'resource', 'buildable'];
const currentIndex = overlayOrder.indexOf(currentOverlay);
const nextIndex = (currentIndex + 1) % overlayOrder.length;
uiStore.setActiveOverlay(overlayOrder[nextIndex]);
const coordinator = getOverlayCoordinator();
coordinator.cycleOverlay();
return true;
}

Expand All @@ -556,17 +585,41 @@ export class GameplayInputHandler implements InputHandler {
return true;
}

/**
* Show attack range overlay (hold-to-show, SC2-style).
* Called on Alt+A keydown.
*/
private handleAttackRangeOverlay(): boolean {
const store = useUIStore.getState();
const current = store.overlaySettings.showAttackRange;
store.setShowAttackRange(!current);
const coordinator = getOverlayCoordinator();
coordinator.setShowAttackRange(true);
return true;
}

/**
* Show vision range overlay (hold-to-show, SC2-style).
* Called on Alt+V keydown.
*/
private handleVisionRangeOverlay(): boolean {
const store = useUIStore.getState();
const current = store.overlaySettings.showVisionRange;
store.setShowVisionRange(!current);
const coordinator = getOverlayCoordinator();
coordinator.setShowVisionRange(true);
return true;
}

/**
* Hide attack range overlay when Alt+A is released.
*/
private handleAttackRangeOverlayRelease(): boolean {
const coordinator = getOverlayCoordinator();
coordinator.setShowAttackRange(false);
return true;
}

/**
* Hide vision range overlay when Alt+V is released.
*/
private handleVisionRangeOverlayRelease(): boolean {
const coordinator = getOverlayCoordinator();
coordinator.setShowVisionRange(false);
return true;
}

Expand Down
Loading