refactor: introduce core ECS for movement and AI
This commit is contained in:
@@ -1,27 +1,75 @@
|
||||
import { type World, type EntityId, type Actor, type Vec2 } from "../core/types";
|
||||
import { type World, type EntityId, type Actor, type Vec2, type CombatantActor } from "../core/types";
|
||||
import { idx } from "./world/world-logic";
|
||||
import { ECSWorld } from "./ecs/World";
|
||||
import { MovementSystem } from "./ecs/MovementSystem";
|
||||
import { AISystem } from "./ecs/AISystem";
|
||||
|
||||
export class EntityManager {
|
||||
private grid: Map<number, EntityId[]> = new Map();
|
||||
private actors: Map<EntityId, Actor>;
|
||||
private world: World;
|
||||
private lastId: number = 0;
|
||||
private ecs: ECSWorld;
|
||||
private movementSystem: MovementSystem;
|
||||
private aiSystem: AISystem;
|
||||
|
||||
constructor(world: World) {
|
||||
this.world = world;
|
||||
this.actors = world.actors;
|
||||
this.ecs = new ECSWorld();
|
||||
this.movementSystem = new MovementSystem(this.ecs, this.world, this);
|
||||
this.aiSystem = new AISystem(this.ecs, this.world, this);
|
||||
this.lastId = Math.max(0, ...this.actors.keys());
|
||||
this.ecs.setNextId(this.lastId + 1);
|
||||
|
||||
this.rebuildGrid();
|
||||
}
|
||||
|
||||
get ecsWorld(): ECSWorld {
|
||||
return this.ecs;
|
||||
}
|
||||
|
||||
get movement(): MovementSystem {
|
||||
return this.movementSystem;
|
||||
}
|
||||
|
||||
get ai(): AISystem {
|
||||
return this.aiSystem;
|
||||
}
|
||||
|
||||
rebuildGrid() {
|
||||
this.grid.clear();
|
||||
// Also re-sync ECS if needed, though typically we do this once at start
|
||||
for (const actor of this.actors.values()) {
|
||||
this.syncActorToECS(actor);
|
||||
this.addToGrid(actor);
|
||||
}
|
||||
}
|
||||
|
||||
private syncActorToECS(actor: Actor) {
|
||||
const id = actor.id;
|
||||
this.ecs.addComponent(id, "position", actor.pos);
|
||||
this.ecs.addComponent(id, "name", { name: actor.id.toString() });
|
||||
|
||||
if (actor.category === "combatant") {
|
||||
const c = actor as CombatantActor;
|
||||
this.ecs.addComponent(id, "stats", c.stats);
|
||||
this.ecs.addComponent(id, "energy", { current: c.energy, speed: c.speed });
|
||||
this.ecs.addComponent(id, "actorType", { type: c.type });
|
||||
if (c.isPlayer) {
|
||||
this.ecs.addComponent(id, "player", {});
|
||||
} else {
|
||||
this.ecs.addComponent(id, "ai", {
|
||||
state: c.aiState || "wandering",
|
||||
alertedAt: c.alertedAt,
|
||||
lastKnownPlayerPos: c.lastKnownPlayerPos
|
||||
});
|
||||
}
|
||||
} else if (actor.category === "collectible") {
|
||||
this.ecs.addComponent(id, "collectible", { type: "exp_orb", amount: actor.expAmount });
|
||||
}
|
||||
}
|
||||
|
||||
private addToGrid(actor: Actor) {
|
||||
const i = idx(this.world, actor.pos.x, actor.pos.y);
|
||||
if (!this.grid.has(i)) {
|
||||
@@ -61,6 +109,13 @@ export class EntityManager {
|
||||
actor.pos.x = to.x;
|
||||
actor.pos.y = to.y;
|
||||
|
||||
// Update ECS
|
||||
const posComp = this.ecs.getComponent(actorId, "position");
|
||||
if (posComp) {
|
||||
posComp.x = to.x;
|
||||
posComp.y = to.y;
|
||||
}
|
||||
|
||||
// Add to new position
|
||||
const newIdx = idx(this.world, to.x, to.y);
|
||||
if (!this.grid.has(newIdx)) this.grid.set(newIdx, []);
|
||||
@@ -69,6 +124,7 @@ export class EntityManager {
|
||||
|
||||
addActor(actor: Actor) {
|
||||
this.actors.set(actor.id, actor);
|
||||
this.syncActorToECS(actor);
|
||||
this.addToGrid(actor);
|
||||
}
|
||||
|
||||
@@ -76,12 +132,11 @@ export class EntityManager {
|
||||
const actor = this.actors.get(actorId);
|
||||
if (actor) {
|
||||
this.removeFromGrid(actor);
|
||||
this.ecs.destroyEntity(actorId);
|
||||
this.actors.delete(actorId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
getActorsAt(x: number, y: number): Actor[] {
|
||||
const i = idx(this.world, x, y);
|
||||
const ids = this.grid.get(i);
|
||||
|
||||
Reference in New Issue
Block a user