import { System } from '../core/System.ts'; import { SystemName, ComponentType } from '../core/Constants.ts'; import type { Entity } from '../core/Entity.ts'; import type { Position } from '../components/Position.ts'; import type { Velocity } from '../components/Velocity.ts'; import type { Health } from '../components/Health.ts'; /** * System responsible for moving entities based on their velocity and handling collisions. */ export class MovementSystem extends System { constructor() { super(SystemName.MOVEMENT); this.requiredComponents = [ComponentType.POSITION, ComponentType.VELOCITY]; this.priority = 10; } /** * Update the position of entities based on their velocity, applying friction and collision detection. * @param deltaTime - Time elapsed since last frame in seconds * @param entities - Entities matching system requirements */ process(deltaTime: number, entities: Entity[]): void { entities.forEach((entity) => { const position = entity.getComponent(ComponentType.POSITION); const velocity = entity.getComponent(ComponentType.VELOCITY); const health = entity.getComponent(ComponentType.HEALTH); if (!position || !velocity) return; if (velocity.lockTimer > 0) { velocity.lockTimer -= deltaTime; if (velocity.lockTimer <= 0) { velocity.lockTimer = 0; velocity.isLocked = false; } } const isProjectile = health && health.isProjectile; if (!isProjectile && !velocity.isLocked) { const speed = Math.sqrt(velocity.vx * velocity.vx + velocity.vy * velocity.vy); if (speed > velocity.maxSpeed) { const factor = velocity.maxSpeed / speed; velocity.vx *= factor; velocity.vy *= factor; } } const tileMap = this.engine.tileMap; const nextX = position.x + velocity.vx * deltaTime; if (tileMap && tileMap.isSolid(nextX, position.y)) { velocity.vx = 0; if (velocity.isLocked) { velocity.lockTimer = 0; velocity.isLocked = false; } } else { position.x = nextX; } const nextY = position.y + velocity.vy * deltaTime; if (tileMap && tileMap.isSolid(position.x, nextY)) { velocity.vy = 0; if (velocity.isLocked) { velocity.lockTimer = 0; velocity.isLocked = false; } } else { position.y = nextY; } if (!isProjectile) { const friction = velocity.isLocked ? 0.98 : 0.9; velocity.vx *= Math.pow(friction, deltaTime * 60); velocity.vy *= Math.pow(friction, deltaTime * 60); } if (tileMap) { const mapWidth = tileMap.cols * tileMap.tileSize; const mapHeight = tileMap.rows * tileMap.tileSize; if (position.x < 0) { position.x = 0; velocity.vx = 0; } else if (position.x > mapWidth) { position.x = mapWidth; velocity.vx = 0; } if (position.y < 0) { position.y = 0; velocity.vy = 0; } else if (position.y > mapHeight) { position.y = mapHeight; velocity.vy = 0; } } else { const canvas = this.engine.canvas; if (position.x < 0) { position.x = 0; velocity.vx = 0; } else if (position.x > canvas.width) { position.x = canvas.width; velocity.vx = 0; } if (position.y < 0) { position.y = 0; velocity.vy = 0; } else if (position.y > canvas.height) { position.y = canvas.height; velocity.vy = 0; } } }); } }