183 lines
5.1 KiB
TypeScript
183 lines
5.1 KiB
TypeScript
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<number, boolean>;
|
|
buttonsPrevious: Record<number, boolean>;
|
|
}
|
|
|
|
/**
|
|
* System responsible for capturing and managing keyboard and mouse input.
|
|
*/
|
|
export class InputSystem extends System {
|
|
keys: Record<string, boolean>;
|
|
keysPrevious: Record<string, boolean>;
|
|
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;
|
|
}
|
|
}
|