import { Grid as GridSystem, SystemNames, System } from "."; import { Game } from ".."; import { ComponentNames, Grid, Interactable } from "../components"; import { Control } from "../components/Control"; import { Action, KeyConstants, MovingSound, SOUNDS } from "../config"; import { Entity, Particles } from "../entities"; import { Coord2D, Direction } from "../interfaces"; import { colors } from "../utils"; export class Input extends System { private keys: Set; private mousePosition: Coord2D; constructor() { super(SystemNames.Input); this.keys = new Set(); this.mousePosition = { x: 0, y: 0 }; } public clearKeys() { this.keys.clear(); } public keyPressed(key: string) { this.keys.add(key); } public keyReleased(key: string) { this.keys.delete(key); } public update(_dt: number, game: Game) { game.forEachEntityWithComponent(ComponentNames.Control, (entity) => this.handleMovement(entity, game), ); game.forEachEntityWithComponent(ComponentNames.Interactable, (entity) => this.handleInteraction(entity), ); } private handleInteraction(entity: Entity) { const interactable = entity.getComponent( ComponentNames.Interactable, ); const interact = this.hasSomeKey( KeyConstants.ActionKeys.get(Action.INTERACT), ); if (!interact) { return; } interactable.interact(); KeyConstants.ActionKeys.get(Action.INTERACT)!.forEach((key) => this.keyReleased(key), ); } public handleMovement(entity: Entity, game: Game) { const controlComponent = entity.getComponent( ComponentNames.Control, ); if (!controlComponent.isControllable) return; const hasGrid = entity.hasComponent(ComponentNames.Grid); // TODO: check grid via controlComponent to notify entity of interaction const [moveUp, moveLeft, moveRight, moveDown] = [ Action.MOVE_UP, Action.MOVE_LEFT, Action.MOVE_RIGHT, Action.MOVE_DOWN, ].map((action) => this.hasSomeKey(KeyConstants.ActionKeys.get(action))); if (!hasGrid) { return; } const gridComponent = entity.getComponent(ComponentNames.Grid)!; if (gridComponent.movingDirection !== Direction.NONE) { return; } if (moveUp) { gridComponent.movingDirection = Direction.UP; KeyConstants.ActionKeys.get(Action.MOVE_UP)!.forEach((key) => this.keyReleased(key), ); } else if (moveLeft) { gridComponent.movingDirection = Direction.LEFT; KeyConstants.ActionKeys.get(Action.MOVE_LEFT)!.forEach((key) => this.keyReleased(key), ); } else if (moveRight) { gridComponent.movingDirection = Direction.RIGHT; KeyConstants.ActionKeys.get(Action.MOVE_RIGHT)!.forEach((key) => this.keyReleased(key), ); } else if (moveDown) { gridComponent.movingDirection = Direction.DOWN; KeyConstants.ActionKeys.get(Action.MOVE_DOWN)!.forEach((key) => this.keyReleased(key), ); } if (moveUp || moveLeft || moveRight || moveDown) { this.spawnParticlesAround(entity, game); this.playMoveSound(); } entity.addComponent(gridComponent); } private playMoveSound() { const movingSounds = Array.from(MovingSound.states!.values()); const soundName = movingSounds[Math.floor(Math.random() * movingSounds.length)].name; SOUNDS.get(soundName)!.play(); } private spawnParticlesAround(entity: Entity, game: Game) { const gridSystem = game.getSystem(SystemNames.Grid); const gridComponent = entity.getComponent(ComponentNames.Grid)!; const particles = new Particles({ center: gridSystem.gridToScreenPosition(gridComponent.gridPosition), particleCount: 4, spawnerShape: "circle", spawnerDimensions: { width: 10, height: 10, }, particleShape: "rectangle", particleMeanSpeed: 0.05, particleSpeedVariance: 0.005, particleMeanLife: 120, particleMeanSize: 3, particleSizeVariance: 1, particleLifeVariance: 30, particleColors: [colors.gray, colors.darkGray, colors.lightPurple], }); game.addEntity(particles); } private hasSomeKey(keys?: string[]): boolean { if (keys) { return keys.some((key) => this.keys.has(key)); } return false; } public setMousePosition(mousePosition: Coord2D) { this.mousePosition = mousePosition; } public getMousePosition(): Coord2D { return this.mousePosition; } }