All checks were successful
Build and Publish Docker Image / Build and Validate (pull_request) Successful in 14s
119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
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<Position>(ComponentType.POSITION);
|
|
const velocity = entity.getComponent<Velocity>(ComponentType.VELOCITY);
|
|
const health = entity.getComponent<Health>(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;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|