@hiddentao/clockwork-engine
Version:
A TypeScript/PIXI.js game engine for deterministic, replayable games with built-in rendering
104 lines (103 loc) • 3.42 kB
JavaScript
import { EventEmitter } from "../EventEmitter";
/**
* Events emitted by CollisionGrid
*/
export var CollisionGridEventType;
(function (CollisionGridEventType) {
CollisionGridEventType["POINTS_CHANGED"] = "pointsChanged";
})(CollisionGridEventType || (CollisionGridEventType = {}));
/**
* CollisionGrid uses direct coordinate mapping for extremely fast collision detection
*/
export class CollisionGrid extends EventEmitter {
constructor() {
super();
this.gridMap = new Map();
this.sourceMap = new Map();
}
add(point, source) {
const key = this.getKey(point);
const sourceId = source.getCollisionSourceId();
// Check if already exists
const existing = this.gridMap.get(key) || [];
for (const existingSource of existing) {
if (existingSource.getCollisionSourceId() === sourceId) {
return false;
}
}
// Add to grid
if (!this.gridMap.has(key)) {
this.gridMap.set(key, []);
}
this.gridMap.get(key).push(source);
// Update source index
if (!this.sourceMap.has(sourceId)) {
this.sourceMap.set(sourceId, new Set());
}
this.sourceMap.get(sourceId).add(key);
this.emit(CollisionGridEventType.POINTS_CHANGED);
return true;
}
remove(point, source) {
const key = this.getKey(point);
const sources = this.gridMap.get(key);
if (!sources)
return false;
const sourceId = source.getCollisionSourceId();
const initialLength = sources.length;
for (let i = sources.length - 1; i >= 0; i--) {
if (sources[i].getCollisionSourceId() === sourceId) {
sources.splice(i, 1);
break;
}
}
if (sources.length === 0) {
this.gridMap.delete(key);
}
const sourceCoords = this.sourceMap.get(sourceId);
if (sourceCoords) {
sourceCoords.delete(key);
if (sourceCoords.size === 0) {
this.sourceMap.delete(sourceId);
}
}
const removed = sources.length < initialLength;
if (removed) {
this.emit(CollisionGridEventType.POINTS_CHANGED);
}
return removed;
}
removeSource(source) {
const sourceId = source.getCollisionSourceId();
const coords = this.sourceMap.get(sourceId);
if (!coords || coords.size === 0)
return false;
for (const key of coords) {
const sources = this.gridMap.get(key);
if (sources) {
const filtered = sources.filter((s) => s.getCollisionSourceId() !== sourceId);
if (filtered.length === 0) {
this.gridMap.delete(key);
}
else {
this.gridMap.set(key, filtered);
}
}
}
this.sourceMap.delete(sourceId);
this.emit(CollisionGridEventType.POINTS_CHANGED);
return true;
}
containsPoint(point) {
const key = this.getKey(point);
return this.gridMap.get(key) || [];
}
clear() {
this.gridMap.clear();
this.sourceMap.clear();
this.emit(CollisionGridEventType.POINTS_CHANGED);
}
getKey(point) {
return `${point.x},${point.y}`;
}
}