import { System } from '../core/System.ts'; import { SystemName } from '../core/Constants.ts'; import type { Engine } from '../core/Engine.ts'; import type { Entity } from '../core/Entity.ts'; interface MouseState { x: number; y: number; buttons: Record; buttonsPrevious: Record; } /** * System responsible for capturing and managing keyboard and mouse input. */ export class InputSystem extends System { keys: Record; keysPrevious: Record; mouse: MouseState; constructor() { super(SystemName.INPUT); this.requiredComponents = []; this.priority = 0; this.keys = {}; this.keysPrevious = {}; this.mouse = { x: 0, y: 0, buttons: {}, buttonsPrevious: {}, }; } /** * Initialize the system and set up event listeners. * @param engine - The game engine instance */ init(engine: Engine): void { super.init(engine); this.setupEventListeners(); } /** * Set up browser event listeners for keyboard and mouse. */ setupEventListeners(): void { window.addEventListener('keydown', (e) => { const key = e.key.toLowerCase(); const code = e.code.toLowerCase(); this.keys[key] = true; this.keys[code] = true; if (key === ' ') { this.keys['space'] = true; } if (code === 'space') { this.keys['space'] = true; } if (code === 'arrowup') this.keys['arrowup'] = true; if (code === 'arrowdown') this.keys['arrowdown'] = true; if (code === 'arrowleft') this.keys['arrowleft'] = true; if (code === 'arrowright') this.keys['arrowright'] = true; if ([' ', 'w', 'a', 's', 'd', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(key)) { e.preventDefault(); } }); window.addEventListener('keyup', (e) => { const key = e.key.toLowerCase(); const code = e.code.toLowerCase(); this.keys[key] = false; this.keys[code] = false; if (key === ' ') { this.keys['space'] = false; } if (code === 'space') { this.keys['space'] = false; } if (code === 'arrowup') this.keys['arrowup'] = false; if (code === 'arrowdown') this.keys['arrowdown'] = false; if (code === 'arrowleft') this.keys['arrowleft'] = false; if (code === 'arrowright') this.keys['arrowright'] = false; }); window.addEventListener('mousemove', (e) => { if (this.engine && this.engine.canvas) { const canvas = this.engine.canvas; const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; this.mouse.x = (e.clientX - rect.left) * scaleX; this.mouse.y = (e.clientY - rect.top) * scaleY; } }); window.addEventListener('mousedown', (e) => { this.mouse.buttons[e.button] = true; }); window.addEventListener('mouseup', (e) => { this.mouse.buttons[e.button] = false; }); } /** * Process input state (placeholder as processing happens via events). * @param _deltaTime - Time elapsed * @param _entities - Matching entities */ process(_deltaTime: number, _entities: Entity[]): void {} /** * Update previous frame states. Should be called at the end of each frame. */ updatePreviousStates(): void { this.keysPrevious = {}; for (const key in this.keys) { this.keysPrevious[key] = this.keys[key]; } this.mouse.buttonsPrevious = {}; for (const button in this.mouse.buttons) { this.mouse.buttonsPrevious[button] = this.mouse.buttons[button]; } } /** * Check if a key is currently being held down. * @param key - The key name or code * @returns True if the key is pressed */ isKeyPressed(key: string): boolean { return this.keys[key.toLowerCase()] === true; } /** * Check if a key was pressed in the current frame. * @param key - The key name or code * @returns True if the key was just pressed */ isKeyJustPressed(key: string): boolean { const keyLower = key.toLowerCase(); const isPressed = this.keys[keyLower] === true; const wasPressed = this.keysPrevious[keyLower] === true; return isPressed && !wasPressed; } /** * Get the current mouse position in world coordinates. * @returns The mouse coordinates */ getMousePosition(): { x: number; y: number } { return { x: this.mouse.x, y: this.mouse.y }; } /** * Check if a mouse button is currently being held down. * @param button - The button index (0=left, 1=middle, 2=right) * @returns True if the button is pressed */ isMouseButtonPressed(button = 0): boolean { return this.mouse.buttons[button] === true; } /** * Check if a mouse button was pressed in the current frame. * @param button - The button index * @returns True if the button was just pressed */ isMouseButtonJustPressed(button = 0): boolean { const isPressed = this.mouse.buttons[button] === true; const wasPressed = this.mouse.buttonsPrevious[button] === true; return isPressed && !wasPressed; } }