UNPKG

@rpgjs/physic

Version:

A deterministic 2D top-down physics library for RPG, sandbox and MMO games

202 lines (201 loc) 5.88 kB
import { Entity } from "./index7.js"; import { EntityMovementBody } from "./index31.js"; class MovementManager { constructor(resolveEntity) { this.resolveEntity = resolveEntity; this.entries = /* @__PURE__ */ new Map(); this.entityWrappers = /* @__PURE__ */ new WeakMap(); } /** * Convenience factory that binds the manager to a physics engine. * * @param engine - Physics engine whose entities will be controlled * @returns A movement manager configured with an entity resolver */ static forEngine(engine) { let manager; manager = new MovementManager((id) => { const entity = engine.getEntityByUUID(id); return entity ? manager.wrapEntity(entity) : void 0; }); return manager; } /** * Adds a movement strategy to an entity. * * @param target - Entity instance or entity UUID when a resolver is configured * @param strategy - Strategy to execute */ add(target, strategy) { const body = this.resolveTarget(target); const key = body.id; if (!this.entries.has(key)) { this.entries.set(key, { body, strategies: [] }); } this.entries.get(key).strategies.push(strategy); } /** * Removes a specific strategy from an entity. * * @param target - Entity instance or identifier * @param strategy - Strategy instance to remove * @returns True when the strategy has been removed */ remove(target, strategy) { const body = this.resolveTarget(target); const entry = this.entries.get(body.id); if (!entry) { return false; } const index = entry.strategies.indexOf(strategy); if (index === -1) { return false; } entry.strategies.splice(index, 1); if (entry.strategies.length === 0) { this.entries.delete(body.id); } return true; } /** * Removes all strategies from an entity. * * @param target - Entity or identifier */ clear(target) { const body = this.resolveTarget(target); this.entries.delete(body.id); } /** * Stops all movement for an entity immediately * * This method completely stops an entity's movement by: * - Removing all active movement strategies (dash, linear moves, etc.) * - Stopping the entity's velocity and angular velocity * - Clearing accumulated forces * - Waking up the entity if it was sleeping * * This is useful when changing maps, teleporting, or when you need * to halt an entity's movement completely without making it static. * * @param target - Entity, MovementBody, or identifier * * @example * ```ts * // Stop movement when changing maps * if (mapChanged) { * movement.stopMovement(playerEntity); * } * * // Stop movement after teleporting * entity.position.set(100, 200); * movement.stopMovement(entity); * * // Stop movement when player dies * if (player.isDead()) { * movement.stopMovement(playerEntity); * } * ``` */ stopMovement(target) { const body = this.resolveTarget(target); this.clear(target); if ("getEntity" in body && typeof body.getEntity === "function") { const entity = body.getEntity(); if (entity && typeof entity.stopMovement === "function") { entity.stopMovement(); } } } /** * Checks if an entity has active strategies. * * @param target - Entity or identifier * @returns True when strategies are registered */ hasActiveStrategies(target) { const body = this.resolveTarget(target); return (this.entries.get(body.id)?.strategies.length ?? 0) > 0; } /** * Returns a snapshot of the strategies assigned to an entity. * * @param target - Entity or identifier * @returns Copy of the strategies array (empty array when none) */ getStrategies(target) { const body = this.resolveTarget(target); const entry = this.entries.get(body.id); return entry ? [...entry.strategies] : []; } /** * Updates all registered strategies. * * Call this method once per frame before `PhysicsEngine.step()` so that the * physics simulation integrates the velocities that strategies configure. * * @param dt - Time delta in seconds */ update(dt) { for (const [key, entry] of this.entries) { const { body, strategies } = entry; if (strategies.length === 0) { this.entries.delete(key); continue; } for (let i = strategies.length - 1; i >= 0; i -= 1) { const current = strategies[i]; if (!current) { continue; } current.update(body, dt); if (current.isFinished?.()) { strategies.splice(i, 1); current.onFinished?.(); } } if (strategies.length === 0) { this.entries.delete(key); } } } /** * Removes all strategies from all entities. */ clearAll() { this.entries.clear(); } resolveTarget(target) { if (this.isMovementBody(target)) { return target; } if (target instanceof Entity) { return this.wrapEntity(target); } if (!this.resolveEntity) { throw new Error("MovementManager: cannot resolve entity from identifier without a resolver."); } const entity = this.resolveEntity(target); if (!entity) { throw new Error(`MovementManager: unable to resolve entity for identifier ${target}.`); } return entity; } wrapEntity(entity) { let wrapper = this.entityWrappers.get(entity); if (!wrapper) { wrapper = new EntityMovementBody(entity); this.entityWrappers.set(entity, wrapper); } return wrapper; } isMovementBody(value) { return Boolean( value && typeof value === "object" && "id" in value && "setVelocity" in value && typeof value.setVelocity === "function" ); } } export { MovementManager }; //# sourceMappingURL=index30.js.map