export class InputHandler { constructor(app) { this.app = app; this.canvas = null; this.isRightMouseDown = false; this.lastMouseX = 0; this.lastMouseY = 0; this.currentTileX = null; this.currentTileY = null; this.cursorUpdateThrottle = 100; // ms this.lastCursorUpdate = 0; this.keyPanSpeed = 3; // Controls camera movement speed with arrow keys } init() { this.canvas = document.getElementById('gameCanvas'); // Mouse events this.canvas.addEventListener('mousedown', (e) => this.onMouseDown(e)); this.canvas.addEventListener('mouseup', (e) => this.onMouseUp(e)); this.canvas.addEventListener('mousemove', (e) => this.onMouseMove(e)); this.canvas.addEventListener('wheel', (e) => this.onWheel(e)); this.canvas.addEventListener('contextmenu', (e) => e.preventDefault()); // Keyboard events document.addEventListener('keydown', (e) => this.onKeyDown(e)); } onMouseDown(event) { if (event.button === 2) { // Right mouse button this.isRightMouseDown = true; this.lastMouseX = event.clientX; this.lastMouseY = event.clientY; this.canvas.style.cursor = 'grabbing'; } else if (event.button === 0) { // Left mouse button const tile = this.app.renderer.screenToWorld(event.clientX, event.clientY); if (this.app.isPlacingBuilding && this.app.selectedBuildingType) { // Place building this.app.placeBuilding(tile.x, tile.y); } } } onMouseUp(event) { if (event.button === 2) { // Right mouse button this.isRightMouseDown = false; // A right-click should cancel building placement mode this.app.isPlacingBuilding = false; this.app.selectedBuildingType = null; this.canvas.style.cursor = 'default'; // Check if click (not drag) const dragThreshold = 5; const dx = Math.abs(event.clientX - this.lastMouseX); const dy = Math.abs(event.clientY - this.lastMouseY); if (dx < dragThreshold && dy < dragThreshold) { // Right click on tile - show context menu const tile = this.app.renderer.screenToWorld(event.clientX, event.clientY); const building = this.app.gameState.buildings[`${tile.x},${tile.y}`]; if (building && building.owner_id === this.app.player.player_id) { this.app.uiManager.showContextMenu( event.clientX, event.clientY, tile.x, tile.y ); } } } } onMouseMove(event) { // Update tile position const tile = this.app.renderer.screenToWorld(event.clientX, event.clientY); if (tile.x !== this.currentTileX || tile.y !== this.currentTileY) { this.currentTileX = tile.x; this.currentTileY = tile.y; // Highlight tile this.app.renderer.highlightTile(tile.x, tile.y); // Send cursor position to server (throttled) const now = Date.now(); if (now - this.lastCursorUpdate > this.cursorUpdateThrottle) { this.app.sendCursorPosition(tile.x, tile.y); this.lastCursorUpdate = now; } } // Handle camera panning if (this.isRightMouseDown) { const dx = (event.clientX - this.lastMouseX) * 0.1; const dy = (event.clientY - this.lastMouseY) * 0.1; this.app.renderer.moveCamera(-dx, dy); this.lastMouseX = event.clientX; this.lastMouseY = event.clientY; } } onWheel(event) { event.preventDefault(); const delta = event.deltaY > 0 ? -0.1 : 0.1; this.app.renderer.zoomCamera(delta); } onKeyDown(event) { if (event.key === 'Escape') { this.app.isPlacingBuilding = false; this.app.selectedBuildingType = null; this.app.uiManager.hideContextMenu(); } let moved = false; const s = this.keyPanSpeed; // shorthand for speed switch (event.key) { case 'ArrowUp': this.app.renderer.moveCamera(s, -s); // Move diagonally to pan straight up moved = true; break; case 'ArrowDown': this.app.renderer.moveCamera(-s, s); // Move diagonally to pan straight down moved = true; break; case 'ArrowLeft': this.app.renderer.moveCamera(-s, -s); // Move diagonally to pan straight left moved = true; break; case 'ArrowRight': this.app.renderer.moveCamera(s, s); // Move diagonally to pan straight right moved = true; break; } if (moved) { event.preventDefault(); // Prevents the browser from scrolling } } }