import {System} from '@/ecs/index.js'; import SpatialHash from '@/util/spatial-hash.js'; export default class Colliders extends System { hash; deindex(entities) { super.deindex(entities); for (const id of entities) { this.hash.remove(id); } } static get priority() { return { after: 'IntegratePhysics', }; } reindex(entities) { for (const id of entities) { if (1 === id) { this.hash = new SpatialHash(this.ecs.get(1).AreaSize); } } super.reindex(entities); for (const id of entities) { this.updateHash(this.ecs.get(id)); } } updateHash(entity) { if (!entity.Collider) { return; } entity.Collider.recalculateAabbs(); this.hash.update(entity.Collider.aabb, entity.id); } tick() { const {Ticking} = this.ecs.get(1); const seen = {}; for (const entity of this.ecs.changed(['Position'])) { if (seen[entity.id]) { continue; } seen[entity.id] = true; if (!entity.Collider) { continue; } const {collidingWith: wasCollidingWith} = entity.Collider; entity.Collider.collidingWith = {}; this.updateHash(entity); for (const other of this.within(entity.Collider.aabb)) { if (seen[other.id]) { continue; } seen[other.id] = true; if (!other.Collider) { continue; } delete other.Collider.collidingWith[entity.id]; const intersections = entity.Collider.isCollidingWith(other.Collider); if (intersections.length > 0) { entity.Collider.collidingWith[other.id] = true; other.Collider.collidingWith[entity.id] = true; if (!wasCollidingWith[other.id]) { if (entity.Collider.collisionStartScriptInstance) { entity.Collider.collisionStartScriptInstance.context.intersections = intersections; entity.Collider.collisionStartScriptInstance.context.other = other; Ticking.addTickingPromise(entity.Collider.collisionStartScriptInstance.tickingPromise()); } if (other.Collider.collisionStartScriptInstance) { other.Collider.collisionStartScriptInstance.context.intersections = intersections .map(([l, r]) => [r, l]); other.Collider.collisionStartScriptInstance.context.other = entity; Ticking.addTickingPromise(other.Collider.collisionStartScriptInstance.tickingPromise()); } } for (const [, {impassable}] of intersections) { if (impassable) { entity.Position.x = entity.Position.lastX entity.Position.y = entity.Position.lastY break; } } } } for (const otherId in wasCollidingWith) { if (!entity.Collider.collidingWith[otherId]) { const other = this.ecs.get(otherId); if (!other || !other.Collider) { continue; } if (entity.Collider.collisionEndScriptInstance) { entity.Collider.collisionEndScriptInstance.context.other = other; Ticking.addTickingPromise(entity.Collider.collisionEndScriptInstance.tickingPromise()); } if (other.Collider.collisionEndScriptInstance) { other.Collider.collisionEndScriptInstance.context.other = entity; Ticking.addTickingPromise(other.Collider.collisionEndScriptInstance.tickingPromise()); } } } } } within(query) { const within = new Set(); for (const id of this.hash.within(query)) { within.add(this.ecs.get(id)); } return within; } }