@caperjs/plugin-crunch 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/lib/Sensor.d.ts +10 -1
- package/lib/Sensor.d.ts.map +1 -1
- package/lib/caper-plugin-crunch.mjs +27 -19
- package/lib/caper-plugin-crunch.mjs.map +1 -1
- package/lib/version.d.ts +1 -1
- package/package.json +9 -7
- package/src/Sensor.ts +36 -4
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caper-plugin-crunch.mjs","names":[],"sources":["../src/types.ts","../src/utils.ts","../src/Entity.ts","../src/Actor.ts","../src/Group.ts","../src/Sensor.ts","../src/Solid.ts","../src/System.ts","../src/CrunchPhysicsPlugin.ts","../src/index.ts"],"sourcesContent":["import { PointLike, SizeLike } from '@caperjs/core';\nimport { Container } from 'pixi.js';\nimport { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Group } from './Group';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\n\nexport interface Vector2 {\n x: number;\n y: number;\n}\n\nexport interface Rectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type CollisionShape = 'rectangle';\nexport type EntityData = {\n [key: string]: any;\n};\n\nexport type PhysicsEntityClass = new (config?: PhysicsEntityConfig) => Actor | Solid | Sensor;\n\n/**\n * Collision layers using bitwise flags\n * Each entity can belong to multiple layers (using bitwise OR)\n * and can collide with multiple layers (using bitwise AND)\n */\nexport enum CollisionLayer {\n NONE = 0,\n DEFAULT = 1 << 0,\n PLAYER = 1 << 1,\n ENEMY = 1 << 2,\n PROJECTILE = 1 << 3,\n PLATFORM = 1 << 4,\n TRIGGER = 1 << 5,\n ITEM = 1 << 6,\n WALL = 1 << 7,\n FX = 1 << 8,\n // Reserve first 16 bits for built-in layers\n // Bits 16-31 are available for user-defined layers\n ALL = 0xffffffff, // All bits set to 1\n}\n\n/**\n * Interface for a registered collision layer\n */\nexport interface RegisteredCollisionLayer {\n /** Name of the collision layer */\n name: string;\n /** Numeric value of the collision layer (bitwise) */\n value: number;\n /** Description of the collision layer (optional) */\n description?: string;\n}\n\n/**\n * Registry for tracking custom collision layers\n */\nexport class CollisionLayerRegistry {\n private static _instance: CollisionLayerRegistry;\n private _layers: Map<string, RegisteredCollisionLayer> = new Map();\n private _usedIndices: Set<number> = new Set();\n\n /**\n * Get the singleton instance of the registry\n */\n public static get instance(): CollisionLayerRegistry {\n if (!CollisionLayerRegistry._instance) {\n CollisionLayerRegistry._instance = new CollisionLayerRegistry();\n }\n return CollisionLayerRegistry._instance;\n }\n\n /**\n * Register a new collision layer\n *\n * @param name Name of the collision layer\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @param description Optional description of the layer\n * @returns The registered collision layer\n */\n public register(name: string, index: number, description?: string): RegisteredCollisionLayer {\n if (index < 0 || index > 15) {\n throw new Error('Custom collision layer index must be between 0 and 15');\n }\n\n if (this._usedIndices.has(index)) {\n throw new Error(`Collision layer index ${index} is already in use`);\n }\n\n if (this._layers.has(name)) {\n throw new Error(`Collision layer with name \"${name}\" already exists`);\n }\n\n const value = 1 << (index + 16);\n const layer: RegisteredCollisionLayer = { name, value, description };\n\n this._layers.set(name, layer);\n this._usedIndices.add(index);\n\n return layer;\n }\n\n /**\n * Get a registered collision layer by name\n *\n * @param name Name of the collision layer\n * @returns The registered collision layer or undefined if not found\n */\n public get(name: string): RegisteredCollisionLayer | undefined {\n return this._layers.get(name);\n }\n\n /**\n * Get all registered collision layers\n *\n * @returns Array of all registered collision layers\n */\n public getAll(): RegisteredCollisionLayer[] {\n return Array.from(this._layers.values());\n }\n\n /**\n * Check if a collision layer with the given name exists\n *\n * @param name Name of the collision layer\n * @returns True if the layer exists\n */\n public has(name: string): boolean {\n return this._layers.has(name);\n }\n\n /**\n * Remove a registered collision layer\n *\n * @param name Name of the collision layer to remove\n * @returns True if the layer was removed, false if it didn't exist\n */\n public remove(name: string): boolean {\n const layer = this._layers.get(name);\n if (!layer) return false;\n\n // Calculate the index from the value\n const value = layer.value;\n const index = Math.log2(value) - 16;\n\n this._usedIndices.delete(index);\n return this._layers.delete(name);\n }\n\n /**\n * Clear all registered collision layers\n */\n public clear(): void {\n this._layers.clear();\n this._usedIndices.clear();\n }\n\n /**\n * Get the next available index for a custom collision layer\n *\n * @returns The next available index or -1 if all indices are used\n */\n public getNextAvailableIndex(): number {\n for (let i = 0; i < 16; i++) {\n if (!this._usedIndices.has(i)) {\n return i;\n }\n }\n return -1;\n }\n}\n\n/**\n * Utility functions for working with collision layers\n */\nexport const CollisionLayers = {\n /**\n * Creates a custom collision layer using bits 16-31 (user space)\n *\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @returns A unique collision layer value\n *\n * @example\n * ```typescript\n * // Create custom collision layers\n * const WATER_LAYER = CollisionLayers.createLayer(0); // 1 << 16\n * const LAVA_LAYER = CollisionLayers.createLayer(1); // 1 << 17\n * const CLOUD_LAYER = CollisionLayers.createLayer(2); // 1 << 18\n *\n * // Use in entity creation\n * const waterEntity = physics.createActor({\n * type: 'Water',\n * position: [100, 400],\n * size: [800, 100],\n * collisionLayer: WATER_LAYER,\n * collisionMask: CollisionLayer.PLAYER | CollisionLayer.ENEMY\n * });\n * ```\n */\n createLayer(index: number): number {\n if (index < 0 || index > 15) {\n throw new Error('Custom collision layer index must be between 0 and 15');\n }\n return 1 << (index + 16);\n },\n\n /**\n * Creates a collision mask from multiple layers\n *\n * @param layers Array of collision layers to combine\n * @returns A combined collision mask\n *\n * @example\n * ```typescript\n * // Create a mask that collides with players, enemies and projectiles\n * const mask = CollisionLayers.createMask([\n * CollisionLayer.PLAYER,\n * CollisionLayer.ENEMY,\n * CollisionLayer.PROJECTILE\n * ]);\n * ```\n */\n createMask(layers: number[]): number {\n return layers.reduce((mask, layer) => mask | layer, 0);\n },\n\n /**\n * Checks if a layer is included in a mask\n *\n * @param layer The layer to check\n * @param mask The mask to check against\n * @returns True if the layer is included in the mask\n *\n * @example\n * ```typescript\n * // Check if player layer is in the mask\n * if (CollisionLayers.isLayerInMask(CollisionLayer.PLAYER, entity.collisionMask)) {\n * console.log('Entity can collide with players');\n * }\n * ```\n */\n isLayerInMask(layer: number, mask: number): boolean {\n return (layer & mask) !== 0;\n },\n\n /**\n * Get the registry for custom collision layers\n *\n * @returns The collision layer registry\n */\n getRegistry(): CollisionLayerRegistry {\n return CollisionLayerRegistry.instance;\n },\n};\n\nexport interface PhysicsEntityConfig<D extends EntityData = EntityData> {\n id?: string;\n class?: PhysicsEntityClass;\n type?: PhysicsEntityType;\n position?: PointLike;\n size?: SizeLike;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n restitution?: number;\n view?: PhysicsEntityView;\n data?: Partial<D>;\n group?: Group;\n groupOffset?: PointLike;\n follows?: Entity;\n followOffset?: PointLike;\n /** Collision layer this entity belongs to (bitwise) */\n collisionLayer?: number;\n /** Collision mask defining which layers this entity collides with (bitwise) */\n collisionMask?: number;\n /** Whether to disable actor-to-actor collisions for this entity */\n disableActorCollisions?: boolean;\n}\n\nexport interface CollisionResult {\n collided: boolean;\n normal?: Vector2;\n penetration?: number;\n solid: Solid;\n}\n\nexport interface SensorOverlap {\n type: `${PhysicsEntityType}|${PhysicsEntityType}`;\n actor: Actor;\n sensor: Sensor;\n}\n\nexport interface CollisionResult {\n collided: boolean;\n normal?: Vector2;\n penetration?: number;\n solid: Solid;\n pushingSolid?: Solid;\n}\n\n/**\n * Result of an actor-to-actor collision\n */\nexport interface ActorCollisionResult {\n collided: boolean;\n normal?: Vector2;\n penetration?: number;\n actor: Actor;\n}\n\nexport interface Collision {\n type: `${PhysicsEntityType}|${PhysicsEntityType}`;\n entity1: Actor | Sensor;\n entity2: Actor | Solid;\n result: CollisionResult;\n}\n\n/**\n * Represents a collision between two actors\n */\nexport interface ActorCollision {\n type: `${PhysicsEntityType}|${PhysicsEntityType}`;\n actor1: Actor;\n actor2: Actor;\n result: ActorCollisionResult;\n}\n\nexport type PhysicsEntityView = Container;\nexport type PhysicsEntityType = 'Actor' | 'Solid' | 'Sensor' | 'Group' | string;\n","import { resolvePointLike, resolveSizeLike } from '@caperjs/core';\nimport { PhysicsEntityConfig } from './types';\n\n/**\n * Utility functions for resolving entity positions and sizes from various input formats.\n * These functions handle the conversion of different coordinate and size specifications\n * into standardized formats used by the physics system.\n */\n\n/**\n * Resolves an entity's position from various input formats.\n * Supports direct x/y values or a position object/array.\n *\n * @param config - Entity configuration containing position information\n * @returns Resolved {x, y} coordinates\n *\n * @example\n * ```typescript\n * // Using direct x/y values\n * resolveEntityPosition({ x: 100, y: 200 })\n * // → { x: 100, y: 200 }\n *\n * // Using position array\n * resolveEntityPosition({ position: [100, 200] })\n * // → { x: 100, y: 200 }\n *\n * // Using position object\n * resolveEntityPosition({ position: { x: 100, y: 200 } })\n * // → { x: 100, y: 200 }\n * ```\n */\nexport function resolveEntityPosition(config: PhysicsEntityConfig): { x: number; y: number } {\n const { x, y } =\n config.position !== undefined ? resolvePointLike(config.position) : { x: config?.x ?? 0, y: config?.y ?? 0 };\n\n return { x, y };\n}\n\n/**\n * Resolves an entity's size from various input formats.\n * Supports direct width/height values or a size object/array.\n *\n * @param config - Entity configuration containing size information\n * @returns Resolved {width, height} dimensions\n *\n * @example\n * ```typescript\n * // Using direct width/height values\n * resolveEntitySize({ width: 100, height: 200 })\n * // → { width: 100, height: 200 }\n *\n * // Using size array\n * resolveEntitySize({ size: [100, 200] })\n * // → { width: 100, height: 200 }\n *\n * // Using size object\n * resolveEntitySize({ size: { width: 100, height: 200 } })\n * // → { width: 100, height: 200 }\n *\n * // Using defaults\n * resolveEntitySize({})\n * // → { width: 32, height: 32 }\n * ```\n */\nexport function resolveEntitySize(config: PhysicsEntityConfig): { width: number; height: number } {\n const { width, height } =\n config.size !== undefined\n ? resolveSizeLike(config.size)\n : { width: config?.width ?? 32, height: config?.height ?? 32 };\n\n return { width, height };\n}\n","import {\n Application,\n bindAllMethods,\n defaultFactoryMethods,\n IApplication,\n PointLike,\n randomUUID,\n resolvePointLike,\n SignalConnection,\n SignalConnections,\n} from '@caperjs/core';\nimport CrunchPhysicsPlugin from './CrunchPhysicsPlugin';\nimport { Group } from './Group';\nimport { System } from './System';\nimport {\n CollisionLayer,\n EntityData,\n PhysicsEntityConfig,\n PhysicsEntityType,\n PhysicsEntityView,\n Rectangle,\n} from './types';\nimport { resolveEntityPosition, resolveEntitySize } from './utils';\n\n/**\n * Base class for all physics entities in the Crunch physics system.\n * Provides common functionality for position, size, view management, and lifecycle.\n *\n * Entity is the foundation for:\n * - Actors (dynamic objects)\n * - Solids (static objects)\n * - Sensors (trigger zones)\n *\n * It handles:\n * - Position and size management\n * - View (sprite) management and updates\n * - Group membership and relative positioning\n * - Culling and lifecycle states\n * - Signal connections for event handling\n *\n * @typeParam A - Application type, defaults to base Application\n * @typeParam D - Entity data type, defaults to base EntityData\n *\n * @example\n * ```typescript\n * // Create a custom entity\n * class CustomEntity extends Entity {\n * constructor() {\n * super({\n * type: 'Custom',\n * position: [100, 100],\n * size: [32, 32],\n * view: sprite\n * });\n * }\n *\n * // Override update for custom behavior\n * update(dt: number) {\n * super.update(dt);\n * // Custom update logic\n * }\n * }\n * ```\n */\nexport class Entity<D extends EntityData = EntityData> {\n public readonly entityType: PhysicsEntityType;\n protected _id: string;\n /** Unique type identifier for this entity */\n public type!: string;\n\n /** Color to use when rendering debug visuals */\n public debugColor: number;\n\n /** Whether the entity should be removed when culled (out of bounds) */\n public shouldRemoveOnCull: boolean;\n\n /** Entity width in pixels */\n public width: number;\n\n /** Entity height in pixels */\n public height: number;\n\n /** Visual representation (sprite/graphics) of this entity */\n public view!: PhysicsEntityView;\n\n /** Whether this entity is active and should be updated */\n\n /** Collision layer this entity belongs to (bitwise) */\n public collisionLayer: number = CollisionLayer.NONE;\n\n /** Collision mask defining which layers this entity collides with (bitwise) */\n public collisionMask: number = CollisionLayer.NONE;\n\n protected _data: Partial<D>;\n protected _group: Group | null;\n protected _groupOffset: { x: number; y: number };\n protected _isCulled: boolean;\n protected _isDestroyed: boolean;\n protected _isInitialized: boolean;\n protected _xRemainder: number;\n protected _yRemainder: number;\n protected _x: number;\n protected _y: number;\n protected signalConnections: SignalConnections;\n protected _following: Entity | null;\n protected _followOffset: { x: number; y: number };\n protected _config: PhysicsEntityConfig<D> | undefined;\n protected _active: boolean = true;\n\n public updatedFollowPosition: boolean = false;\n public updatedGroupPosition: boolean = false;\n\n set active(value: boolean) {\n this._active = value;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n set id(value: string) {\n this._id = value;\n }\n\n get id(): string {\n return this._id || this.type;\n }\n\n get make(): typeof defaultFactoryMethods {\n return this.app.make;\n }\n\n /**\n * Custom data associated with this entity\n */\n set data(value: Partial<D>) {\n this._data = value;\n }\n\n get data(): Partial<D> {\n return this._data;\n }\n\n setFollowing(entityToFollow: Entity | null, offset: PointLike = { x: 0, y: 0 }) {\n this._followOffset = resolvePointLike(offset);\n if (this._following) {\n this.system.removeFollower(this);\n }\n this._following = entityToFollow;\n if (entityToFollow) {\n this.system.addFollower(entityToFollow, this);\n }\n }\n\n get followOffset(): { x: number; y: number } {\n return this._followOffset || { x: 0, y: 0 };\n }\n\n get following(): Entity | null {\n return this._following;\n }\n\n get followers(): Entity[] {\n return this.system.getFollowersOf(this);\n }\n\n /**\n * The group this entity belongs to, if any.\n * Groups allow for collective movement and management of entities.\n */\n get group(): Group | null {\n return this._group;\n }\n\n set groupOffset(value: { x: number; y: number }) {\n this._groupOffset = resolvePointLike(value);\n }\n\n get groupOffset(): { x: number; y: number } {\n return this._groupOffset || { x: 0, y: 0 };\n }\n\n setGroup(group: Group | null, offset: PointLike = { x: 0, y: 0 }) {\n this._groupOffset = resolvePointLike(offset);\n // if we're already in a group, remove ourselves first\n if (this._group) {\n this.system.removeFromGroup(this);\n this.onRemovedFromGroup();\n }\n this._group = group;\n if (group) {\n this.system.addToGroup(group, this);\n this.onAddedToGroup();\n }\n }\n\n set position(value: PointLike) {\n const { x, y } = resolvePointLike(value);\n this.setPosition(x, y);\n }\n\n get position(): PointLike {\n return { x: this.x, y: this.y };\n }\n\n /**\n * Entity's X position in world space.\n * If the entity belongs to a group, returns position relative to group.\n */\n set x(value: number) {\n this._x = value;\n }\n\n get x(): number {\n if (this._group) {\n return Math.round(this._x + this._group.getChildOffset(this).x); // Return world position\n }\n return Math.round(this._x);\n }\n\n /**\n * Entity's Y position in world space.\n * If the entity belongs to a group, returns position relative to group.\n */\n set y(value: number) {\n this._y = value;\n }\n\n get y(): number {\n if (this._group) {\n return Math.round(this._y + this._group.getChildOffset(this).y); // Return world position\n }\n return Math.round(this._y);\n }\n\n /** Whether this entity is currently culled (out of bounds) */\n get isCulled(): boolean {\n return this._isCulled;\n }\n\n /** Whether this entity has been destroyed */\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /** Reference to the main application instance */\n get app(): IApplication {\n return Application.getInstance();\n }\n\n /** Reference to the physics plugin */\n get physics(): CrunchPhysicsPlugin {\n return this.app.getPlugin('crunch') as CrunchPhysicsPlugin;\n }\n\n /**\n * Creates a new Entity instance.\n *\n * @param config - Optional configuration for the entity\n */\n constructor(config?: PhysicsEntityConfig<D>) {\n bindAllMethods(this);\n\n this._config = config;\n\n this.signalConnections = new SignalConnections();\n this.shouldRemoveOnCull = false;\n this.width = 0;\n this.height = 0;\n\n this._data = {};\n this._group = null;\n this._isCulled = false;\n this._isDestroyed = false;\n this._isInitialized = false;\n this._xRemainder = 0;\n this._yRemainder = 0;\n\n this._x = 0;\n this._y = 0;\n\n if (config) {\n this.init(config);\n }\n\n this.initialize();\n this.addView();\n }\n\n /**\n * Called after construction to perform additional initialization.\n * Override this in subclasses to add custom initialization logic.\n */\n protected initialize() {\n // Override in subclass\n }\n\n /**\n * Called before update to prepare for the next frame.\n * Override this in subclasses to add pre-update logic.\n */\n public preUpdate(): void {\n // Override in subclass\n }\n\n /**\n * Called every frame to update the entity's state.\n * Override this in subclasses to add update logic.\n *\n * @param dt - Delta time in seconds since last update\n */\n public update(dt: number): void {\n // Override in subclass\n void dt;\n }\n\n /**\n * Called after update to finalize the frame.\n * Override this in subclasses to add post-update logic.\n */\n public postUpdate(): void {\n // Override in subclass\n }\n\n /**\n * Excludes collision types for this entity\n * @deprecated Use setCollisionMask instead\n */\n excludeCollisionType() {\n console.warn('excludeCollisionType is deprecated. Use setCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Includes collision types for this entity\n * @deprecated Use setCollisionMask instead\n */\n includeCollisionType() {\n console.warn('includeCollisionType is deprecated. Use setCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Removes collision types for this entity\n * @deprecated Use removeCollisionMask instead\n */\n removeCollisionType() {\n console.warn('removeCollisionType is deprecated. Use removeCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Adds collision types for this entity\n * @deprecated Use addCollisionMask instead\n */\n addCollisionType() {\n console.warn('addCollisionType is deprecated. Use addCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Checks if this entity can collide with a specific type\n */\n canCollideWith(): boolean {\n // Always return true as we're now using only collision layers/masks\n return true;\n }\n\n /**\n * Adds the entity's view to the physics container and updates its position.\n */\n protected addView() {\n if (this.view) {\n this.view.visible = true;\n this.view.label = this.id || this.type;\n if (this.system.container) {\n this.system.container.addChild(this.view);\n this.updateView();\n }\n }\n }\n\n /**\n * Initializes or reinitializes the entity with new configuration.\n * Used by object pools when recycling entities.\n *\n * @param config - New configuration to apply\n */\n public init(config: PhysicsEntityConfig<D>): void {\n if (!config) return;\n this._config = config as PhysicsEntityConfig<D>;\n\n if (config.id) {\n this._id = config.id;\n } else {\n this._id = randomUUID();\n }\n\n if (config.type) {\n this.type = config.type;\n }\n\n if (config.data) {\n this._data = config.data as Partial<D>;\n }\n\n const position = resolveEntityPosition(config);\n this._x = position.x;\n this._y = position.y;\n\n const size = resolveEntitySize(config);\n this.width = size.width;\n this.height = size.height;\n\n // Initialize collision layers\n if (config.collisionLayer !== undefined) {\n this.setCollisionLayer(config.collisionLayer);\n }\n\n if (config.collisionMask !== undefined) {\n this.setCollisionMask(config.collisionMask);\n }\n\n // Reset physics properties\n this._xRemainder = 0;\n this._yRemainder = 0;\n\n if (config.view) {\n this.setView(config.view);\n }\n\n if (config.group) {\n this.setGroup(config.group ?? null, config.groupOffset ? resolvePointLike(config.groupOffset) : { x: 0, y: 0 });\n }\n\n if (config.follows) {\n this.setFollowing(\n config.follows ?? null,\n config.followOffset ? resolvePointLike(config.followOffset) : { x: 0, y: 0 },\n );\n }\n // Show and update view if it exists\n this.addView();\n }\n\n /**\n * Resets the entity to its initial state for reuse in object pools.\n * Override this to handle custom reset logic.\n */\n public reset(): void {\n // Reset culling state\n this._isCulled = false;\n this._isDestroyed = false;\n\n // Reset remainders\n this._xRemainder = 0;\n this._yRemainder = 0;\n\n this._followOffset = { x: 0, y: 0 };\n this._following = null;\n\n this._groupOffset = { x: 0, y: 0 };\n this._group = null;\n\n this._data = {};\n\n this._x = -Number.MAX_SAFE_INTEGER;\n this._y = -Number.MAX_SAFE_INTEGER;\n\n if (this.view) {\n this.view.visible = false;\n }\n\n this.system.removeEntity(this);\n }\n\n /** Reference to the physics system */\n get system(): System {\n return this.physics.system;\n }\n\n /**\n * Called when the entity is added to a group.\n * Override this to handle custom group addition logic.\n */\n public onAddedToGroup(): void {\n // Override in subclass\n }\n\n /**\n * Called when the entity is removed from a group.\n * Override this to handle custom group removal logic.\n */\n public onRemovedFromGroup(): void {\n // Override in subclass\n }\n\n /**\n * Updates the entity's position and view.\n */\n public updatePosition(): void {\n this.x = this._x;\n this.y = this._y;\n this.updateView();\n }\n\n /**\n * Called when the entity is culled (goes out of bounds).\n * Override this to handle culling differently.\n */\n public onCull(): void {\n this._isCulled = true;\n // Default behavior: hide the view\n if (this.view) {\n this.view.visible = false;\n }\n }\n\n /**\n * Called when the entity is brought back after being culled.\n * Override this to handle unculling differently.\n */\n public onUncull(): void {\n this._isCulled = false;\n // Default behavior: show the view\n if (this.view) {\n this.view.visible = true;\n }\n }\n\n /**\n * Prepares the entity for removal/recycling.\n * Override this to handle custom cleanup.\n */\n public destroy(): void {\n if (this._isDestroyed) return;\n\n this._isDestroyed = true;\n this._isCulled = false;\n\n this.signalConnections.disconnectAll();\n\n // Don't destroy the view - it will be reused\n if (this.view) {\n this.view.visible = false;\n this.view.removeFromParent();\n }\n\n this.system.removeFollower(this);\n }\n\n /**\n * Called when the entity is removed from the physics system.\n * Override this to handle custom removal logic.\n */\n public onRemoved(): void {\n if (!this._isDestroyed) {\n this.destroy();\n }\n }\n\n /**\n * Sets a new view for the entity and updates its position.\n *\n * @param view - The new view to use\n */\n public setView(view: PhysicsEntityView): void {\n this.view = view;\n this.updateView();\n }\n\n /**\n * Updates the view's position to match the entity's position.\n */\n public updateView(): void {\n if (this.view && this.view.visible && this.view.position) {\n this.view.position.set(this.x, this.y);\n }\n }\n\n /**\n * Gets the entity's bounding rectangle.\n *\n * @returns Rectangle representing the entity's bounds\n */\n public getBounds(): Rectangle {\n return {\n x: this.x,\n y: this.y,\n width: this.width,\n height: this.height,\n };\n }\n\n /**\n * Sets the entity's position, resetting any movement remainders.\n *\n * @param x - New X position\n * @param y - New Y position\n */\n public setPosition(x: number, y: number): void {\n this._x = x;\n this._y = y;\n this._xRemainder = 0;\n this._yRemainder = 0;\n this.updateView();\n }\n\n /**\n * Alias for setPosition.\n *\n * @param x - New X position\n * @param y - New Y position\n */\n public moveTo(x: number, y: number): void {\n this.setPosition(x, y);\n }\n\n /**\n * Adds signal connections to the entity.\n *\n * @param args - Signal connections to add\n */\n public addSignalConnection(...args: SignalConnection[]) {\n for (const connection of args) {\n this.signalConnections.add(connection);\n }\n }\n\n /**\n * Alias for addSignalConnection.\n *\n * @param args - Signal connections to add\n */\n public connectSignal(...args: SignalConnection[]) {\n for (const connection of args) {\n this.signalConnections.add(connection);\n }\n }\n\n /**\n * Alias for addSignalConnection, specifically for action signals.\n *\n * @param args - Action signal connections to add\n */\n public connectAction(...args: SignalConnection[]) {\n for (const connection of args) {\n this.signalConnections.add(connection);\n }\n }\n\n /**\n * Checks if this entity can collide with another entity\n */\n public canCollideWithEntity(entity: Entity): boolean {\n // Check if the entities can collide based on their collision layers and masks\n // A collision occurs when (A.layer & B.mask) !== 0 && (B.layer & A.mask) !== 0\n return (this.collisionLayer & entity.collisionMask) !== 0 && (entity.collisionLayer & this.collisionMask) !== 0;\n }\n\n /**\n * Sets the collision layer for this entity\n *\n * @param layer The collision layer or layers (can be combined with bitwise OR)\n */\n public setCollisionLayer(layer: number): void {\n this.collisionLayer = layer;\n }\n\n /**\n * Adds the specified layers to this entity's collision layer\n *\n * @param layers The layers to add (can be combined with bitwise OR)\n */\n public addCollisionLayer(layers: number): void {\n this.collisionLayer |= layers;\n }\n\n /**\n * Removes the specified layers from this entity's collision layer\n *\n * @param layers The layers to remove (can be combined with bitwise OR)\n */\n public removeCollisionLayer(layers: number): void {\n this.collisionLayer &= ~layers;\n }\n\n /**\n * Sets the collision mask for this entity\n *\n * @param mask The collision mask (can be combined with bitwise OR)\n */\n public setCollisionMask(...mask: number[]): void {\n this.collisionMask = this.physics.createCollisionMask(...mask);\n }\n\n /**\n * Adds the specified layers to this entity's collision mask\n *\n * @param layers The layers to add to the mask (can be combined with bitwise OR)\n */\n public addCollisionMask(layers: number): void {\n this.collisionMask |= layers;\n }\n\n /**\n * Removes the specified layers from this entity's collision mask\n *\n * @param layers The layers to remove from the mask (can be combined with bitwise OR)\n */\n public removeCollisionMask(layers: number): void {\n this.collisionMask &= ~layers;\n }\n\n /**\n * Checks if this entity belongs to a specific collision layer\n *\n * @param layer The layer to check\n * @returns True if the entity belongs to the specified layer\n *\n * @example\n * ```typescript\n * // Check if entity is on the PLAYER layer\n * if (entity.hasCollisionLayer(CollisionLayer.PLAYER)) {\n * console.log('Entity is a player');\n * }\n *\n * // Check if entity is on a custom layer\n * const WATER_LAYER = CollisionLayers.createLayer(0);\n * if (entity.hasCollisionLayer(WATER_LAYER)) {\n * console.log('Entity is water');\n * }\n * ```\n */\n public hasCollisionLayer(layer: number): boolean {\n return (this.collisionLayer & layer) !== 0;\n }\n\n /**\n * Checks if this entity can collide with a specific collision layer\n *\n * @param layer The layer to check\n * @returns True if the entity can collide with the specified layer\n *\n * @example\n * ```typescript\n * // Check if entity can collide with players\n * if (entity.canCollideWithLayer(CollisionLayer.PLAYER)) {\n * console.log('Entity can collide with players');\n * }\n *\n * // Check if entity can collide with a custom layer\n * const WATER_LAYER = CollisionLayers.createLayer(0);\n * if (entity.canCollideWithLayer(WATER_LAYER)) {\n * console.log('Entity can collide with water');\n * }\n * ```\n */\n public canCollideWithLayer(layer: number): boolean {\n return (this.collisionMask & layer) !== 0;\n }\n}\n","import { Entity } from './Entity';\nimport { Solid } from './Solid';\nimport {\n ActorCollisionResult,\n CollisionResult,\n EntityData,\n PhysicsEntityConfig,\n PhysicsEntityType,\n Vector2,\n} from './types';\n\n/**\n * Dynamic physics entity that can move and collide with other entities.\n * Actors are typically used for players, enemies, projectiles, and other moving game objects.\n *\n * Features:\n * - Velocity-based movement with gravity\n * - Collision detection and response\n * - Solid surface detection (riding)\n * - Automatic culling when out of bounds\n * - Actor-to-actor collision detection\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a player actor\n * class Player extends Actor {\n * constructor() {\n * super({\n * type: 'Player',\n * position: [100, 100],\n * size: [32, 64],\n * view: playerSprite\n * });\n * }\n *\n * // Handle collisions\n * onCollide(result: CollisionResult) {\n * if (result.solid.type === 'Spike') {\n * this.die();\n * }\n * }\n *\n * // Handle actor-to-actor collisions\n * onActorCollide(result: ActorCollisionResult) {\n * if (result.actor.type === 'Enemy') {\n * this.takeDamage(10);\n * }\n * }\n *\n * // Custom movement\n * update(dt: number) {\n * super.update(dt);\n *\n * // Move left/right\n * if (this.app.input.isKeyDown('ArrowLeft')) {\n * this.velocity.x = -200;\n * } else if (this.app.input.isKeyDown('ArrowRight')) {\n * this.velocity.x = 200;\n * }\n *\n * // Jump when on ground\n * if (this.app.input.isKeyPressed('Space') && this.isRidingSolid()) {\n * this.velocity.y = -400;\n * }\n * }\n * }\n * ```\n */\nexport class Actor<D extends EntityData = EntityData> extends Entity<D> {\n public readonly entityType: PhysicsEntityType = 'Actor';\n\n /** Current velocity in pixels per second */\n public velocity: Vector2 = { x: 0, y: 0 };\n\n /** Whether actor-to-actor collisions are disabled for this actor */\n public disableActorCollisions: boolean = false;\n\n /** Whether the actor should be removed when culled (out of bounds) */\n public shouldRemoveOnCull: boolean = true;\n\n /** List of current frame collisions */\n public collisions: CollisionResult[] = [];\n\n /** List of current frame actor-to-actor collisions */\n public actorCollisions: ActorCollisionResult[] = [];\n\n /** Cache for isRidingSolid check */\n private _isRidingSolidCache: boolean | null = null;\n\n /** Tracks which solid is currently carrying this actor in the current frame */\n private _carriedBy: Solid | null = null;\n private _carriedByOverlap: number = 0;\n\n /** Tracks the grid cells this actor currently occupies */\n private _currentGridCells: string[] = [];\n\n /**\n * Initialize or reinitialize the actor with new configuration.\n *\n * @param config - Configuration for the actor\n */\n public init(config: PhysicsEntityConfig<D>): void {\n super.init(config);\n // Reset velocity and carried state\n this.velocity = { x: 0, y: 0 };\n this._isRidingSolidCache = null;\n this._carriedBy = null;\n this._carriedByOverlap = 0;\n this.actorCollisions = [];\n this._currentGridCells = [];\n\n if (config.disableActorCollisions !== undefined) {\n this.disableActorCollisions = config.disableActorCollisions;\n }\n\n // Add actor to grid initially if actor collisions are enabled\n if (this.system.enableActorCollisions && !this.disableActorCollisions) {\n this.updateGridCells();\n }\n }\n\n /**\n * Called at the start of each update to prepare for collision checks.\n */\n public preUpdate(): void {\n if (!this.active) return;\n\n this.collisions = [];\n this.actorCollisions = [];\n // Reset the cache at the start of each update\n this._isRidingSolidCache = null;\n this._carriedBy = null;\n this._carriedByOverlap = 0;\n }\n\n /**\n * Updates the actor's position based on velocity and handles collisions.\n *\n * @param dt - Delta time in seconds\n */\n public update(dt: number): void {\n if (!this.active) return;\n\n // Ensure velocity is valid\n if (!this.isRidingSolid()) {\n this.velocity.y += this.system.gravity * dt;\n }\n\n // Clamp velocity\n this.velocity.x = Math.min(Math.max(this.velocity.x, -this.system.maxVelocity), this.system.maxVelocity);\n this.velocity.y = Math.min(Math.max(this.velocity.y, -this.system.maxVelocity), this.system.maxVelocity);\n\n // Move horizontally\n if (this.velocity.x !== 0) {\n this.moveX(this.velocity.x * dt);\n }\n\n // Move vertically\n if (this.velocity.y !== 0) {\n this.moveY(this.velocity.y * dt);\n }\n\n if (this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n\n // Update view\n this.updateView();\n }\n\n /**\n * Called after update to handle post-movement effects.\n */\n public postUpdate(): void {\n if (!this.active) return;\n\n if (this.isRidingSolid()) {\n this.velocity.y = 0;\n }\n }\n\n /**\n * Resets the actor to its initial state.\n */\n public reset(): void {\n super.reset();\n\n this._isRidingSolidCache = null;\n this._carriedBy = null;\n this._carriedByOverlap = 0;\n this.velocity = { x: 0, y: 0 };\n\n this.updatePosition();\n }\n\n /**\n * Called when the actor is culled (goes out of bounds).\n * Override this to handle culling differently.\n */\n public onCull(): void {\n // Default behavior: destroy the view\n this.view?.destroy();\n }\n\n /**\n * Called when this actor collides with a solid.\n * Override this method to implement custom collision response.\n *\n * @param result - Information about the collision\n */\n public onCollide(result: CollisionResult): void {\n // Default implementation does nothing\n // Override this in your actor subclass to handle collisions\n void result;\n }\n\n /**\n * Called when this actor collides with another actor.\n * Override this method to implement custom actor-to-actor collision response.\n *\n * @param result - Information about the actor collision\n */\n public onActorCollide(result: ActorCollisionResult): void {\n // Default implementation does nothing\n // Override this in your actor subclass to handle actor-to-actor collisions\n void result;\n }\n\n /**\n * Checks if this actor is riding the given solid.\n * An actor is riding if it's directly above the solid.\n *\n * @param solid - The solid to check against\n * @returns True if riding the solid\n */\n public isRiding(solid: Solid): boolean {\n // Skip if solid has no collisions\n if (!solid.collideable) return false;\n\n // Check collision layers and masks\n // An actor can only ride a solid if their collision layers/masks allow interaction\n if ((this.collisionLayer & solid.collisionMask) === 0 || (solid.collisionLayer & this.collisionMask) === 0) {\n return false;\n }\n\n // If we're already being carried by a different solid this frame,\n // we can't be riding this one\n if (this._carriedBy && this._carriedBy !== solid) {\n return false;\n }\n\n // Must be directly above the solid (within 1 pixel)\n const actorBottom = this.y + this.height;\n const onTop = Math.abs(actorBottom - solid.y) <= 1;\n\n // Must be horizontally overlapping\n const overlap = this.x + this.width > solid.x && this.x < solid.x + solid.width;\n const overlapWidth = Math.min(this.x + this.width, solid.x + solid.width) - Math.max(this.x, solid.x);\n\n const isRiding = onTop && overlap;\n\n if (isRiding && overlapWidth > this._carriedByOverlap) {\n this._carriedBy = solid;\n this._carriedByOverlap = overlapWidth;\n }\n return isRiding;\n }\n\n /**\n * Checks if this actor is riding any solid in the physics system.\n * Uses caching to optimize multiple checks per frame.\n *\n * @returns True if riding any solid\n */\n public isRidingSolid(): boolean {\n // Return cached value if available\n if (this._isRidingSolidCache !== null) {\n return this._isRidingSolidCache;\n }\n\n // Calculate and cache the result\n const solids = this.getSolidsAt(this.x, this.y + 1);\n this._isRidingSolidCache = solids.some((solid) => this.isRiding(solid));\n return this._isRidingSolidCache;\n }\n\n /**\n * Called when the actor is squeezed between solids.\n * Override this to handle squishing differently.\n */\n public squish(result: CollisionResult): void {\n void result;\n // do something\n }\n\n /**\n * Updates the actor's grid cells in the spatial partitioning system.\n * This is called when the actor moves or when its size changes.\n */\n public updateGridCells(): void {\n // Skip if actor collisions are disabled system-wide or for this actor specifically\n if (this.disableActorCollisions) return;\n\n this.system.updateActorInGrid(this);\n }\n\n /**\n * Gets the current grid cells this actor occupies\n */\n public get currentGridCells(): string[] {\n return this._currentGridCells;\n }\n\n /**\n * Sets the current grid cells this actor occupies\n */\n public set currentGridCells(cells: string[]) {\n this._currentGridCells = cells;\n }\n\n /**\n * Moves the actor horizontally, checking for collisions with solids.\n *\n * @param amount - Distance to move in pixels\n * @param collisionHandler - Optional callback for handling collisions\n * @returns Array of collision results\n */\n public moveX(\n amount: number,\n collisionHandler?: (result: CollisionResult) => void,\n pushingSolid?: Solid,\n ): CollisionResult[] {\n // Early return if inactive or zero movement\n if (!this.active || amount === 0) return [];\n\n this._xRemainder += amount;\n const move = Math.round(this._xRemainder);\n\n // Early return if rounded movement is zero\n if (move === 0) return [];\n\n const collisions: CollisionResult[] = [];\n\n // Cache collision layer and mask for faster access\n const actorLayer = this.collisionLayer;\n const actorMask = this.collisionMask;\n\n // Skip collision checks if no collision mask\n if (actorMask === 0) {\n // Just move without checking collisions\n this._xRemainder -= move;\n this._x += move;\n this.updateView();\n\n return [];\n }\n\n this._xRemainder -= move;\n const sign = Math.sign(move);\n let remaining = Math.abs(move);\n const step = sign;\n\n // If we're being pushed by a solid, temporarily make it non-collidable\n if (pushingSolid) {\n pushingSolid.collideable = false;\n }\n\n // Move one pixel at a time, checking for collisions\n while (remaining > 0) {\n const nextX = this._x + step;\n\n // Get solids at the next position\n const solids = this.getSolidsAt(nextX, this._y);\n let collided = false;\n\n // Check for collisions with each solid\n for (const solid of solids) {\n // Skip if solid can't collide\n if (!solid.canCollide) continue;\n\n // Skip if collision layers don't match\n if ((actorLayer & solid.collisionMask) === 0 || (solid.collisionLayer & actorMask) === 0) {\n continue;\n }\n\n // Calculate collision details\n const result: CollisionResult = {\n collided: true,\n solid,\n normal: { x: -sign, y: 0 },\n penetration: step > 0 ? this.x + this.width - solid.x : solid.x + solid.width - this.x,\n pushingSolid,\n };\n\n // Add to collisions array\n collisions.push(result);\n\n // Call collision handler if provided\n if (collisionHandler) {\n collisionHandler(result);\n }\n\n // Call actor's collision handler\n this.onCollide(result);\n\n collided = true;\n }\n\n if (collided) {\n // Stop movement on collision\n break;\n } else {\n // Move to next position\n this._x = nextX;\n remaining--;\n\n // Update view every few pixels for better performance\n // This reduces the number of view updates during movement\n if (remaining % 4 === 0 || remaining === 0) {\n this.updateView();\n }\n }\n }\n\n // Restore solid's collidable state\n if (pushingSolid) {\n pushingSolid.collideable = true;\n }\n\n // Final view update if we moved\n if (Math.abs(move) - remaining > 0) {\n this.updateView();\n }\n\n return collisions;\n }\n\n /**\n * Moves the actor vertically, checking for collisions with solids.\n *\n * @param amount - Distance to move in pixels\n * @param collisionHandler - Optional callback for handling collisions\n * @returns Array of collision results\n */\n public moveY(\n amount: number,\n collisionHandler?: (result: CollisionResult) => void,\n pushingSolid?: Solid,\n ): CollisionResult[] {\n // Early return if inactive or zero movement\n if (!this.active || amount === 0) return [];\n\n this._yRemainder += amount;\n const move = Math.round(this._yRemainder);\n\n // Early return if rounded movement is zero\n if (move === 0) return [];\n\n const collisions: CollisionResult[] = [];\n\n // Cache collision layer and mask for faster access\n const actorLayer = this.collisionLayer;\n const actorMask = this.collisionMask;\n\n // Skip collision checks if no collision mask\n if (actorMask === 0) {\n // Just move without checking collisions\n this._yRemainder -= move;\n this._y += move;\n this.updateView();\n\n return [];\n }\n\n this._yRemainder -= move;\n const sign = Math.sign(move);\n let remaining = Math.abs(move);\n const step = sign;\n\n // If we're being pushed by a solid, temporarily make it non-collidable\n if (pushingSolid) {\n pushingSolid.collideable = false;\n }\n\n // Move one pixel at a time, checking for collisions\n while (remaining > 0) {\n const nextY = this._y + step;\n\n // Get solids at the next position\n const solids = this.getSolidsAt(this._x, nextY);\n let collided = false;\n\n // Check for collisions with each solid\n for (const solid of solids) {\n // Skip if solid can't collide\n if (!solid.canCollide) continue;\n\n // Skip if collision layers don't match\n if ((actorLayer & solid.collisionMask) === 0 || (solid.collisionLayer & actorMask) === 0) {\n continue;\n }\n\n // Calculate collision details\n const result: CollisionResult = {\n collided: true,\n solid,\n normal: { x: 0, y: -sign },\n penetration: step > 0 ? this.y + this.height - solid.y : solid.y + solid.height - this.y,\n pushingSolid,\n };\n\n // Add to collisions array\n collisions.push(result);\n\n // Call collision handler if provided\n if (collisionHandler) {\n collisionHandler(result);\n }\n\n // Call actor's collision handler\n this.onCollide(result);\n\n collided = true;\n }\n\n if (collided) {\n // Stop movement on collision\n break;\n } else {\n // Move to next position\n this._y = nextY;\n remaining--;\n\n // Update view every few pixels for better performance\n // This reduces the number of view updates during movement\n if (remaining % 4 === 0 || remaining === 0) {\n this.updateView();\n }\n }\n }\n\n // Restore solid's collidable state\n if (pushingSolid) {\n pushingSolid.collideable = true;\n }\n\n // Final view update if we moved\n if (Math.abs(move) - remaining > 0) {\n this.updateView();\n\n // Update grid cells if actor moved and actor collisions are enabled\n }\n\n return collisions;\n }\n\n /**\n * Updates the actor's view position.\n */\n public updateView(): void {\n if (this.view && this.view.visible) {\n this.view.x = this._x;\n this.view.y = this._y;\n }\n }\n\n /**\n * Gets all solids at the specified position that could collide with this actor.\n *\n * @param _x - X position to check\n * @param _y - Y position to check\n * @returns Array of solids at the position\n */\n protected getSolidsAt(_x: number, _y: number): Solid[] {\n return this.system.getSolidsAt(_x, _y, this);\n }\n\n /**\n * Checks if this actor is colliding with another actor.\n * The collision will only occur if:\n * 1. Both actors are active\n * 2. Neither actor has disabled actor collisions\n * 3. The collision layers and masks match:\n * - (this.collisionLayer & other.collisionMask) !== 0\n * - (other.collisionLayer & this.collisionMask) !== 0\n *\n * @param actor - The actor to check collision with\n * @returns Collision result with information about the collision\n */\n public checkActorCollision(actor: Actor): ActorCollisionResult {\n // Skip if either actor is not active\n if (!this.active || !actor.active) {\n return { collided: false, actor };\n }\n\n // Skip if either actor has disabled actor collisions\n if (this.disableActorCollisions || actor.disableActorCollisions) {\n return { collided: false, actor };\n }\n\n // Skip if the actors can't collide based on collision layers\n if ((this.collisionLayer & actor.collisionMask) === 0 || (actor.collisionLayer & this.collisionMask) === 0) {\n return { collided: false, actor };\n }\n\n // Simple AABB collision check\n const thisLeft = this.x;\n const thisRight = this.x + this.width;\n const thisTop = this.y;\n const thisBottom = this.y + this.height;\n\n const otherLeft = actor.x;\n const otherRight = actor.x + actor.width;\n const otherTop = actor.y;\n const otherBottom = actor.y + actor.height;\n\n // Check if the bounding boxes overlap\n if (thisRight > otherLeft && thisLeft < otherRight && thisBottom > otherTop && thisTop < otherBottom) {\n // Calculate penetration and normal\n const overlapX = Math.min(thisRight - otherLeft, otherRight - thisLeft);\n const overlapY = Math.min(thisBottom - otherTop, otherBottom - thisTop);\n\n let normal: Vector2;\n let penetration: number;\n\n // Determine the collision normal based on the smallest overlap\n if (overlapX < overlapY) {\n penetration = overlapX;\n normal = {\n x: thisLeft < otherLeft ? -1 : 1,\n y: 0,\n };\n } else {\n penetration = overlapY;\n normal = {\n x: 0,\n y: thisTop < otherTop ? -1 : 1,\n };\n }\n\n return {\n collided: true,\n actor,\n normal,\n penetration,\n };\n }\n\n return { collided: false, actor };\n }\n\n /**\n * Resolves a collision with another actor.\n *\n * @param result - The collision result to resolve\n * @param shouldMove - Whether this actor should move to resolve the collision\n * @returns The updated collision result\n */\n public resolveActorCollision(result: ActorCollisionResult): ActorCollisionResult {\n if (!result.collided || !result.normal || !result.penetration) {\n return result;\n }\n\n // Call the collision handler\n this.onActorCollide(result);\n\n // Example:\n // Move this actor to resolve the collision\n // this.x += result.normal.x * result.penetration * 0.5;\n // this.y += result.normal.y * result.penetration * 0.5;\n\n return result;\n }\n\n /**\n * Sets the actor's size and updates grid cells if needed.\n *\n * @param width - New width in pixels\n * @param height - New height in pixels\n */\n public setSize(width: number, height: number): void {\n const sizeChanged = this.width !== width || this.height !== height;\n\n this.width = width;\n this.height = height;\n\n // Update grid cells if size changed and actor collisions are enabled\n if (sizeChanged && this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n }\n\n /**\n * Sets the actor's width and updates grid cells if needed.\n *\n * @param value - New width in pixels\n */\n public setWidth(value: number): void {\n if (this.width !== value) {\n this.width = value;\n\n // Update grid cells if size changed and actor collisions are enabled\n if (this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n }\n }\n\n /**\n * Sets the actor's height and updates grid cells if needed.\n *\n * @param value - New height in pixels\n */\n public setHeight(value: number): void {\n if (this.height !== value) {\n this.height = value;\n\n // Update grid cells if size changed and actor collisions are enabled\n if (this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n }\n }\n}\n","import { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\nimport { EntityData, PhysicsEntityType } from './types';\n\n/**\n * A container for managing collections of physics entities that move together.\n * Groups are useful for creating compound objects, moving platforms with riders,\n * and other scenarios where multiple entities need to move as one unit.\n *\n * Features:\n * - Maintains relative positioning of child entities\n * - Supports actors, solids, and sensors as children\n * - Preserves world positions when adding entities\n * - Provides type-safe access to children by entity type\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a moving platform with obstacles\n * const platformGroup = physics.createGroup({\n * type: 'MovingPlatform',\n * position: [100, 300]\n * });\n *\n * // Add platform and spikes\n * const platform = physics.createSolid({\n * type: 'Platform',\n * size: [200, 32]\n * });\n *\n * const spikes = physics.createSensor({\n * type: 'Spikes',\n * position: [0, -32],\n * size: [200, 32]\n * });\n *\n * platformGroup.add(platform);\n * platformGroup.add(spikes);\n *\n * // Move the entire group\n * gsap.to(platformGroup, {\n * x: 500,\n * duration: 2,\n * yoyo: true,\n * repeat: -1\n * });\n * ```\n */\nexport class Group<D extends EntityData = EntityData> extends Entity<D> {\n protected _visible: boolean = true;\n\n public entityType: PhysicsEntityType = 'Group';\n /** Map of child entities to their relative offsets from the group's position */\n private childOffsets: Map<Entity, { x: number; y: number }> = new Map();\n /** Whether this group's position is static (not affected by physics) */\n public isStatic: boolean = true;\n\n /**\n * Gets the relative offset of a child entity from the group's position.\n * @param entity - The child entity to get the offset for\n * @returns The relative {x, y} offset of the entity\n */\n public getChildOffset(entity: Entity): { x: number; y: number } {\n return this.childOffsets.get(entity) ?? { x: 0, y: 0 };\n }\n\n set visible(value: boolean) {\n this.system.getEntitiesInGroup(this).forEach((entity) => {\n entity.view.visible = value;\n });\n }\n\n get visible(): boolean {\n return this._visible;\n }\n\n /**\n * Sets the group's X position, affecting all child entities.\n */\n set x(value: number) {\n this._x = value;\n }\n\n get x(): number {\n return this._x;\n }\n\n /**\n * Sets the group's Y position, affecting all child entities.\n */\n set y(value: number) {\n this._y = value;\n }\n\n get y(): number {\n return this._y;\n }\n\n /**\n * Add an entity to this container\n * @param entity The entity to add\n * @param preserveWorldPosition If true, the entity's world position will be preserved\n */\n public add(entity: Entity, offset: { x: number; y: number } = { x: 0, y: 0 }): Group {\n entity.setGroup(this, offset);\n return this;\n }\n\n /**\n * Remove an entity from this container\n */\n public remove(entity: Entity): Group {\n entity.setGroup(null);\n return this;\n }\n\n /**\n * Move the container and all its children\n */\n public move(x: number, y: number): void {\n // Update container position\n this._x += x;\n this._y += y;\n\n this.updateView();\n }\n\n /**\n * Force move the container and all its children to an absolute position\n */\n public moveTo(x: number, y: number): void {\n const dx = x - this.x;\n const dy = y - this.y;\n this.move(dx, dy);\n }\n\n /**\n * Get all children of a specific type\n */\n public getChildrenByType<E extends Entity>(type: PhysicsEntityType): E[] {\n return this.system\n .getEntitiesInGroup(this)\n .filter((child): child is E => child.entityType === type || child.type === type);\n }\n\n /**\n * Get all actors in this container\n */\n public getActors(): Actor[] {\n return this.getChildrenByType('Actor');\n }\n\n /**\n * Get all solids in this container\n */\n public getSolids(): Solid[] {\n return this.getChildrenByType('Solid');\n }\n\n /**\n * Get all sensors in this container\n */\n public getSensors(): Sensor[] {\n return this.getChildrenByType('Sensor');\n }\n\n /**\n * Get all groups in this container\n */\n public getGroups(): Group[] {\n return this.getChildrenByType('Group');\n }\n\n public destroy(): void {\n this.system.removeGroup(this);\n super.destroy();\n }\n}\n","import { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Solid } from './Solid';\nimport { EntityData, PhysicsEntityConfig, PhysicsEntityType, SensorOverlap, Vector2 } from './types';\n\n/**\n * A trigger zone that can detect overlaps with actors.\n * Sensors are typically used for collectibles, triggers, and detection zones.\n *\n * Features:\n * - Overlap detection with specific actor types\n * - Optional gravity and movement\n * - Can be static or dynamic\n * - Callbacks for enter/exit events\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a coin pickup sensor\n * class Coin extends Sensor {\n * constructor() {\n * super({\n * type: 'Coin',\n * position: [100, 100],\n * size: [32, 32],\n * view: coinSprite\n * });\n *\n * // Only detect overlaps with player\n * this.collidableTypes = ['Player'];\n * }\n *\n * // Called when a player enters the coin\n * onActorEnter(actor: Actor) {\n * if (actor.type === 'Player') {\n * increaseScore(10);\n * this.physics.removeSensor(this);\n * }\n * }\n * }\n *\n * // Create a damage zone\n * class Spikes extends Sensor {\n * constructor() {\n * super({\n * type: 'Spikes',\n * position: [300, 500],\n * size: [100, 32],\n * view: spikesSprite\n * });\n *\n * this.collidableTypes = ['Player', 'Enemy'];\n * this.isStatic = true; // Don't move or fall\n * }\n *\n * onActorEnter(actor: Actor) {\n * if (actor.type === 'Player') {\n * actor.damage(10);\n * }\n * }\n * }\n * ```\n */\nexport class Sensor<D extends EntityData = EntityData> extends Entity<D> {\n public readonly entityType: PhysicsEntityType = 'Sensor';\n /** Whether this sensor should be removed when culled */\n public shouldRemoveOnCull = false;\n\n /** List of actor types this sensor can detect */\n public collidableTypes: string[] = [];\n\n /** Current velocity in pixels per second */\n public velocity: Vector2 = { x: 0, y: 0 };\n\n /** Whether this sensor should stay in place */\n public isStatic: boolean = false;\n\n /** Set of actors currently overlapping this sensor */\n private overlappingActors: Set<Actor> = new Set();\n\n /** Cache for isRidingSolid check */\n private _isRidingSolidCache: boolean | null = null;\n\n private _currentSensorOverlaps = new Set<SensorOverlap>();\n private _currentOverlaps = new Set<Actor>();\n\n public setPosition(x: number, y: number): void {\n if (this.isStatic) {\n this._x = x;\n this._y = y;\n this._xRemainder = 0;\n this._yRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n } else {\n super.setPosition(x, y);\n }\n }\n\n set x(value: number) {\n super.x = value;\n if (this.isStatic) {\n this._xRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n }\n }\n\n get x(): number {\n return super.x;\n }\n\n set y(value: number) {\n super.y = value;\n if (this.isStatic) {\n this._yRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n }\n }\n\n get y(): number {\n return super.y;\n }\n\n /**\n * Initializes or reinitializes the sensor with new configuration.\n *\n * @param config - Configuration for the sensor\n */\n public init(config: PhysicsEntityConfig<D>): void {\n super.init(config);\n if (!this.velocity) {\n this.velocity = { x: 0, y: 0 };\n }\n this.velocity.x = 0;\n this.velocity.y = 0;\n if (!this.overlappingActors) {\n this.overlappingActors = new Set();\n }\n this._isRidingSolidCache = null;\n }\n\n /**\n * Checks if this sensor is riding the given solid.\n * Takes into account gravity direction for proper riding detection.\n *\n * @param solid - The solid to check against\n * @returns True if riding the solid\n */\n public isRiding(solid: Solid): boolean {\n const gravityDirection = Math.sign(this.system.gravity);\n if (gravityDirection > 0) {\n // Normal gravity - check if we're on top of the solid\n const sensorBottom = this.y + this.height;\n const onTop = Math.abs(sensorBottom - solid.y) <= 1;\n const overlap = this.x + this.width > solid.x && this.x < solid.x + solid.width;\n return onTop && overlap;\n } else {\n // Reversed gravity - check if we're on bottom of the solid\n const sensorTop = this.y;\n const onBottom = Math.abs(sensorTop - (solid.y + solid.height)) <= 1;\n const overlap = this.x + this.width > solid.x && this.x < solid.x + solid.width;\n return onBottom && overlap;\n }\n }\n\n /**\n * Checks if this sensor is riding any solid.\n * Uses caching to optimize multiple checks per frame.\n *\n * @returns True if riding any solid\n */\n public isRidingSolid(): boolean {\n // Return cached value if available\n if (this._isRidingSolidCache !== null) {\n return this._isRidingSolidCache;\n }\n\n // Calculate and cache the result\n const solids = this.getSolidsAt(this.x, this.system.gravity > 0 ? this.y + 1 : this.y - 1);\n this._isRidingSolidCache = solids.some((solid) => this.isRiding(solid));\n return this._isRidingSolidCache;\n }\n\n /**\n * Force moves the sensor to a new position, ignoring static state and collisions.\n *\n * @param x - New X position\n * @param y - New Y position\n */\n public moveStatic(x: number, y: number): void {\n this._x = x;\n this._y = y;\n this._xRemainder = 0;\n this._yRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n }\n\n /**\n * Moves the sensor horizontally, passing through solids.\n *\n * @param amount - Distance to move in pixels\n */\n public moveX(amount: number): void {\n if (this.isStatic) {\n return;\n }\n\n this._xRemainder += amount;\n const move = Math.round(this._xRemainder);\n\n if (move !== 0) {\n this._xRemainder -= move;\n const sign = Math.sign(move);\n let remaining = Math.abs(move);\n while (remaining > 0) {\n const step = sign;\n const nextX = this.x + step;\n\n // Check for collision with any solid\n let collided = false;\n for (const solid of this.getSolidsAt(nextX, this.y)) {\n if (solid.canCollide) {\n collided = true;\n break;\n }\n }\n\n if (!collided) {\n this._x = nextX;\n remaining--;\n this.updateView();\n this.checkActorOverlaps();\n } else {\n // Stop horizontal movement when hitting a solid\n this.velocity.x = 0;\n break;\n }\n }\n }\n }\n\n /**\n * Moves the sensor vertically, colliding with solids for riding.\n *\n * @param amount - Distance to move in pixels\n */\n public moveY(amount: number): void {\n if (this.isStatic) {\n return;\n }\n\n this._yRemainder += amount;\n const move = Math.round(this._yRemainder);\n\n if (move !== 0) {\n this._yRemainder -= move;\n const sign = Math.sign(move);\n\n let remaining = Math.abs(move);\n while (remaining > 0) {\n const step = sign;\n const nextY = this.y + step;\n\n // Check for collision with any solid\n let collided = false;\n // Only check collisions when moving in the direction of gravity\n if (Math.sign(this.system.gravity) === sign) {\n for (const solid of this.getSolidsAt(this.x, nextY)) {\n if (solid.canCollide) {\n collided = true;\n break;\n }\n }\n }\n\n if (!collided) {\n this._y = nextY;\n remaining--;\n this.updateView();\n this.checkActorOverlaps();\n } else {\n // Stop vertical movement when landing on a solid\n this.velocity.y = 0;\n break;\n }\n }\n }\n }\n\n /**\n * Updates the sensor's position and checks for overlapping actors.\n *\n * @param deltaTime - Delta time in seconds\n */\n public update(deltaTime: number): void {\n // Reset the cache at the start of each update\n this._isRidingSolidCache = null;\n\n if (!this.active) {\n return;\n }\n\n // Only apply gravity if not static and not riding a solid\n if (!this.isStatic && !this.isRidingSolid()) {\n this.velocity.y += this.system.gravity * deltaTime;\n }\n\n // Move\n if (this.velocity.x !== 0) {\n this.moveX(this.velocity.x * deltaTime);\n }\n if (this.velocity.y !== 0) {\n this.moveY(this.velocity.y * deltaTime);\n }\n }\n\n /**\n * Checks for overlapping actors and triggers callbacks.\n *\n * @returns Set of current overlaps\n */\n public checkActorOverlaps(): Set<SensorOverlap> {\n // Skip if sensor has no collision mask or is inactive\n if (this.collisionMask === 0 || !this.active) {\n return new Set();\n }\n\n this._currentSensorOverlaps.clear();\n this._currentOverlaps.clear();\n\n // Get all actors at current position - use spatial filtering first\n // Only get actors that are potentially in the same grid cells\n\n // Cache collision layer and mask for faster access\n const sensorLayer = this.collisionLayer;\n const sensorMask = this.collisionMask;\n\n // Get actors by type, but only process those that could be nearby\n const nearbyActors = this.system.getActorsByType(this.collidableTypes);\n\n for (const actor of nearbyActors) {\n // Skip inactive actors\n if (!actor.active) continue;\n\n // Fast collision layer check\n if ((sensorLayer & actor.collisionMask) === 0 || (actor.collisionLayer & sensorMask) === 0) {\n continue;\n }\n\n // Fast AABB check before detailed overlap\n if (this.system.aabbOverlap(this, actor)) {\n this._currentOverlaps.add(actor);\n if (!this.overlappingActors.has(actor)) {\n // New overlap\n this._currentSensorOverlaps.add({\n actor,\n sensor: this,\n type: `${actor.type}|${this.type}`,\n });\n\n this.onActorEnter(actor);\n }\n }\n }\n\n // Check for actors that are no longer overlapping\n for (const actor of this.overlappingActors) {\n if (!this._currentOverlaps.has(actor)) {\n this.onActorExit(actor);\n }\n }\n\n // Swap the sets to avoid unnecessary clear and forEach operations\n const temp = this.overlappingActors;\n this.overlappingActors = this._currentOverlaps;\n this._currentOverlaps = temp;\n this._currentOverlaps.clear();\n\n return this._currentSensorOverlaps;\n }\n\n public reset(): void {\n super.reset();\n this._currentSensorOverlaps.clear();\n this._currentOverlaps.clear();\n this._isRidingSolidCache = null;\n this.velocity = { x: 0, y: 0 };\n this.overlappingActors = new Set();\n }\n\n /**\n * Called when an actor starts overlapping with this sensor.\n * Override this to handle overlap start events.\n *\n * @param actor - The actor that entered\n */\n public onActorEnter<A extends Actor = Actor>(actor: A): void {\n // Override in subclass\n void actor;\n }\n\n /**\n * Called when an actor stops overlapping with this sensor.\n * Override this to handle overlap end events.\n *\n * @param actor - The actor that exited\n */\n public onActorExit<A extends Actor = Actor>(actor: A): void {\n // Override in subclass\n void actor;\n }\n\n /**\n * Gets all solids at the specified position.\n *\n * @param x - X position to check\n * @param y - Y position to check\n * @returns Array of solids at the position\n */\n protected getSolidsAt(x: number, y: number): Solid[] {\n return this.system.getSolidsAt(x, y, this);\n }\n}\n","import { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Sensor } from './Sensor';\nimport { EntityData, PhysicsEntityConfig, PhysicsEntityType } from './types';\n\n/**\n * Static or moving solid object that other entities can collide with.\n * Solids are typically used for platforms, walls, and other obstacles.\n *\n * Features:\n * - Static collision boundaries\n * - Support for moving platforms\n * - Carries actors and sensors that are riding it\n * - Pushes overlapping entities out of the way\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a static platform\n * const platform = physics.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32],\n * view: platformSprite\n * });\n *\n * // Create a moving platform\n * const movingPlatform = physics.createSolid({\n * type: 'Platform',\n * position: [100, 300],\n * size: [200, 32],\n * view: platformSprite\n * });\n *\n * // Move the platform back and forth\n * gsap.to(movingPlatform, {\n * x: 500,\n * duration: 2,\n * yoyo: true,\n * repeat: -1\n * });\n * ```\n */\nexport class Solid<D extends EntityData = EntityData> extends Entity<D> {\n public readonly entityType: PhysicsEntityType = 'Solid';\n /** Whether this solid should be removed when culled (typically false) */\n public shouldRemoveOnCull = false;\n\n /** Whether this solid can be collided with */\n private _canCollide: boolean = true;\n\n get canCollide(): boolean {\n return this._canCollide;\n }\n\n /** Whether this solid has collisions enabled */\n public collideable: boolean = true;\n\n /** Whether this solid is currently moving */\n public moving: boolean = false;\n\n private _nextX: number;\n private _nextY: number;\n\n public setPosition(x: number, y: number): void {\n this.x = x;\n this.y = y;\n }\n\n /**\n * Sets the solid's X position.\n * For moving solids, this queues the movement to be applied on next update.\n */\n public set x(value: number) {\n this._nextX = Math.round(value);\n this.moving = true;\n }\n\n public get x(): number {\n return this._x;\n }\n\n /**\n * Sets the solid's Y position.\n * For moving solids, this queues the movement to be applied on next update.\n */\n public set y(value: number) {\n this._nextY = Math.round(value);\n this.moving = true;\n }\n\n public get y(): number {\n return this._y;\n }\n\n /**\n * Initializes or reinitializes the solid with new configuration.\n *\n * @param config - Configuration for the solid\n */\n public init(config: PhysicsEntityConfig<D>): void {\n super.init(config);\n if (config) {\n this._nextX = this._x;\n this._nextY = this._y;\n }\n }\n\n /** Right edge X coordinate */\n public get right(): number {\n return this.x + this.width;\n }\n\n /** Left edge X coordinate */\n public get left(): number {\n return this.x;\n }\n\n /** Top edge Y coordinate */\n public get top(): number {\n return this.y;\n }\n\n /** Bottom edge Y coordinate */\n public get bottom(): number {\n return this.y + this.height;\n }\n\n /**\n * Checks if this solid can collide with the given entity type.\n * This method is kept for backward compatibility.\n *\n * @returns Always returns true as we're now using only collision layers/masks\n */\n canCollideWith(): boolean {\n // First check if collisions are enabled at all\n if (!this.collideable) {\n return false;\n }\n\n // We're now using only collision layers/masks, so this method is simplified\n return true;\n }\n\n /**\n * Moves the solid by the specified amount, carrying any riding entities.\n * Also pushes any overlapping entities out of the way.\n *\n * @param x - X distance to move\n * @param y - Y distance to move\n * @param actors - Set of actors to check for riding/pushing\n * @param sensors - Set of sensors to check for riding/pushing\n */\n public move(\n x: number,\n y: number,\n actors: Set<Actor> = this.system.actors,\n sensors: Set<Sensor> = this.system.sensors,\n force: boolean = false,\n ): void {\n if (!this.active) {\n return;\n }\n\n // Calculate total movement including remainder\n const totalX = x + (this._nextX - this._x);\n const totalY = y + (this._nextY - this._y);\n\n this._xRemainder += totalX;\n this._yRemainder += totalY;\n\n const moveX = Math.round(this._xRemainder);\n const moveY = Math.round(this._yRemainder);\n\n if (moveX !== 0 || moveY !== 0 || force) {\n if (this.collideable) {\n // Get all riding actors and sensors before movement\n const ridingActors = new Set<Actor>();\n const ridingSensors = new Set<Sensor>();\n\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0 &&\n actor.isRiding(this)\n ) {\n ridingActors.add(actor);\n }\n }\n\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0 &&\n sensor.isRiding(this)\n ) {\n ridingSensors.add(sensor);\n }\n }\n\n // Make this solid non-collidable so actors don't get stuck\n this._canCollide = false;\n\n if (moveX !== 0) {\n this._xRemainder -= moveX;\n this._x += moveX;\n\n if (moveX > 0) {\n // Moving right\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push right\n actor.moveX(this.right - actor.x, actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry right\n actor.moveX(moveX);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push right\n sensor.moveX(this.right - sensor.x);\n } else if (ridingSensors.has(sensor)) {\n // Carry right\n sensor.moveX(moveX);\n }\n }\n } else {\n // Moving left\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push left\n actor.moveX(this.left - (actor.x + actor.width), actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry left\n actor.moveX(moveX);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push left\n sensor.moveX(this.left - (sensor.x + sensor.width));\n } else if (ridingSensors.has(sensor)) {\n // Carry left\n sensor.moveX(moveX);\n }\n }\n }\n }\n\n if (moveY !== 0) {\n this._yRemainder -= moveY;\n this._y += moveY;\n\n if (moveY > 0) {\n // Moving down\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push down\n actor.moveY(this.bottom - actor.y, actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry down\n actor.moveY(moveY);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push down\n sensor.moveY(this.bottom - sensor.y);\n } else if (ridingSensors.has(sensor)) {\n // Carry down\n sensor.moveY(moveY);\n }\n }\n } else {\n // Moving up\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push up\n actor.moveY(this.top - (actor.y + actor.height), actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry up\n actor.moveY(moveY);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push up\n sensor.moveY(this.top - (sensor.y + sensor.height));\n } else if (ridingSensors.has(sensor)) {\n // Carry up\n sensor.moveY(moveY);\n }\n }\n }\n }\n\n // Re-enable collisions\n this._canCollide = true;\n } else {\n // When collisions are disabled, just move without affecting other entities\n if (moveX !== 0) {\n this._xRemainder -= moveX;\n this._x += moveX;\n }\n if (moveY !== 0) {\n this._yRemainder -= moveY;\n this._y += moveY;\n }\n }\n\n // Update next positions to match current\n this._nextX = this._x;\n this._nextY = this._y;\n\n // Update the view position\n this.updateView();\n }\n }\n\n /**\n * Checks if this solid overlaps with the given entity.\n *\n * @param entity - Entity to check for overlap\n * @returns True if overlapping\n */\n private overlaps(entity: Actor | Sensor): boolean {\n // Check collision layers and masks\n // A collision occurs when (A.layer & B.mask) !== 0 && (B.layer & A.mask) !== 0\n if ((this.collisionLayer & entity.collisionMask) === 0 || (entity.collisionLayer & this.collisionMask) === 0) {\n return false;\n }\n\n // Check for AABB overlap\n return this.system.aabbOverlap(this, entity);\n }\n}\n","import { Container, Graphics } from 'pixi.js';\nimport { Actor } from './Actor';\nimport CrunchPhysicsPlugin from './CrunchPhysicsPlugin';\nimport { Entity } from './Entity';\nimport { Group } from './Group';\nimport { AABBLike } from './interfaces';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\nimport {\n ActorCollision,\n ActorCollisionResult,\n Collision,\n PhysicsEntityConfig,\n PhysicsEntityView,\n Rectangle,\n SensorOverlap,\n} from './types';\n\n/**\n * Configuration options for the Crunch physics system.\n * These options control the core behavior of the physics simulation.\n *\n * @example\n * ```typescript\n * const options: PhysicsSystemOptions = {\n * gridSize: 32,\n * gravity: 980,\n * maxVelocity: 1000,\n * debug: true,\n * boundary: { x: 0, y: 0, width: 800, height: 600 },\n * culling: true,\n * collisionResolver: (collisions) => {\n * for (const collision of collisions) {\n * handleCollision(collision);\n * }\n * }\n * };\n * ```\n */\nexport interface PhysicsSystemOptions {\n /** Reference to the parent Crunch physics plugin */\n plugin: CrunchPhysicsPlugin;\n /** Size of each grid cell for spatial partitioning (in pixels) */\n gridSize: number;\n /** Gravity strength in pixels per second squared */\n gravity: number;\n /** Maximum velocity for any entity in pixels per second */\n maxVelocity: number;\n /** Whether to render debug visualizations */\n debug?: boolean;\n /** World boundary for culling entities */\n boundary?: Rectangle;\n /** Whether to automatically cull out-of-bounds entities */\n culling?: boolean;\n /** Custom handler for resolving collisions */\n collisionResolver?: (collisions: Collision[]) => void;\n /** Custom handler for resolving sensor overlaps */\n overlapResolver?: (overlaps: SensorOverlap[]) => void;\n /** Custom handler for resolving actor-to-actor collisions */\n actorCollisionResolver?: (collisions: ActorCollision[]) => void;\n /** Whether to enable actor-to-actor collisions */\n enableActorCollisions?: boolean;\n /** Default collision layer for new entities */\n defaultCollisionLayer?: number;\n /** Default collision mask for new entities */\n defaultCollisionMask?: number;\n}\n\n/**\n * Represents a cell in the spatial partitioning grid that can contain both solids and actors\n */\ninterface GridCell {\n solids: Set<Solid>;\n actors: Set<Actor>;\n}\n\n/**\n * Core physics system that manages all physics entities and their interactions.\n * Handles spatial partitioning, collision detection, and entity lifecycle.\n *\n * Features:\n * - Grid-based spatial partitioning for efficient collision checks\n * - Entity management (actors, solids, sensors, groups)\n * - Collision and overlap detection\n * - Debug visualization\n * - Culling system for out-of-bounds entities\n *\n * @example\n * ```typescript\n * // Create the physics system\n * const system = new System({\n * gridSize: 32,\n * gravity: 980,\n * maxVelocity: 1000\n * });\n *\n * // Add entities\n * const player = system.createActor({\n * type: 'Player',\n * position: [100, 100]\n * });\n *\n * const platform = system.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32]\n * });\n *\n * // Update physics (in game loop)\n * system.update(deltaTime);\n * ```\n */\nexport class System {\n private readonly options: PhysicsSystemOptions;\n // Public collections\n public entities: Set<Entity> = new Set();\n private _flaggedEntities: Set<Entity> = new Set();\n // Type-based lookup maps\n public actors: Set<Actor> = new Set();\n public solids: Set<Solid> = new Set();\n public sensors: Set<Sensor> = new Set();\n public groups: Set<Group> = new Set();\n // Type-based lookup maps\n private actorsByType: Map<string, Set<Actor>> = new Map();\n private solidsByType: Map<string, Set<Solid>> = new Map();\n private sensorsByType: Map<string, Set<Sensor>> = new Map();\n private groupsByType: Map<string, Set<Group>> = new Map();\n\n // Followers tracking\n private followers: Map<Entity, Set<Entity>> = new Map();\n\n // groups tracking\n private groupWithEntities: Map<Group, Set<Entity>> = new Map();\n\n // Single grid for both solids and actors\n private grid: Map<string, GridCell> = new Map();\n\n // Collision tracking\n private collisions: Collision[] = [];\n private sensorOverlaps: SensorOverlap[] = [];\n private actorCollisions: ActorCollision[] = [];\n\n // Reusable data structures for actor collision detection\n private _checkedPairs: Set<string> = new Set();\n\n // Object pools for collision results to reduce GC pressure\n private _collisionResultPool: ActorCollisionResult[] = [];\n private _collisionResultPoolIndex: number = 0;\n private _actorCollisionPool: ActorCollision[] = [];\n private _actorCollisionPoolIndex: number = 0;\n\n // Debugging\n private _debugContainer: Container;\n private _debugGfx: Graphics | null = null;\n private _debug: boolean = false;\n\n private _movedActors: Set<Actor> = new Set();\n private _activeGridCells: Set<string> = new Set();\n private _potentialCollisions: Map<string, [Actor, Actor]> = new Map();\n\n set debug(value: boolean) {\n this._debug = value;\n\n if (this._debug) {\n if (!this._debugContainer) {\n this._debugContainer = this.options.plugin.container.addChild(new Container());\n }\n if (!this._debugGfx) {\n this._debugGfx = new Graphics();\n }\n this._debugContainer.addChild(this._debugGfx);\n } else {\n this._debugGfx?.clear();\n this._debugContainer?.removeChildren();\n }\n }\n\n set gridSize(value: number) {\n this.options.gridSize = value;\n this.grid.clear();\n\n for (const solid of this.solids) {\n this.addSolidToGrid(solid);\n }\n }\n\n set gravity(value: number) {\n this.options.gravity = value;\n }\n\n get gravity(): number {\n return this.options.gravity;\n }\n\n set maxVelocity(value: number) {\n this.options.maxVelocity = value;\n }\n\n get maxVelocity(): number {\n return this.options.maxVelocity;\n }\n\n set boundary(value: Rectangle) {\n this.options.boundary = value;\n }\n\n get boundary(): Rectangle {\n return this.options.boundary!;\n }\n\n get container(): Container {\n return this.options.plugin.container;\n }\n\n public addView(view: PhysicsEntityView): void {\n this.container.addChild(view);\n }\n\n constructor(options: PhysicsSystemOptions) {\n this.options = {\n ...options,\n culling: options.culling ?? false,\n };\n this.debug = options.debug ?? false;\n this._debugContainer = new Container();\n options.plugin.container.addChild(this._debugContainer);\n }\n\n private _resetPositionFlags(entity: Entity): void {\n entity.updatedFollowPosition = false;\n entity.updatedGroupPosition = false;\n }\n\n public update(dt: number): void {\n if (!this.options.plugin.enabled) return;\n\n // Clear collections at the start with pre-allocated capacity\n this.collisions.length = 0;\n this.sensorOverlaps.length = 0;\n this.actorCollisions.length = 0;\n\n // Reset object pools\n this.resetCollisionPools();\n\n // Convert delta time to seconds (cache this calculation)\n const deltaTime = dt / 60;\n\n // Update containers first (groups)\n for (const group of this.groups) {\n group.update(deltaTime);\n }\n\n // Update solids with optimized grid management\n this.updateSolids(deltaTime);\n\n // Update sensors (before actors so they can detect entry/exit in the same frame)\n for (const sensor of this.sensors) {\n this.updateSensor(sensor, deltaTime);\n }\n\n // Clear actor collision lists before updating actors\n for (const actor of this.actors) {\n if (actor.actorCollisions.length > 0) {\n actor.actorCollisions.length = 0;\n }\n }\n\n // Update actors with potential batching\n this.updateActors(deltaTime);\n\n // Process position updates for followers and group entities\n this.updateEntityPositions();\n\n // Process overlaps, collisions, and culling in batch operations\n this.processCollisionsAndOverlaps();\n\n // Cull out-of-bounds entities if enabled and boundary is set\n if (this.options.culling && this.options.boundary) {\n this.cullOutOfBounds();\n }\n\n // Update debug rendering if enabled\n if (this._debug) {\n this.debugRender();\n }\n }\n\n // New optimized methods to break up the update loop\n private updateSolids(deltaTime: number): void {\n // Fast path if no solids are moving\n let hasMovingSolids = false;\n\n for (const solid of this.solids) {\n solid.preUpdate();\n solid.update(deltaTime);\n solid.postUpdate();\n\n if (solid.moving) {\n hasMovingSolids = true;\n }\n }\n\n // Skip grid updates if nothing is moving\n if (!hasMovingSolids) return;\n\n // Batch grid updates for moving solids\n const movedSolids = new Set<Solid>();\n\n for (const solid of this.solids) {\n if (solid.moving) {\n // Remove from old grid cells\n this.removeSolidFromGrid(solid);\n // Move the solid (which will handle pushing/carrying actors and sensors)\n solid.move(0, 0, this.actors, this.sensors, true);\n // Track for batch grid update\n movedSolids.add(solid);\n }\n }\n\n // Batch add to grid after all movements are complete\n for (const solid of movedSolids) {\n this.addSolidToGrid(solid);\n }\n }\n\n private updateActors(deltaTime: number): void {\n // Skip if no active actors\n if (this.actors.size === 0) return;\n\n // Use for...of instead of forEach for better performance\n for (const actor of this.actors) {\n if (actor.active) {\n actor.preUpdate();\n actor.update(deltaTime);\n actor.postUpdate();\n\n // Collect collisions in batch\n const actorCollisions = actor.collisions;\n if (actorCollisions.length > 0) {\n for (const result of actorCollisions) {\n this.collisions.push({\n type: `${actor.type}|${result.solid!.type}`,\n entity1: actor,\n entity2: result.solid!,\n result: {\n collided: result.collided,\n normal: result.normal,\n penetration: result.penetration,\n solid: result.solid,\n },\n });\n }\n }\n }\n }\n }\n\n private updateEntityPositions(): void {\n this._flaggedEntities.clear();\n\n // Process followers first (most dependent)\n for (const [entity, followers] of this.followers) {\n const followX = entity.x;\n const followY = entity.y;\n\n for (const follower of followers) {\n if (!follower.updatedFollowPosition) {\n // Cache group checks to avoid repeated property access\n const hasGroup = follower.group !== null;\n const sameGroup = hasGroup && follower.group === entity.group;\n\n if (sameGroup) {\n // Get the entity's position relative to its group using the offset\n const entityGroupOffset = entity.groupOffset;\n const groupX = follower.group!.x;\n const groupY = follower.group!.y;\n\n // Set position using combined offsets relative to group\n follower.setPosition(\n groupX + entityGroupOffset.x + follower.followOffset.x,\n groupY + entityGroupOffset.y + follower.followOffset.y,\n );\n } else if (hasGroup) {\n // Different groups or followed entity not in a group\n follower.setPosition(\n followX + follower.followOffset.x + follower.group!.x + follower.groupOffset.x,\n followY + follower.followOffset.y + follower.group!.y + follower.groupOffset.y,\n );\n } else {\n // No group, just following\n follower.setPosition(followX + follower.followOffset.x, followY + follower.followOffset.y);\n }\n\n follower.updatedFollowPosition = true;\n this._flaggedEntities.add(follower);\n\n if (hasGroup) {\n follower.updatedGroupPosition = true;\n }\n }\n }\n }\n\n // Process remaining group entities\n for (const [group, groupEntities] of this.groupWithEntities) {\n const groupX = group.x;\n const groupY = group.y;\n\n for (const entity of groupEntities) {\n if (!entity.updatedGroupPosition && !entity.updatedFollowPosition) {\n entity.setPosition(groupX + entity.groupOffset.x, groupY + entity.groupOffset.y);\n entity.updatedGroupPosition = true;\n this._flaggedEntities.add(entity);\n }\n }\n }\n\n // Reset flags in batch\n this._flaggedEntities.forEach(this._resetPositionFlags);\n }\n\n private processCollisionsAndOverlaps(): void {\n // Process overlaps if resolver is set and we have overlaps\n if (this.options.overlapResolver && this.sensorOverlaps.length > 0) {\n this.options.overlapResolver(this.sensorOverlaps);\n }\n\n // Check actor-to-actor collisions if enabled and we have multiple actors\n if (this.options.enableActorCollisions && this.actors.size > 1) {\n this.checkActorCollisions();\n }\n\n // Process collisions if resolver is set and we have collisions\n if (this.collisions.length > 0 && this.options.collisionResolver) {\n this.options.collisionResolver(this.collisions);\n }\n\n // Process actor-to-actor collisions if resolver is set and we have collisions\n if (this.actorCollisions.length > 0 && this.options.actorCollisionResolver) {\n this.options.actorCollisionResolver(this.actorCollisions);\n }\n }\n\n // Optimize the cullOutOfBounds method\n private cullOutOfBounds(): void {\n // Skip if no boundary\n if (!this.options.boundary) return;\n\n const boundary = this.options.boundary;\n // Pre-allocate removal arrays with reasonable capacity\n const toRemoveActors: Actor[] = [];\n const toRemoveSolids: Solid[] = [];\n const toRemoveSensors: Sensor[] = [];\n const toRemoveGroups: Group[] = [];\n\n // Cache boundary values to avoid repeated property access\n const boundX = boundary.x;\n const boundY = boundary.y;\n const boundRight = boundX + boundary.width;\n const boundBottom = boundY + boundary.height;\n\n // Check actors with optimized bounds check\n for (const actor of this.actors) {\n const actorRight = actor.x + actor.width;\n const actorBottom = actor.y + actor.height;\n\n const inBounds = !(\n actor.x >= boundRight || // Completely to the right\n actorRight <= boundX || // Completely to the left\n actor.y >= boundBottom || // Completely below\n actorBottom <= boundY // Completely above\n );\n\n if (!inBounds) {\n if (!actor.isCulled) {\n actor.onCull();\n }\n if (actor.shouldRemoveOnCull) {\n toRemoveActors.push(actor);\n }\n } else if (actor.isCulled) {\n // Uncull if back in bounds\n actor.onUncull();\n }\n }\n\n // Check solids with the same optimized bounds check\n for (const solid of this.solids) {\n const solidRight = solid.x + solid.width;\n const solidBottom = solid.y + solid.height;\n\n const inBounds = !(\n solid.x >= boundRight ||\n solidRight <= boundX ||\n solid.y >= boundBottom ||\n solidBottom <= boundY\n );\n\n if (!inBounds) {\n if (!solid.isCulled) {\n solid.onCull();\n }\n if (solid.shouldRemoveOnCull) {\n toRemoveSolids.push(solid);\n }\n } else if (solid.isCulled) {\n solid.onUncull();\n }\n }\n\n // Check sensors with the same optimized bounds check\n for (const sensor of this.sensors) {\n const sensorRight = sensor.x + sensor.width;\n const sensorBottom = sensor.y + sensor.height;\n\n const inBounds = !(\n sensor.x >= boundRight ||\n sensorRight <= boundX ||\n sensor.y >= boundBottom ||\n sensorBottom <= boundY\n );\n\n if (!inBounds) {\n if (!sensor.isCulled) {\n sensor.onCull();\n }\n if (sensor.shouldRemoveOnCull) {\n toRemoveSensors.push(sensor);\n }\n } else if (sensor.isCulled) {\n sensor.onUncull();\n }\n }\n\n // Check groups with the same optimized bounds check\n for (const group of this.groups) {\n const groupRight = group.x + group.width;\n const groupBottom = group.y + group.height;\n\n const inBounds = !(\n group.x >= boundRight ||\n groupRight <= boundX ||\n group.y >= boundBottom ||\n groupBottom <= boundY\n );\n\n if (!inBounds) {\n if (!group.isCulled) {\n group.onCull();\n }\n if (group.shouldRemoveOnCull) {\n toRemoveGroups.push(group);\n }\n } else if (group.isCulled) {\n group.onUncull();\n }\n }\n\n // Batch remove culled entities if any exist\n const actorCount = toRemoveActors.length;\n const solidCount = toRemoveSolids.length;\n const sensorCount = toRemoveSensors.length;\n const groupCount = toRemoveGroups.length;\n\n if (actorCount + solidCount + sensorCount + groupCount === 0) return;\n\n // Use for loops instead of forEach for better performance\n for (let i = 0; i < actorCount; i++) {\n this.removeActor(toRemoveActors[i]);\n }\n\n for (let i = 0; i < solidCount; i++) {\n this.removeSolid(toRemoveSolids[i]);\n }\n\n for (let i = 0; i < sensorCount; i++) {\n this.removeSensor(toRemoveSensors[i]);\n }\n\n for (let i = 0; i < groupCount; i++) {\n this.removeGroup(toRemoveGroups[i]);\n }\n }\n\n private updateSensor(sensor: Sensor, dt: number): void {\n sensor.preUpdate();\n sensor.update(dt);\n sensor.postUpdate();\n\n const overlaps = sensor.checkActorOverlaps();\n this.sensorOverlaps.push(...overlaps);\n }\n\n public createEntity(config: PhysicsEntityConfig): Actor | Solid | Sensor | Group {\n if (config.type === 'actor') {\n return this.createActor(config);\n } else if (config.type === 'solid') {\n return this.createSolid(config);\n } else if (config.type === 'sensor') {\n return this.createSensor(config);\n } else if (config.type === 'group') {\n return this.createGroup(config);\n }\n throw new Error(`Invalid entity type: ${config.type}`);\n }\n\n public addEntity(entity: Entity | Actor | Solid | Sensor | Group): Actor | Solid | Sensor | Group {\n if (!entity || !entity.entityType) {\n throw new Error('Entity is required');\n }\n if (entity.entityType === 'Actor') {\n return this.addActor(entity as Actor);\n } else if (entity.entityType === 'Solid') {\n return this.addSolid(entity as Solid);\n } else if (entity.entityType === 'Sensor') {\n return this.addSensor(entity as Sensor);\n } else if (entity.entityType === 'Group') {\n return this.addGroup(entity as Group);\n }\n throw new Error(`Invalid entity type: ${entity!.entityType}`);\n }\n\n public removeEntity(entity: Entity | Actor | Solid | Sensor | Group): void {\n if (entity.entityType === 'Actor') {\n this.removeActor(entity as Actor);\n } else if (entity.entityType === 'Solid') {\n this.removeSolid(entity as Solid);\n } else if (entity.entityType === 'Sensor') {\n this.removeSensor(entity as Sensor);\n } else if (entity.entityType === 'Group') {\n this.removeGroup(entity as Group);\n }\n }\n\n public createActor(config: PhysicsEntityConfig): Actor {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const actor = config.class ? (new config.class(config) as Actor) : new Actor(config);\n return this.addActor(actor);\n }\n\n public addActor(actor: Actor): Actor {\n this.entities.add(actor);\n this.actors.add(actor);\n\n // Add to type index\n if (!this.actorsByType.has(actor.type)) {\n this.actorsByType.set(actor.type, new Set());\n }\n this.actorsByType.get(actor.type)!.add(actor);\n\n // Add to actor grid if actor collisions are enabled\n if (this.options.enableActorCollisions) {\n actor.updateGridCells();\n }\n\n return actor;\n }\n\n public createSensor(config: PhysicsEntityConfig): Sensor {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const sensor = config.class ? (new config.class(config) as Sensor) : new Sensor(config);\n return this.addSensor(sensor);\n }\n\n public addSensor(sensor: Sensor): Sensor {\n this.entities.add(sensor);\n this.sensors.add(sensor);\n // Add to type index\n if (!this.sensorsByType.has(sensor.type)) {\n this.sensorsByType.set(sensor.type, new Set());\n }\n this.sensorsByType.get(sensor.type)!.add(sensor);\n return sensor;\n }\n\n public createSolid(config: PhysicsEntityConfig): Solid {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const solid = config.class ? (new config.class(config) as Solid) : new Solid(config);\n return this.addSolid(solid);\n }\n\n public addSolid(solid: Solid): Solid {\n this.entities.add(solid);\n this.solids.add(solid);\n // Add to type index\n if (!this.solidsByType.has(solid.type)) {\n this.solidsByType.set(solid.type, new Set());\n }\n this.solidsByType.get(solid.type)!.add(solid);\n // Add to spatial grid\n this.addSolidToGrid(solid);\n\n return solid;\n }\n\n public removeActor(actor: Actor, destroyView: boolean = true): void {\n this.entities.delete(actor);\n this.actors.delete(actor);\n // Remove from type index\n const typeSet = this.actorsByType.get(actor.type);\n if (typeSet) {\n typeSet.delete(actor);\n if (typeSet.size === 0) {\n this.actorsByType.delete(actor.type);\n }\n }\n actor.onRemoved();\n\n if (destroyView) {\n actor.view?.removeFromParent();\n }\n\n // Remove from actor grid\n if (this.options.enableActorCollisions) {\n this.removeActorFromGrid(actor);\n }\n }\n\n public removeSolid(solid: Solid, destroyView: boolean = true): void {\n this.entities.delete(solid);\n this.solids.delete(solid);\n // Remove from type index\n const typeSet = this.solidsByType.get(solid.type);\n if (typeSet) {\n typeSet.delete(solid);\n if (typeSet.size === 0) {\n this.solidsByType.delete(solid.type);\n }\n }\n this.removeSolidFromGrid(solid);\n\n if (destroyView) {\n solid.view?.removeFromParent();\n }\n\n solid.onRemoved();\n }\n\n public removeSensor(sensor: Sensor, destroyView: boolean = true): void {\n this.entities.delete(sensor);\n this.sensors.delete(sensor);\n // Remove from type index\n const typeSet = this.sensorsByType.get(sensor.type);\n if (typeSet) {\n typeSet.delete(sensor);\n if (typeSet.size === 0) {\n this.sensorsByType.delete(sensor.type);\n }\n }\n\n sensor.onRemoved();\n\n if (destroyView) {\n sensor.view?.removeFromParent();\n }\n }\n\n public moveSolid(solid: Solid, x: number, y: number): void {\n // Remove from old grid cells\n this.removeSolidFromGrid(solid);\n\n // Move the solid (which will handle pushing/carrying actors)\n solid.move(x, y, this.actors, this.sensors);\n\n // Add to new grid cells\n this.addSolidToGrid(solid);\n\n solid.updateView();\n }\n\n public getSolidsAt(x: number, y: number, entity: Actor | Sensor): Solid[] {\n // Early return if entity has no collision mask\n if (entity.collisionMask === 0) return [];\n\n const bounds = {\n x,\n y,\n width: entity.width,\n height: entity.height,\n };\n\n // Calculate movement direction from current position to check position\n const dx = x - entity.x;\n const dy = y - entity.y;\n\n // Get the base cells that the bounds intersect\n const cells = this.getCells(bounds);\n\n // Add one extra cell in the direction of movement\n if (dx !== 0) {\n const extraX =\n dx > 0 ? Math.ceil((x + bounds.width) / this.options.gridSize) : Math.floor(x / this.options.gridSize) - 1;\n\n for (\n let y = Math.floor(bounds.y / this.options.gridSize);\n y < Math.ceil((bounds.y + bounds.height) / this.options.gridSize);\n y++\n ) {\n cells.push(`${extraX},${y}`);\n }\n }\n\n if (dy !== 0) {\n const extraY =\n dy > 0 ? Math.ceil((y + bounds.height) / this.options.gridSize) : Math.floor(y / this.options.gridSize) - 1;\n\n for (\n let x = Math.floor(bounds.x / this.options.gridSize);\n x < Math.ceil((bounds.x + bounds.width) / this.options.gridSize);\n x++\n ) {\n cells.push(`${x},${extraY}`);\n }\n }\n\n // Create a Set of excluded types for O(1) lookups\n // Pre-allocate result array with a reasonable size to avoid resizing\n const result: Solid[] = [];\n const seen = new Set<Solid>();\n\n // Cache entity collision layer and mask for faster access\n const entityLayer = entity.collisionLayer;\n const entityMask = entity.collisionMask;\n\n for (const cell of cells) {\n const gridCell = this.grid.get(cell);\n if (gridCell) {\n for (const solid of gridCell.solids) {\n // Skip already seen solids\n if (seen.has(solid)) continue;\n\n // Fast collision layer check\n if ((entityLayer & solid.collisionMask) === 0 || (solid.collisionLayer & entityMask) === 0) continue;\n\n seen.add(solid);\n\n // Only add solids that actually overlap with the entity's bounds\n if (\n this.overlaps(bounds, {\n x: solid.x,\n y: solid.y,\n width: solid.width,\n height: solid.height,\n })\n ) {\n result.push(solid);\n }\n }\n }\n }\n\n return result;\n }\n\n public addFollower(entity: Entity, follower: Entity): void {\n // Logger.log('adding follower', entity.type, follower.type);\n if (!this.followers.has(entity)) {\n this.followers.set(entity, new Set());\n }\n this.followers.get(entity)!.add(follower);\n }\n\n public removeFollower(entity: Entity): void {\n // remove the entity from any set in the followers map\n for (const followers of this.followers.values()) {\n followers.delete(entity);\n }\n }\n\n public getFollowersOf(entity: Entity): Entity[] {\n return Array.from(this.followers.get(entity) || []);\n }\n\n public removeFollowersOf(entity: Entity): void {\n const set = this.followers.get(entity);\n if (set) {\n set.forEach((follower) => {\n follower.destroy();\n });\n set.clear();\n this.followers.delete(entity);\n }\n }\n\n public addToGroup(group: Group, entity: Entity): Entity {\n if (!this.groupWithEntities.has(group)) {\n this.groupWithEntities.set(group, new Set());\n }\n this.groupWithEntities.get(group)!.add(entity);\n return entity;\n }\n\n public removeFromGroup(entity: Entity): Entity {\n for (const group of this.groupWithEntities.values()) {\n group.delete(entity);\n }\n return entity;\n }\n\n public getEntitiesInGroup(group: Group): Entity[] {\n return Array.from(this.groupWithEntities.get(group) || []);\n }\n\n public removeEntitiesOfGroup(group: Group): Entity[] {\n const entities = Array.from(this.groupWithEntities.get(group) || []) || [];\n entities.forEach((entity) => {\n entity.setGroup(null);\n });\n this.groupWithEntities.delete(group);\n return entities;\n }\n\n private overlaps(a: Rectangle, b: Rectangle): boolean {\n return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;\n }\n\n private getCells(bounds: Rectangle): string[] {\n const cells: string[] = [];\n const { gridSize } = this.options;\n\n // Calculate the exact grid cells that the bounds intersect with\n const startX = Math.floor(bounds.x / gridSize);\n const startY = Math.floor(bounds.y / gridSize);\n const endX = Math.ceil((bounds.x + bounds.width) / gridSize);\n const endY = Math.ceil((bounds.y + bounds.height) / gridSize);\n\n // Pre-allocate array with exact size to avoid resizing\n cells.length = (endX - startX) * (endY - startY);\n\n // Use a single index counter to avoid array.push() operations\n let index = 0;\n\n // Only check one additional cell in the direction of movement for actors\n // For solids or static bounds, just use the exact cells\n for (let x = startX; x < endX; x++) {\n for (let y = startY; y < endY; y++) {\n // Use template string only once per iteration\n cells[index++] = `${x},${y}`;\n }\n }\n\n return cells;\n }\n\n private addSolidToGrid(solid: Solid): void {\n const bounds = {\n x: solid.x,\n y: solid.y,\n width: solid.width,\n height: solid.height,\n };\n const cells = this.getCells(bounds);\n\n for (const cell of cells) {\n if (!this.grid.has(cell)) {\n this.grid.set(cell, { solids: new Set(), actors: new Set() });\n }\n this.grid.get(cell)!.solids.add(solid);\n }\n }\n\n private removeSolidFromGrid(solid: Solid): void {\n const bounds = {\n x: solid.x,\n y: solid.y,\n width: solid.width,\n height: solid.height,\n };\n const cells = this.getCells(bounds);\n\n for (const cell of cells) {\n const gridCell = this.grid.get(cell);\n if (gridCell) {\n gridCell.solids.delete(solid);\n if (gridCell.solids.size === 0 && gridCell.actors.size === 0) {\n this.grid.delete(cell);\n }\n }\n }\n }\n\n public debugRender(): void {\n if (!this._debugGfx) {\n return;\n }\n const gfx = this._debugGfx!;\n gfx.clear();\n\n // Draw boundary if set\n if (this.options.boundary) {\n const b = this.options.boundary;\n gfx.rect(b.x, b.y, b.width, b.height);\n gfx.stroke({ color: 0xff0000, width: 2, alignment: 0.5 });\n }\n\n // Draw grid\n for (const cell of this.grid.keys()) {\n const [x, y] = cell.split(',').map(Number);\n gfx.rect(x * this.options.gridSize, y * this.options.gridSize, this.options.gridSize, this.options.gridSize);\n }\n gfx.stroke({ color: 0x00ff00, width: 1, join: 'miter', cap: 'butt' });\n\n // Draw solids\n for (const solid of this.solids) {\n gfx.rect(solid.x, solid.y, solid.width, solid.height);\n gfx.stroke({ color: solid.debugColor ?? 0x00ff00, alpha: 1 });\n }\n\n // Draw actors\n for (const actor of this.actors) {\n gfx.rect(actor.x, actor.y, actor.width, actor.height);\n gfx.stroke({ color: actor.debugColor ?? 0xff0000, alpha: 1 });\n }\n\n // Draw sensors\n for (const sensor of this.sensors) {\n gfx.rect(sensor.x, sensor.y, sensor.width, sensor.height);\n gfx.stroke({ color: sensor.debugColor ?? 0xffff00, alpha: 1 });\n }\n }\n\n /**\n * Get all entities of a specific type\n * @param type The type to look for\n * @returns Array of entities matching the type\n */\n public getByType(type: string): (Actor | Solid)[] {\n const actors = this.actorsByType.get(type) || new Set<Actor>();\n const solids = this.solidsByType.get(type) || new Set<Solid>();\n return [...actors, ...solids];\n }\n\n /**\n * Get all actors of a specific type\n * @param type The type to look for\n * @returns Array of actors matching the type\n */\n public getActorsByType(type: string | string[]): Actor[] {\n if (Array.isArray(type)) {\n return type.flatMap((t) => Array.from(this.actorsByType.get(t) || new Set()));\n }\n return Array.from(this.actorsByType.get(type) || new Set());\n }\n /**\n * Get all solids of a specific type\n * @param type The type to look for\n * @returns Array of solids matching the type\n */\n public getSolidsByType(type: string | string[]): Solid[] {\n if (Array.isArray(type)) {\n return type.flatMap((t) => Array.from(this.solidsByType.get(t) || new Set()));\n }\n return Array.from(this.solidsByType.get(type) || new Set());\n }\n /**\n * Get all sensors of a specific type\n * @param type The type to look for\n * @returns Array of sensors matching the type\n */\n public getSensorsByType(type: string | string[]): Sensor[] {\n if (Array.isArray(type)) {\n return type.flatMap((t) => Array.from(this.sensorsByType.get(t) || new Set()));\n }\n return Array.from(this.sensorsByType.get(type) || new Set());\n }\n\n public clearGrid(): void {\n this.grid.clear();\n }\n\n public clearAll(destroy: boolean = true) {\n this.grid.clear();\n this.entities.clear();\n\n if (destroy) {\n this.solids.forEach((solid) => {\n solid.destroy();\n });\n this.actors.forEach((actor) => {\n actor.destroy();\n });\n this.sensors.forEach((sensor) => sensor.destroy());\n }\n\n this.solids.clear();\n this.actors.clear();\n this.sensors.clear();\n\n this.solidsByType.clear();\n this.actorsByType.clear();\n this.sensorsByType.clear();\n }\n\n public destroy(): void {\n this.debug = false;\n this.gravity = 0;\n this.maxVelocity = 0;\n\n this.clearAll();\n }\n\n /**\n * Gets a collision result object from the pool or creates a new one if needed\n */\n private getCollisionResult(): ActorCollisionResult {\n if (this._collisionResultPoolIndex < this._collisionResultPool.length) {\n const result = this._collisionResultPool[this._collisionResultPoolIndex++];\n result.collided = false;\n result.actor = null as any;\n result.normal = undefined;\n result.penetration = 0;\n return result;\n }\n\n // Create a new object and add it to the pool\n const newResult: ActorCollisionResult = {\n collided: false,\n actor: null as any,\n normal: undefined,\n penetration: 0,\n };\n\n this._collisionResultPool.push(newResult);\n this._collisionResultPoolIndex++;\n return newResult;\n }\n\n /**\n * Gets an actor collision object from the pool or creates a new one if needed\n */\n private getActorCollision(): ActorCollision {\n if (this._actorCollisionPoolIndex < this._actorCollisionPool.length) {\n return this._actorCollisionPool[this._actorCollisionPoolIndex++];\n }\n\n // Create a new object and add it to the pool\n const newCollision: ActorCollision = {\n type: 'unknown|unknown' as `${string}|${string}`,\n actor1: null as any,\n actor2: null as any,\n result: null as any,\n };\n\n this._actorCollisionPool.push(newCollision);\n this._actorCollisionPoolIndex++;\n return newCollision;\n }\n\n /**\n * Resets the collision result pools for the next frame\n */\n private resetCollisionPools(): void {\n this._collisionResultPoolIndex = 0;\n this._actorCollisionPoolIndex = 0;\n }\n\n /**\n * Updates an actor's position in the grid.\n * This is called when an actor moves or when its size changes.\n *\n * @param actor - The actor to update in the grid\n */\n public updateActorInGrid(actor: Actor): void {\n // Skip if actor collisions are disabled\n if (!this.options.enableActorCollisions) return;\n\n // Skip if actor can't collide\n if (!actor.active || actor.collisionLayer === 0 || actor.collisionMask === 0) {\n // If actor has cells, remove it from them\n if (actor.currentGridCells.length > 0) {\n this.removeActorFromGrid(actor);\n }\n return;\n }\n\n // First remove from current cells\n this.removeActorFromGrid(actor);\n\n // Calculate new cells\n const bounds = {\n x: actor.x,\n y: actor.y,\n width: actor.width,\n height: actor.height,\n };\n\n const cells = this.getCells(bounds);\n\n // Store new cells in actor\n actor.currentGridCells = cells;\n\n // Add to new cells\n for (const cell of cells) {\n if (!this.grid.has(cell)) {\n this.grid.set(cell, { solids: new Set(), actors: new Set() });\n }\n\n const gridCell = this.grid.get(cell)!;\n gridCell.actors.add(actor);\n }\n }\n\n /**\n * Removes an actor from its current grid cells.\n *\n * @param actor - The actor to remove from the grid\n */\n public removeActorFromGrid(actor: Actor): void {\n const currentCells = actor.currentGridCells;\n\n for (const cell of currentCells) {\n const gridCell = this.grid.get(cell);\n\n if (gridCell) {\n gridCell.actors.delete(actor);\n\n // Remove the cell if it's empty\n if (gridCell.actors.size === 0 && gridCell.solids.size === 0) {\n this.grid.delete(cell);\n }\n }\n }\n\n // Clear the actor's cells\n actor.currentGridCells = [];\n }\n\n /**\n * Checks for collisions between all active actors.\n * This is an O(n²) operation, so it can be expensive with many actors.\n */\n private checkActorCollisions(): void {\n // Skip if there are no actors or only one actor\n if (this.actors.size <= 1 || !this.options.enableActorCollisions) return;\n\n // Clear the checked pairs set and potential collisions\n this._checkedPairs.clear();\n this._potentialCollisions.clear();\n this._activeGridCells.clear();\n\n // First pass: Mark cells with moving actors as active\n for (const [cellKey, gridCell] of this.grid.entries()) {\n for (const actor of gridCell.actors) {\n if (!actor.active) continue;\n\n // If actor is moving or recently moved\n if (actor.velocity.x !== 0 || actor.velocity.y !== 0) {\n this._activeGridCells.add(cellKey);\n break;\n }\n }\n }\n\n // Second pass: Generate potential collision pairs from active cells\n for (const cellKey of this._activeGridCells) {\n const gridCell = this.grid.get(cellKey);\n if (!gridCell || gridCell.actors.size <= 1) continue;\n\n const actorsInCell = Array.from(gridCell.actors);\n const cellActorCount = actorsInCell.length;\n\n // Check each actor against others in the same cell\n for (let i = 0; i < cellActorCount; i++) {\n const actor1 = actorsInCell[i];\n if (!actor1.active) continue;\n\n for (let j = i + 1; j < cellActorCount; j++) {\n const actor2 = actorsInCell[j];\n if (!actor2.active) continue;\n\n // Create a unique key for this actor pair\n const pairKey = actor1.id < actor2.id ? `${actor1.id}|${actor2.id}` : `${actor2.id}|${actor1.id}`;\n\n // Skip if we've already checked this pair\n if (this._checkedPairs.has(pairKey)) continue;\n this._checkedPairs.add(pairKey);\n\n // Fast collision layer check\n if (\n (actor1.collisionLayer & actor2.collisionMask) === 0 &&\n (actor2.collisionLayer & actor1.collisionMask) === 0\n )\n continue;\n\n // Fast AABB check\n if (\n actor1.x < actor2.x + actor2.width &&\n actor1.x + actor1.width > actor2.x &&\n actor1.y < actor2.y + actor2.height &&\n actor1.y + actor1.height > actor2.y\n ) {\n // Store potential collision pair\n this._potentialCollisions.set(pairKey, [actor1, actor2]);\n }\n }\n }\n }\n\n // Final pass: Detailed collision checks for potential pairs\n for (const [actor1, actor2] of this._potentialCollisions.values()) {\n // Check for collision\n const collisionResult = actor1.checkActorCollision(actor2);\n\n if (collisionResult.collided) {\n // Add to actor1's collision list\n actor1.actorCollisions.push(collisionResult);\n\n // Get a mirrored result from the pool\n const mirroredResult = this.getCollisionResult();\n mirroredResult.collided = true;\n mirroredResult.actor = actor1;\n\n if (collisionResult.normal) {\n if (!mirroredResult.normal) {\n mirroredResult.normal = { x: 0, y: 0 };\n }\n mirroredResult.normal.x = -collisionResult.normal.x;\n mirroredResult.normal.y = -collisionResult.normal.y;\n } else {\n mirroredResult.normal = undefined;\n }\n\n mirroredResult.penetration = collisionResult.penetration;\n\n // Add to actor2's collision list\n actor2.actorCollisions.push(mirroredResult);\n\n // Get an actor collision from the pool and add to system's collision list\n const actorCollision = this.getActorCollision();\n actorCollision.type = `${actor1.type}|${actor2.type}`;\n actorCollision.actor1 = actor1;\n actorCollision.actor2 = actor2;\n actorCollision.result = collisionResult;\n this.actorCollisions[this._actorCollisionPoolIndex++] = actorCollision;\n\n // Call collision handlers\n actor1.onActorCollide(collisionResult);\n actor2.onActorCollide(mirroredResult);\n\n // Resolve the collision\n actor1.resolveActorCollision(collisionResult);\n actor2.resolveActorCollision(mirroredResult);\n }\n }\n\n // Process collisions with resolver if provided\n if (this._actorCollisionPoolIndex > 0 && this.options.actorCollisionResolver) {\n this.options.actorCollisionResolver(this.actorCollisions.slice(0, this._actorCollisionPoolIndex));\n }\n }\n\n setCollisionResolver(resolver: (collisions: Collision[]) => void): void {\n this.options.collisionResolver = resolver;\n }\n\n /**\n * Sets a custom resolver for actor-to-actor collisions.\n *\n * @param resolver - Function to handle actor-to-actor collisions\n */\n public setActorCollisionResolver(resolver: (collisions: ActorCollision[]) => void): void {\n this.options.actorCollisionResolver = resolver;\n }\n\n /**\n * Returns whether actor-to-actor collision detection is enabled.\n *\n * @returns True if actor collisions are enabled\n */\n public get enableActorCollisions(): boolean {\n return !!this.options.enableActorCollisions;\n }\n\n /**\n * Enables or disables actor-to-actor collision detection.\n * When enabled, it initializes the grid for all existing actors.\n *\n * @param enabled - Whether to enable actor-to-actor collisions\n */\n public setActorCollisionsEnabled(enabled: boolean): void {\n const wasEnabled = this.options.enableActorCollisions;\n this.options.enableActorCollisions = enabled;\n\n // If newly enabled, add all actors to the grid\n if (enabled && !wasEnabled) {\n for (const actor of this.actors) {\n actor.updateGridCells();\n }\n } else if (!enabled && wasEnabled) {\n // If newly disabled, clear the actor grid\n this.grid.clear();\n\n // Clear all actors' grid cells\n for (const actor of this.actors) {\n actor.currentGridCells = [];\n }\n }\n }\n\n public createGroup(config: PhysicsEntityConfig): Group {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const group = config.class ? (new config.class(config) as unknown as Group) : new Group(config);\n return this.addGroup(group);\n }\n\n public addGroup(group: Group): Group {\n this.entities.add(group);\n this.groups.add(group);\n\n // Add to type index\n if (!this.groupsByType.has(group.type)) {\n this.groupsByType.set(group.type, new Set());\n }\n this.groupsByType.get(group.type)!.add(group);\n\n return group;\n }\n\n public removeGroup(group: Group, destroyView: boolean = true): void {\n // Early return if group doesn't exist\n if (!this.groups.has(group)) return;\n\n this.entities.delete(group);\n this.groups.delete(group);\n\n // Remove from type index\n const typeSet = this.groupsByType.get(group.type);\n if (typeSet) {\n typeSet.delete(group);\n if (typeSet.size === 0) {\n this.groupsByType.delete(group.type);\n }\n }\n\n // Get all entities in the group before removing them\n const groupEntities = this.groupWithEntities.get(group);\n if (groupEntities && groupEntities.size > 0) {\n // Create arrays for each entity type to batch process\n const actors: Actor[] = [];\n const solids: Solid[] = [];\n const sensors: Sensor[] = [];\n\n // Sort entities by type for batch processing\n for (const entity of groupEntities) {\n if (entity.entityType === 'Actor') {\n actors.push(entity as Actor);\n } else if (entity.entityType === 'Solid') {\n solids.push(entity as Solid);\n } else if (entity.entityType === 'Sensor') {\n sensors.push(entity as Sensor);\n }\n }\n\n // Batch remove entities by type\n for (const actor of actors) {\n this.removeActor(actor, destroyView);\n }\n\n for (const solid of solids) {\n this.removeSolid(solid, destroyView);\n }\n\n for (const sensor of sensors) {\n this.removeSensor(sensor, destroyView);\n }\n }\n\n // Clean up the group's entry in the tracking map\n this.groupWithEntities.delete(group);\n\n group.onRemoved();\n }\n\n /**\n * Get all groups of a specific type\n * @param type The type to look for\n * @returns Array of groups matching the type\n */\n public getGroupsByType(type: string | string[]): Group[] {\n if (Array.isArray(type)) {\n // Pre-calculate total size to avoid array resizing\n let totalSize = 0;\n for (const t of type) {\n const set = this.groupsByType.get(t);\n if (set) totalSize += set.size;\n }\n\n // Pre-allocate result array\n const result = new Array<Group>(totalSize);\n let index = 0;\n\n // Fill array without using flatMap (more efficient)\n for (const t of type) {\n const set = this.groupsByType.get(t);\n if (set) {\n for (const group of set) {\n result[index++] = group;\n }\n }\n }\n\n return result;\n }\n\n return Array.from(this.groupsByType.get(type) || new Set());\n }\n\n /**\n * Checks if two AABB rectangles overlap.\n *\n * @param a - The first AABB rectangle\n * @param b - The second AABB rectangle\n * @returns True if the rectangles overlap, false otherwise\n */\n public aabbOverlap(a: AABBLike, b: AABBLike): boolean {\n return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;\n }\n}\n","import { IApplication, IPlugin, isDev, Logger, Plugin, version } from '@caperjs/core';\nimport { Container as PIXIContainer, Rectangle, Ticker } from 'pixi.js';\nimport { Actor } from './Actor';\nimport { Group } from './Group';\nimport { AABBLike, CrunchPhysicsOptions } from './interfaces';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\nimport { System } from './System';\nimport {\n ActorCollision,\n Collision,\n CollisionLayer,\n CollisionLayers,\n PhysicsEntityConfig,\n RegisteredCollisionLayer,\n SensorOverlap,\n} from './types';\n\n/**\n * Interface for the Crunch physics plugin, providing a simple yet powerful 2D physics system\n * inspired by Towerfall's physics mechanics. Supports dynamic actors, static solids, and trigger sensors.\n *\n * @example\n * ```typescript\n * // Initialize the physics system\n * const physics = this.app.getPlugin('crunch') as ICrunchPhysicsPlugin;\n *\n * const physics = await this.physics.initialize({\n * gravity: 900,\n * debug: true,\n * gridSize: 32\n * });\n *\n * // Create a player character\n * const playerSprite = this.add.sprite({asset:Texture.WHITE, width: 32, height: 64});\n *\n * const player = physics.createActor({\n * type: 'Player',\n * position: [100, 100],\n * size: [32, 64],\n * view: playerSprite\n * });\n *\n * // Create a platform\n * const platformSprite = this.add.sprite({asset:Texture.WHITE, width: 800, height: 32, tint: 0x00FF00});\n * const platform = physics.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32],\n * view: platformSprite\n * });\n *\n * // Create a coin pickup sensor\n * const coinSprite = this.add.sprite({asset:Texture.WHITE, width: 32, height: 32, tint: 0x00FF00});\n * const coin = physics.createSensor({\n * type: 'Coin',\n * position: [400, 400],\n * size: [32, 32],\n * view: coinSprite\n * });\n * ```\n */\nexport interface ICrunchPhysicsPlugin extends IPlugin<CrunchPhysicsOptions> {\n system: System;\n container: PIXIContainer;\n enabled: boolean;\n\n /**\n * Initializes the physics system with the specified options.\n */\n initialize(options?: Partial<CrunchPhysicsOptions>, app?: IApplication): Promise<void>;\n\n /**\n * Sets a custom collision resolver function that will be called when collisions occur.\n *\n * @param resolver - Function to handle collisions\n *\n * @example\n * ```typescript\n * physics.setCollisionResolver((collisions) => {\n * collisions.forEach(collision => {\n * if (collision.actor.type === 'Player' && collision.solid.type === 'Spike') {\n * player.damage(10);\n * }\n * });\n * });\n * ```\n */\n setCollisionResolver(resolver: (collisions: Collision[]) => void): void;\n\n /**\n * Sets a custom resolver for actor-to-actor collisions.\n *\n * @param resolver - Function to handle actor-to-actor collisions\n *\n * @example\n * ```typescript\n * physics.setActorCollisionResolver((collisions) => {\n * collisions.forEach(collision => {\n * if (collision.actor1.type === 'Player' && collision.actor2.type === 'Enemy') {\n * player.takeDamage(10);\n * }\n * });\n * });\n * ```\n */\n setActorCollisionResolver(resolver: (collisions: ActorCollision[]) => void): void;\n\n /**\n * Enables or disables actor-to-actor collision detection.\n *\n * @param enabled - Whether to enable actor-to-actor collisions\n *\n * @example\n * ```typescript\n * // Enable actor-to-actor collisions\n * physics.setActorCollisionsEnabled(true);\n *\n * // Disable actor-to-actor collisions for performance\n * physics.setActorCollisionsEnabled(false);\n * ```\n */\n setActorCollisionsEnabled(enabled: boolean): void;\n\n /**\n * Creates a generic physics entity based on the provided configuration.\n * Use this when you need to create an entity without knowing its specific type at compile time.\n *\n * @param config - Configuration for the physics entity\n * @returns The created physics entity\n *\n * @example\n * ```typescript\n * const entityConfig = {\n * type: 'Platform',\n * position: [100, 100],\n * size: [200, 32],\n * view: sprite\n * };\n * const entity = physics.createEntity(entityConfig);\n * ```\n */\n createEntity(config: PhysicsEntityConfig): Actor | Solid | Sensor | Group;\n\n /**\n * Adds an existing physics entity to the system.\n * Useful when you want to manage entity creation yourself.\n *\n * @param entity - The physics entity to add\n * @returns The added entity\n *\n * @example\n * ```typescript\n * class CustomActor extends Actor {\n * constructor() {\n * super({ type: 'Custom', position: [0, 0], size: [32, 32] });\n * }\n * }\n *\n * const customActor = new CustomActor();\n * physics.addEntity(customActor);\n * ```\n */\n addEntity(entity: Actor | Solid | Sensor | Group): Actor | Solid | Sensor | Group;\n\n /**\n * Creates a dynamic physics actor that can move and collide with other entities.\n * Actors are typically used for players, enemies, or any moving game objects.\n *\n * @param config - Configuration for the actor\n * @returns The created actor\n *\n * @example\n * ```typescript\n * // Create a player character\n * const player = physics.createActor({\n * type: 'Player',\n * position: [100, 100],\n * size: [32, 64],\n * view: playerSprite\n * });\n *\n * // Update player in game loop\n * player.velocity.x = 300; // Move right\n * player.velocity.y = -600; // Jump\n * ```\n */\n createActor(config: PhysicsEntityConfig): Actor;\n\n /**\n * Creates a static solid object that other entities can collide with.\n * Solids are typically used for platforms, walls, and other immovable objects.\n *\n * @param config - Configuration for the solid\n * @returns The created solid\n *\n * @example\n * ```typescript\n * // Create a static platform\n * const platform = physics.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32],\n * view: platformSprite\n * });\n *\n * // Create a moving platform\n * const movingPlatform = physics.createSolid({\n * type: 'Platform',\n * position: [100, 300],\n * size: [200, 32],\n * view: platformSprite\n * });\n *\n * // Update platform position\n * gsap.to(movingPlatform, {\n * x: 500,\n * duration: 2,\n * yoyo: true,\n * repeat: -1\n * });\n * ```\n */\n createSolid(config: PhysicsEntityConfig): Solid;\n\n /**\n * Creates a sensor zone that can detect overlaps with other entities.\n * Sensors are typically used for triggers, collectibles, or detection zones.\n *\n * @param config - Configuration for the sensor\n * @returns The created sensor\n *\n * @example\n * ```typescript\n * // Create a coin pickup\n * const coin = physics.createSensor({\n * type: 'Coin',\n * position: [400, 300],\n * size: [32, 32],\n * view: coinSprite\n * });\n *\n * // Handle coin collection\n * coin.onActorEnter = (actor) => {\n * if (actor.type === 'Player') {\n * increaseScore(10);\n * physics.removeSensor(coin);\n * }\n * };\n * ```\n */\n createSensor(config: PhysicsEntityConfig): Sensor;\n\n createGroup(config: PhysicsEntityConfig): Group;\n\n addActor(actor: Actor): Actor;\n\n addSolid(solid: Solid): Solid;\n\n addSensor(sensor: Sensor): Sensor;\n\n addGroup(group: Group): Group;\n\n /**\n * Removes an actor from the physics system.\n *\n * @param actor - The actor to remove\n *\n * @example\n * ```typescript\n * // Remove player when they die\n * function killPlayer(player: Actor) {\n * playDeathAnimation();\n * physics.removeActor(player);\n * }\n * ```\n */\n removeActor(actor: Actor): void;\n\n /**\n * Removes a solid from the physics system.\n *\n * @param solid - The solid to remove\n *\n * @example\n * ```typescript\n * // Remove a platform when it's destroyed\n * function destroyPlatform(platform: Solid) {\n * playBreakAnimation();\n * physics.removeSolid(platform);\n * }\n * ```\n */\n removeSolid(solid: Solid): void;\n\n /**\n * Removes a sensor from the physics system.\n *\n * @param sensor - The sensor to remove\n *\n * @example\n * ```typescript\n * // Remove a coin when it's collected\n * function collectCoin(coin: Sensor) {\n * playCollectSound();\n * physics.removeSensor(coin);\n * }\n * ```\n */\n removeSensor(sensor: Sensor): void;\n\n /**\n * Cleans up the physics system and removes all entities.\n * Call this when transitioning between scenes or shutting down the game.\n *\n * @example\n * ```typescript\n * class GameScene extends Scene {\n * destroy() {\n * this.physics.destroy();\n * super.destroy();\n * }\n * }\n * ```\n */\n destroy(): void;\n\n /**\n * Creates a custom collision layer.\n *\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @returns A unique collision layer value\n *\n * @example\n * ```typescript\n * // Create custom collision layers\n * const WATER_LAYER = physics.createCollisionLayer(0);\n * const LAVA_LAYER = physics.createCollisionLayer(1);\n *\n * // Use in entity creation\n * const waterEntity = physics.createActor({\n * type: 'Water',\n * position: [100, 400],\n * size: [800, 100],\n * collisionLayer: WATER_LAYER,\n * collisionMask: CollisionLayer.PLAYER | CollisionLayer.ENEMY\n * });\n * ```\n */\n createCollisionLayer(index: number): number;\n\n /**\n * Creates a collision mask from multiple layers.\n *\n * @param layers Array of collision layers to combine\n * @returns A combined collision mask\n *\n * @example\n * ```typescript\n * // Create a mask that collides with players, enemies and projectiles\n * const mask = physics.createCollisionMask([\n * CollisionLayer.PLAYER,\n * CollisionLayer.ENEMY,\n * CollisionLayer.PROJECTILE\n * ]);\n * ```\n */\n createCollisionMask(...layers: number[]): number;\n\n /**\n * Registers a named collision layer for better intellisense support.\n *\n * @param name Name of the collision layer (used for intellisense)\n * @param indexOrDescription Index or description of the layer\n * @param description Optional description of the layer\n * @returns The numeric value of the registered collision layer\n *\n * @example\n * ```typescript\n * // Register named collision layers\n * const WATER = physics.registerCollisionLayer('WATER', 0, 'Water surfaces');\n * const LAVA = physics.registerCollisionLayer('LAVA', 1, 'Lava surfaces that damage players');\n *\n * // Use in entity creation\n * const waterEntity = physics.createActor({\n * type: 'Water',\n * position: [100, 400],\n * size: [800, 100],\n * collisionLayer: WATER,\n * collisionMask: CollisionLayer.PLAYER | CollisionLayer.ENEMY\n * });\n * ```\n */\n registerCollisionLayer(name: string, indexOrDescription?: number | string, description?: string): number;\n\n /**\n * Gets a registered collision layer by name.\n *\n * @param name Name of the collision layer\n * @returns The numeric value of the registered collision layer or undefined if not found\n *\n * @example\n * ```typescript\n * // Get a registered collision layer\n * const waterLayer = physics.getCollisionLayer('WATER');\n * if (waterLayer !== undefined) {\n * // Use the layer\n * entity.setCollisionLayer(waterLayer);\n * }\n * ```\n */\n getCollisionLayer(name: string): number | undefined;\n\n /**\n * Gets all registered collision layers.\n *\n * @returns Array of all registered collision layers\n *\n * @example\n * ```typescript\n * // Get all registered collision layers\n * const layers = physics.getCollisionLayers();\n * console.log(`Registered layers: ${layers.map(l => l.name).join(', ')}`);\n * ```\n */\n getCollisionLayers(): RegisteredCollisionLayer[];\n\n /**\n * Removes a registered collision layer.\n *\n * @param name Name of the collision layer to remove\n * @returns True if the layer was removed, false if it didn't exist\n *\n * @example\n * ```typescript\n * // Remove a registered collision layer\n * physics.removeCollisionLayer('WATER');\n * ```\n */\n removeCollisionLayer(name: string): boolean;\n\n /**\n * Clears all registered collision layers.\n *\n * @example\n * ```typescript\n * // Clear all registered collision layers\n * physics.clearCollisionLayers();\n * ```\n */\n clearCollisionLayers(): void;\n}\n\nconst defaultOptions: Partial<CrunchPhysicsOptions> = {\n gridSize: 128,\n gravity: 900,\n maxVelocity: 400,\n debug: false,\n culling: true,\n boundary: new Rectangle(0, 0, 800, 600),\n};\n\n/**\n * Implementation of the Crunch physics plugin.\n * See {@link ICrunchPhysicsPlugin} for detailed API documentation and examples.\n */\nexport default class CrunchPhysicsPlugin extends Plugin<CrunchPhysicsOptions> implements ICrunchPhysicsPlugin {\n public system: System;\n public container: PIXIContainer;\n public enabled = false;\n private collisionResolver?: (collisions: Collision[]) => void;\n private overlapResolver?: (overlaps: SensorOverlap[]) => void;\n private actorCollisionResolver?: (collisions: ActorCollision[]) => void;\n\n constructor() {\n super('crunch');\n }\n\n private hello() {\n const hello = `%c Caper Crunch Physics Plugin v${version}`;\n console.log(hello, 'background: rgba(31, 41, 55, 1);color: #74b64c');\n }\n\n public async initialize(options?: Partial<CrunchPhysicsOptions>, _app: IApplication): Promise<void> {\n this.hello();\n if (!options) {\n return;\n }\n\n this._options = { ...defaultOptions, ...options } as CrunchPhysicsOptions;\n\n this.enabled = true;\n this.container = options.container || this.app.stage;\n\n if (isDev) {\n Logger.log(this._options);\n }\n\n // Create the physics system\n const gridSize = options.gridSize ?? 32;\n const gravity = options.gravity ?? 900;\n const maxVelocity = options.maxVelocity ?? 400;\n const debug = options.debug ?? false;\n const enableActorCollisions = options.enableActorCollisions ?? false;\n\n // Create the system with default collision layers set to NONE for better performance\n this.system = new System({\n gridSize,\n gravity,\n maxVelocity,\n boundary: options.boundary,\n culling: options.culling,\n plugin: this,\n collisionResolver: this.collisionResolver,\n overlapResolver: this.overlapResolver,\n actorCollisionResolver: this.actorCollisionResolver,\n enableActorCollisions,\n defaultCollisionLayer: CollisionLayer.NONE,\n defaultCollisionMask: CollisionLayer.NONE,\n });\n\n if (debug) {\n this.system.debug = true;\n }\n\n // Register update loop\n this.app.ticker.add(this.update);\n }\n\n public setCollisionResolver(resolver: (collisions: Collision[]) => void): void {\n this.collisionResolver = resolver;\n this.system.setCollisionResolver(resolver);\n }\n\n public setActorCollisionResolver(resolver: (collisions: ActorCollision[]) => void): void {\n this.actorCollisionResolver = resolver;\n this.system.setActorCollisionResolver(resolver);\n }\n\n public setActorCollisionsEnabled(enabled: boolean): void {\n this.system.setActorCollisionsEnabled(enabled);\n }\n\n public createEntity(config: PhysicsEntityConfig): Actor | Solid | Sensor | Group {\n return this.system.createEntity(config);\n }\n\n public addEntity(entity: Actor | Solid | Sensor | Group): Actor | Solid | Sensor | Group {\n return this.system.addEntity(entity);\n }\n\n public createActor(config: PhysicsEntityConfig): Actor {\n return this.system.createActor(config);\n }\n\n public createSolid(config: PhysicsEntityConfig): Solid {\n return this.system.createSolid(config);\n }\n\n public createSensor(config: PhysicsEntityConfig): Sensor {\n return this.system.createSensor(config);\n }\n\n public createGroup(config: PhysicsEntityConfig): Group {\n return this.system.createGroup(config);\n }\n\n public addActor(actor: Actor): Actor {\n return this.system.addActor(actor);\n }\n\n public addSolid(solid: Solid): Solid {\n return this.system.addSolid(solid);\n }\n\n public addSensor(sensor: Sensor): Sensor {\n return this.system.addSensor(sensor);\n }\n\n public addGroup(group: Group): Group {\n return this.system.addGroup(group);\n }\n\n public removeActor(actor: Actor): void {\n this.system.removeActor(actor);\n }\n\n public removeSolid(solid: Solid): void {\n this.system.removeSolid(solid);\n }\n\n public removeSensor(sensor: Sensor): void {\n this.system.removeSensor(sensor);\n }\n\n public removeGroup(group: Group): void {\n this.system.removeGroup(group);\n }\n\n public destroy(): void {\n this.enabled = false;\n this.app.ticker.remove(this.update);\n if (this.system) {\n this.system.destroy();\n // @ts-expect-error system can't be null\n this.system = null;\n }\n super.destroy();\n }\n\n private update(_ticker: Ticker) {\n if (!this.enabled) return;\n this.system.update(_ticker.deltaTime);\n }\n\n /**\n * Creates a custom collision layer.\n *\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @returns A unique collision layer value\n */\n public createCollisionLayer(index: number): number {\n return CollisionLayers.createLayer(index);\n }\n\n /**\n * Creates a collision mask from multiple layers.\n *\n * @param layers Array of collision layers to combine\n * @returns A combined collision mask\n */\n public createCollisionMask(...layers: number[]): number {\n return CollisionLayers.createMask(layers);\n }\n\n /**\n * Registers a named collision layer for better intellisense support.\n *\n * @param name Name of the collision layer (used for intellisense)\n * @param indexOrDescription Index or description of the layer\n * @param description Optional description of the layer\n * @returns The numeric value of the registered collision layer\n */\n public registerCollisionLayer(name: string, indexOrDescription?: number | string, description?: string): number {\n const registry = CollisionLayers.getRegistry();\n\n // Handle overloaded method signature\n if (typeof indexOrDescription === 'number') {\n // First overload: registerCollisionLayer(name, index, description?)\n const index = indexOrDescription;\n const layer = registry.register(name, index, description);\n return layer.value;\n } else {\n // Second overload: registerCollisionLayer(name, description?)\n const layerDescription = indexOrDescription;\n const nextIndex = registry.getNextAvailableIndex();\n\n if (nextIndex === -1) {\n throw new Error('No more collision layer indices available (maximum 16)');\n }\n\n const layer = registry.register(name, nextIndex, layerDescription);\n return layer.value;\n }\n }\n\n /**\n * Gets a registered collision layer by name.\n *\n * @param name Name of the collision layer\n * @returns The numeric value of the registered collision layer or undefined if not found\n */\n public getCollisionLayer(name: string): number | undefined {\n const registry = CollisionLayers.getRegistry();\n const layer = registry.get(name);\n return layer?.value;\n }\n\n /**\n * Gets all registered collision layers.\n *\n * @returns Array of all registered collision layers\n */\n public getCollisionLayers(): RegisteredCollisionLayer[] {\n const registry = CollisionLayers.getRegistry();\n return registry.getAll();\n }\n\n /**\n * Removes a registered collision layer.\n *\n * @param name Name of the collision layer to remove\n * @returns True if the layer was removed, false if it didn't exist\n */\n public removeCollisionLayer(name: string): boolean {\n const registry = CollisionLayers.getRegistry();\n return registry.remove(name);\n }\n\n /**\n * Clears all registered collision layers.\n */\n public clearCollisionLayers(): void {\n const registry = CollisionLayers.getRegistry();\n registry.clear();\n }\n\n /**\n * Checks if two AABB rectangles overlap.\n *\n * @param a - The first AABB rectangle\n * @param b - The second AABB rectangle\n * @returns True if the rectangles overlap, false otherwise\n */\n public aabbOverlap(a: AABBLike, b: AABBLike): boolean {\n return this.system.aabbOverlap(a, b);\n }\n}\n","import CrunchPhysicsPlugin from './CrunchPhysicsPlugin';\n\nexport * from './Actor';\n// Don't export from collision-layers.d.ts as it's a declaration file\nexport * from './Entity';\nexport * from './Group';\nexport * from './interfaces';\nexport * from './Sensor';\nexport * from './Solid';\nexport * from './System';\nexport * from './types';\n\nexport type { ICrunchPhysicsPlugin } from './CrunchPhysicsPlugin';\n\n// Export specific types for easier access\nexport * from './types';\n\nexport default CrunchPhysicsPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"caper-plugin-crunch.mjs","names":[],"sources":["../src/types.ts","../src/utils.ts","../src/Entity.ts","../src/Actor.ts","../src/Group.ts","../src/Sensor.ts","../src/Solid.ts","../src/System.ts","../src/CrunchPhysicsPlugin.ts","../src/index.ts"],"sourcesContent":["import { PointLike, SizeLike } from '@caperjs/core';\nimport { Container } from 'pixi.js';\nimport { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Group } from './Group';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\n\nexport interface Vector2 {\n x: number;\n y: number;\n}\n\nexport interface Rectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type CollisionShape = 'rectangle';\nexport type EntityData = {\n [key: string]: any;\n};\n\nexport type PhysicsEntityClass = new (config?: PhysicsEntityConfig) => Actor | Solid | Sensor;\n\n/**\n * Collision layers using bitwise flags\n * Each entity can belong to multiple layers (using bitwise OR)\n * and can collide with multiple layers (using bitwise AND)\n */\nexport enum CollisionLayer {\n NONE = 0,\n DEFAULT = 1 << 0,\n PLAYER = 1 << 1,\n ENEMY = 1 << 2,\n PROJECTILE = 1 << 3,\n PLATFORM = 1 << 4,\n TRIGGER = 1 << 5,\n ITEM = 1 << 6,\n WALL = 1 << 7,\n FX = 1 << 8,\n // Reserve first 16 bits for built-in layers\n // Bits 16-31 are available for user-defined layers\n ALL = 0xffffffff, // All bits set to 1\n}\n\n/**\n * Interface for a registered collision layer\n */\nexport interface RegisteredCollisionLayer {\n /** Name of the collision layer */\n name: string;\n /** Numeric value of the collision layer (bitwise) */\n value: number;\n /** Description of the collision layer (optional) */\n description?: string;\n}\n\n/**\n * Registry for tracking custom collision layers\n */\nexport class CollisionLayerRegistry {\n private static _instance: CollisionLayerRegistry;\n private _layers: Map<string, RegisteredCollisionLayer> = new Map();\n private _usedIndices: Set<number> = new Set();\n\n /**\n * Get the singleton instance of the registry\n */\n public static get instance(): CollisionLayerRegistry {\n if (!CollisionLayerRegistry._instance) {\n CollisionLayerRegistry._instance = new CollisionLayerRegistry();\n }\n return CollisionLayerRegistry._instance;\n }\n\n /**\n * Register a new collision layer\n *\n * @param name Name of the collision layer\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @param description Optional description of the layer\n * @returns The registered collision layer\n */\n public register(name: string, index: number, description?: string): RegisteredCollisionLayer {\n if (index < 0 || index > 15) {\n throw new Error('Custom collision layer index must be between 0 and 15');\n }\n\n if (this._usedIndices.has(index)) {\n throw new Error(`Collision layer index ${index} is already in use`);\n }\n\n if (this._layers.has(name)) {\n throw new Error(`Collision layer with name \"${name}\" already exists`);\n }\n\n const value = 1 << (index + 16);\n const layer: RegisteredCollisionLayer = { name, value, description };\n\n this._layers.set(name, layer);\n this._usedIndices.add(index);\n\n return layer;\n }\n\n /**\n * Get a registered collision layer by name\n *\n * @param name Name of the collision layer\n * @returns The registered collision layer or undefined if not found\n */\n public get(name: string): RegisteredCollisionLayer | undefined {\n return this._layers.get(name);\n }\n\n /**\n * Get all registered collision layers\n *\n * @returns Array of all registered collision layers\n */\n public getAll(): RegisteredCollisionLayer[] {\n return Array.from(this._layers.values());\n }\n\n /**\n * Check if a collision layer with the given name exists\n *\n * @param name Name of the collision layer\n * @returns True if the layer exists\n */\n public has(name: string): boolean {\n return this._layers.has(name);\n }\n\n /**\n * Remove a registered collision layer\n *\n * @param name Name of the collision layer to remove\n * @returns True if the layer was removed, false if it didn't exist\n */\n public remove(name: string): boolean {\n const layer = this._layers.get(name);\n if (!layer) return false;\n\n // Calculate the index from the value\n const value = layer.value;\n const index = Math.log2(value) - 16;\n\n this._usedIndices.delete(index);\n return this._layers.delete(name);\n }\n\n /**\n * Clear all registered collision layers\n */\n public clear(): void {\n this._layers.clear();\n this._usedIndices.clear();\n }\n\n /**\n * Get the next available index for a custom collision layer\n *\n * @returns The next available index or -1 if all indices are used\n */\n public getNextAvailableIndex(): number {\n for (let i = 0; i < 16; i++) {\n if (!this._usedIndices.has(i)) {\n return i;\n }\n }\n return -1;\n }\n}\n\n/**\n * Utility functions for working with collision layers\n */\nexport const CollisionLayers = {\n /**\n * Creates a custom collision layer using bits 16-31 (user space)\n *\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @returns A unique collision layer value\n *\n * @example\n * ```typescript\n * // Create custom collision layers\n * const WATER_LAYER = CollisionLayers.createLayer(0); // 1 << 16\n * const LAVA_LAYER = CollisionLayers.createLayer(1); // 1 << 17\n * const CLOUD_LAYER = CollisionLayers.createLayer(2); // 1 << 18\n *\n * // Use in entity creation\n * const waterEntity = physics.createActor({\n * type: 'Water',\n * position: [100, 400],\n * size: [800, 100],\n * collisionLayer: WATER_LAYER,\n * collisionMask: CollisionLayer.PLAYER | CollisionLayer.ENEMY\n * });\n * ```\n */\n createLayer(index: number): number {\n if (index < 0 || index > 15) {\n throw new Error('Custom collision layer index must be between 0 and 15');\n }\n return 1 << (index + 16);\n },\n\n /**\n * Creates a collision mask from multiple layers\n *\n * @param layers Array of collision layers to combine\n * @returns A combined collision mask\n *\n * @example\n * ```typescript\n * // Create a mask that collides with players, enemies and projectiles\n * const mask = CollisionLayers.createMask([\n * CollisionLayer.PLAYER,\n * CollisionLayer.ENEMY,\n * CollisionLayer.PROJECTILE\n * ]);\n * ```\n */\n createMask(layers: number[]): number {\n return layers.reduce((mask, layer) => mask | layer, 0);\n },\n\n /**\n * Checks if a layer is included in a mask\n *\n * @param layer The layer to check\n * @param mask The mask to check against\n * @returns True if the layer is included in the mask\n *\n * @example\n * ```typescript\n * // Check if player layer is in the mask\n * if (CollisionLayers.isLayerInMask(CollisionLayer.PLAYER, entity.collisionMask)) {\n * console.log('Entity can collide with players');\n * }\n * ```\n */\n isLayerInMask(layer: number, mask: number): boolean {\n return (layer & mask) !== 0;\n },\n\n /**\n * Get the registry for custom collision layers\n *\n * @returns The collision layer registry\n */\n getRegistry(): CollisionLayerRegistry {\n return CollisionLayerRegistry.instance;\n },\n};\n\nexport interface PhysicsEntityConfig<D extends EntityData = EntityData> {\n id?: string;\n class?: PhysicsEntityClass;\n type?: PhysicsEntityType;\n position?: PointLike;\n size?: SizeLike;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n restitution?: number;\n view?: PhysicsEntityView;\n data?: Partial<D>;\n group?: Group;\n groupOffset?: PointLike;\n follows?: Entity;\n followOffset?: PointLike;\n /** Collision layer this entity belongs to (bitwise) */\n collisionLayer?: number;\n /** Collision mask defining which layers this entity collides with (bitwise) */\n collisionMask?: number;\n /** Whether to disable actor-to-actor collisions for this entity */\n disableActorCollisions?: boolean;\n}\n\nexport interface CollisionResult {\n collided: boolean;\n normal?: Vector2;\n penetration?: number;\n solid: Solid;\n}\n\nexport interface SensorOverlap {\n type: `${PhysicsEntityType}|${PhysicsEntityType}`;\n actor: Actor;\n sensor: Sensor;\n}\n\nexport interface CollisionResult {\n collided: boolean;\n normal?: Vector2;\n penetration?: number;\n solid: Solid;\n pushingSolid?: Solid;\n}\n\n/**\n * Result of an actor-to-actor collision\n */\nexport interface ActorCollisionResult {\n collided: boolean;\n normal?: Vector2;\n penetration?: number;\n actor: Actor;\n}\n\nexport interface Collision {\n type: `${PhysicsEntityType}|${PhysicsEntityType}`;\n entity1: Actor | Sensor;\n entity2: Actor | Solid;\n result: CollisionResult;\n}\n\n/**\n * Represents a collision between two actors\n */\nexport interface ActorCollision {\n type: `${PhysicsEntityType}|${PhysicsEntityType}`;\n actor1: Actor;\n actor2: Actor;\n result: ActorCollisionResult;\n}\n\nexport type PhysicsEntityView = Container;\nexport type PhysicsEntityType = 'Actor' | 'Solid' | 'Sensor' | 'Group' | string;\n","import { resolvePointLike, resolveSizeLike } from '@caperjs/core';\nimport { PhysicsEntityConfig } from './types';\n\n/**\n * Utility functions for resolving entity positions and sizes from various input formats.\n * These functions handle the conversion of different coordinate and size specifications\n * into standardized formats used by the physics system.\n */\n\n/**\n * Resolves an entity's position from various input formats.\n * Supports direct x/y values or a position object/array.\n *\n * @param config - Entity configuration containing position information\n * @returns Resolved {x, y} coordinates\n *\n * @example\n * ```typescript\n * // Using direct x/y values\n * resolveEntityPosition({ x: 100, y: 200 })\n * // → { x: 100, y: 200 }\n *\n * // Using position array\n * resolveEntityPosition({ position: [100, 200] })\n * // → { x: 100, y: 200 }\n *\n * // Using position object\n * resolveEntityPosition({ position: { x: 100, y: 200 } })\n * // → { x: 100, y: 200 }\n * ```\n */\nexport function resolveEntityPosition(config: PhysicsEntityConfig): { x: number; y: number } {\n const { x, y } =\n config.position !== undefined ? resolvePointLike(config.position) : { x: config?.x ?? 0, y: config?.y ?? 0 };\n\n return { x, y };\n}\n\n/**\n * Resolves an entity's size from various input formats.\n * Supports direct width/height values or a size object/array.\n *\n * @param config - Entity configuration containing size information\n * @returns Resolved {width, height} dimensions\n *\n * @example\n * ```typescript\n * // Using direct width/height values\n * resolveEntitySize({ width: 100, height: 200 })\n * // → { width: 100, height: 200 }\n *\n * // Using size array\n * resolveEntitySize({ size: [100, 200] })\n * // → { width: 100, height: 200 }\n *\n * // Using size object\n * resolveEntitySize({ size: { width: 100, height: 200 } })\n * // → { width: 100, height: 200 }\n *\n * // Using defaults\n * resolveEntitySize({})\n * // → { width: 32, height: 32 }\n * ```\n */\nexport function resolveEntitySize(config: PhysicsEntityConfig): { width: number; height: number } {\n const { width, height } =\n config.size !== undefined\n ? resolveSizeLike(config.size)\n : { width: config?.width ?? 32, height: config?.height ?? 32 };\n\n return { width, height };\n}\n","import {\n Application,\n bindAllMethods,\n defaultFactoryMethods,\n IApplication,\n PointLike,\n randomUUID,\n resolvePointLike,\n SignalConnection,\n SignalConnections,\n} from '@caperjs/core';\nimport CrunchPhysicsPlugin from './CrunchPhysicsPlugin';\nimport { Group } from './Group';\nimport { System } from './System';\nimport {\n CollisionLayer,\n EntityData,\n PhysicsEntityConfig,\n PhysicsEntityType,\n PhysicsEntityView,\n Rectangle,\n} from './types';\nimport { resolveEntityPosition, resolveEntitySize } from './utils';\n\n/**\n * Base class for all physics entities in the Crunch physics system.\n * Provides common functionality for position, size, view management, and lifecycle.\n *\n * Entity is the foundation for:\n * - Actors (dynamic objects)\n * - Solids (static objects)\n * - Sensors (trigger zones)\n *\n * It handles:\n * - Position and size management\n * - View (sprite) management and updates\n * - Group membership and relative positioning\n * - Culling and lifecycle states\n * - Signal connections for event handling\n *\n * @typeParam A - Application type, defaults to base Application\n * @typeParam D - Entity data type, defaults to base EntityData\n *\n * @example\n * ```typescript\n * // Create a custom entity\n * class CustomEntity extends Entity {\n * constructor() {\n * super({\n * type: 'Custom',\n * position: [100, 100],\n * size: [32, 32],\n * view: sprite\n * });\n * }\n *\n * // Override update for custom behavior\n * update(dt: number) {\n * super.update(dt);\n * // Custom update logic\n * }\n * }\n * ```\n */\nexport class Entity<D extends EntityData = EntityData> {\n public readonly entityType: PhysicsEntityType;\n protected _id: string;\n /** Unique type identifier for this entity */\n public type!: string;\n\n /** Color to use when rendering debug visuals */\n public debugColor: number;\n\n /** Whether the entity should be removed when culled (out of bounds) */\n public shouldRemoveOnCull: boolean;\n\n /** Entity width in pixels */\n public width: number;\n\n /** Entity height in pixels */\n public height: number;\n\n /** Visual representation (sprite/graphics) of this entity */\n public view!: PhysicsEntityView;\n\n /** Whether this entity is active and should be updated */\n\n /** Collision layer this entity belongs to (bitwise) */\n public collisionLayer: number = CollisionLayer.NONE;\n\n /** Collision mask defining which layers this entity collides with (bitwise) */\n public collisionMask: number = CollisionLayer.NONE;\n\n protected _data: Partial<D>;\n protected _group: Group | null;\n protected _groupOffset: { x: number; y: number };\n protected _isCulled: boolean;\n protected _isDestroyed: boolean;\n protected _isInitialized: boolean;\n protected _xRemainder: number;\n protected _yRemainder: number;\n protected _x: number;\n protected _y: number;\n protected signalConnections: SignalConnections;\n protected _following: Entity | null;\n protected _followOffset: { x: number; y: number };\n protected _config: PhysicsEntityConfig<D> | undefined;\n protected _active: boolean = true;\n\n public updatedFollowPosition: boolean = false;\n public updatedGroupPosition: boolean = false;\n\n set active(value: boolean) {\n this._active = value;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n set id(value: string) {\n this._id = value;\n }\n\n get id(): string {\n return this._id || this.type;\n }\n\n get make(): typeof defaultFactoryMethods {\n return this.app.make;\n }\n\n /**\n * Custom data associated with this entity\n */\n set data(value: Partial<D>) {\n this._data = value;\n }\n\n get data(): Partial<D> {\n return this._data;\n }\n\n setFollowing(entityToFollow: Entity | null, offset: PointLike = { x: 0, y: 0 }) {\n this._followOffset = resolvePointLike(offset);\n if (this._following) {\n this.system.removeFollower(this);\n }\n this._following = entityToFollow;\n if (entityToFollow) {\n this.system.addFollower(entityToFollow, this);\n }\n }\n\n get followOffset(): { x: number; y: number } {\n return this._followOffset || { x: 0, y: 0 };\n }\n\n get following(): Entity | null {\n return this._following;\n }\n\n get followers(): Entity[] {\n return this.system.getFollowersOf(this);\n }\n\n /**\n * The group this entity belongs to, if any.\n * Groups allow for collective movement and management of entities.\n */\n get group(): Group | null {\n return this._group;\n }\n\n set groupOffset(value: { x: number; y: number }) {\n this._groupOffset = resolvePointLike(value);\n }\n\n get groupOffset(): { x: number; y: number } {\n return this._groupOffset || { x: 0, y: 0 };\n }\n\n setGroup(group: Group | null, offset: PointLike = { x: 0, y: 0 }) {\n this._groupOffset = resolvePointLike(offset);\n // if we're already in a group, remove ourselves first\n if (this._group) {\n this.system.removeFromGroup(this);\n this.onRemovedFromGroup();\n }\n this._group = group;\n if (group) {\n this.system.addToGroup(group, this);\n this.onAddedToGroup();\n }\n }\n\n set position(value: PointLike) {\n const { x, y } = resolvePointLike(value);\n this.setPosition(x, y);\n }\n\n get position(): PointLike {\n return { x: this.x, y: this.y };\n }\n\n /**\n * Entity's X position in world space.\n * If the entity belongs to a group, returns position relative to group.\n */\n set x(value: number) {\n this._x = value;\n }\n\n get x(): number {\n if (this._group) {\n return Math.round(this._x + this._group.getChildOffset(this).x); // Return world position\n }\n return Math.round(this._x);\n }\n\n /**\n * Entity's Y position in world space.\n * If the entity belongs to a group, returns position relative to group.\n */\n set y(value: number) {\n this._y = value;\n }\n\n get y(): number {\n if (this._group) {\n return Math.round(this._y + this._group.getChildOffset(this).y); // Return world position\n }\n return Math.round(this._y);\n }\n\n /** Whether this entity is currently culled (out of bounds) */\n get isCulled(): boolean {\n return this._isCulled;\n }\n\n /** Whether this entity has been destroyed */\n get isDestroyed(): boolean {\n return this._isDestroyed;\n }\n\n /** Reference to the main application instance */\n get app(): IApplication {\n return Application.getInstance();\n }\n\n /** Reference to the physics plugin */\n get physics(): CrunchPhysicsPlugin {\n return this.app.getPlugin('crunch') as CrunchPhysicsPlugin;\n }\n\n /**\n * Creates a new Entity instance.\n *\n * @param config - Optional configuration for the entity\n */\n constructor(config?: PhysicsEntityConfig<D>) {\n bindAllMethods(this);\n\n this._config = config;\n\n this.signalConnections = new SignalConnections();\n this.shouldRemoveOnCull = false;\n this.width = 0;\n this.height = 0;\n\n this._data = {};\n this._group = null;\n this._isCulled = false;\n this._isDestroyed = false;\n this._isInitialized = false;\n this._xRemainder = 0;\n this._yRemainder = 0;\n\n this._x = 0;\n this._y = 0;\n\n if (config) {\n this.init(config);\n }\n\n this.initialize();\n this.addView();\n }\n\n /**\n * Called after construction to perform additional initialization.\n * Override this in subclasses to add custom initialization logic.\n */\n protected initialize() {\n // Override in subclass\n }\n\n /**\n * Called before update to prepare for the next frame.\n * Override this in subclasses to add pre-update logic.\n */\n public preUpdate(): void {\n // Override in subclass\n }\n\n /**\n * Called every frame to update the entity's state.\n * Override this in subclasses to add update logic.\n *\n * @param dt - Delta time in seconds since last update\n */\n public update(dt: number): void {\n // Override in subclass\n void dt;\n }\n\n /**\n * Called after update to finalize the frame.\n * Override this in subclasses to add post-update logic.\n */\n public postUpdate(): void {\n // Override in subclass\n }\n\n /**\n * Excludes collision types for this entity\n * @deprecated Use setCollisionMask instead\n */\n excludeCollisionType() {\n console.warn('excludeCollisionType is deprecated. Use setCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Includes collision types for this entity\n * @deprecated Use setCollisionMask instead\n */\n includeCollisionType() {\n console.warn('includeCollisionType is deprecated. Use setCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Removes collision types for this entity\n * @deprecated Use removeCollisionMask instead\n */\n removeCollisionType() {\n console.warn('removeCollisionType is deprecated. Use removeCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Adds collision types for this entity\n * @deprecated Use addCollisionMask instead\n */\n addCollisionType() {\n console.warn('addCollisionType is deprecated. Use addCollisionMask instead.');\n // No-op as we're removing this functionality\n }\n\n /**\n * Checks if this entity can collide with a specific type\n */\n canCollideWith(): boolean {\n // Always return true as we're now using only collision layers/masks\n return true;\n }\n\n /**\n * Adds the entity's view to the physics container and updates its position.\n */\n protected addView() {\n if (this.view) {\n this.view.visible = true;\n this.view.label = this.id || this.type;\n if (this.system.container) {\n this.system.container.addChild(this.view);\n this.updateView();\n }\n }\n }\n\n /**\n * Initializes or reinitializes the entity with new configuration.\n * Used by object pools when recycling entities.\n *\n * @param config - New configuration to apply\n */\n public init(config: PhysicsEntityConfig<D>): void {\n if (!config) return;\n this._config = config as PhysicsEntityConfig<D>;\n\n if (config.id) {\n this._id = config.id;\n } else {\n this._id = randomUUID();\n }\n\n if (config.type) {\n this.type = config.type;\n }\n\n if (config.data) {\n this._data = config.data as Partial<D>;\n }\n\n const position = resolveEntityPosition(config);\n this._x = position.x;\n this._y = position.y;\n\n const size = resolveEntitySize(config);\n this.width = size.width;\n this.height = size.height;\n\n // Initialize collision layers\n if (config.collisionLayer !== undefined) {\n this.setCollisionLayer(config.collisionLayer);\n }\n\n if (config.collisionMask !== undefined) {\n this.setCollisionMask(config.collisionMask);\n }\n\n // Reset physics properties\n this._xRemainder = 0;\n this._yRemainder = 0;\n\n if (config.view) {\n this.setView(config.view);\n }\n\n if (config.group) {\n this.setGroup(config.group ?? null, config.groupOffset ? resolvePointLike(config.groupOffset) : { x: 0, y: 0 });\n }\n\n if (config.follows) {\n this.setFollowing(\n config.follows ?? null,\n config.followOffset ? resolvePointLike(config.followOffset) : { x: 0, y: 0 },\n );\n }\n // Show and update view if it exists\n this.addView();\n }\n\n /**\n * Resets the entity to its initial state for reuse in object pools.\n * Override this to handle custom reset logic.\n */\n public reset(): void {\n // Reset culling state\n this._isCulled = false;\n this._isDestroyed = false;\n\n // Reset remainders\n this._xRemainder = 0;\n this._yRemainder = 0;\n\n this._followOffset = { x: 0, y: 0 };\n this._following = null;\n\n this._groupOffset = { x: 0, y: 0 };\n this._group = null;\n\n this._data = {};\n\n this._x = -Number.MAX_SAFE_INTEGER;\n this._y = -Number.MAX_SAFE_INTEGER;\n\n if (this.view) {\n this.view.visible = false;\n }\n\n this.system.removeEntity(this);\n }\n\n /** Reference to the physics system */\n get system(): System {\n return this.physics.system;\n }\n\n /**\n * Called when the entity is added to a group.\n * Override this to handle custom group addition logic.\n */\n public onAddedToGroup(): void {\n // Override in subclass\n }\n\n /**\n * Called when the entity is removed from a group.\n * Override this to handle custom group removal logic.\n */\n public onRemovedFromGroup(): void {\n // Override in subclass\n }\n\n /**\n * Updates the entity's position and view.\n */\n public updatePosition(): void {\n this.x = this._x;\n this.y = this._y;\n this.updateView();\n }\n\n /**\n * Called when the entity is culled (goes out of bounds).\n * Override this to handle culling differently.\n */\n public onCull(): void {\n this._isCulled = true;\n // Default behavior: hide the view\n if (this.view) {\n this.view.visible = false;\n }\n }\n\n /**\n * Called when the entity is brought back after being culled.\n * Override this to handle unculling differently.\n */\n public onUncull(): void {\n this._isCulled = false;\n // Default behavior: show the view\n if (this.view) {\n this.view.visible = true;\n }\n }\n\n /**\n * Prepares the entity for removal/recycling.\n * Override this to handle custom cleanup.\n */\n public destroy(): void {\n if (this._isDestroyed) return;\n\n this._isDestroyed = true;\n this._isCulled = false;\n\n this.signalConnections.disconnectAll();\n\n // Don't destroy the view - it will be reused\n if (this.view) {\n this.view.visible = false;\n this.view.removeFromParent();\n }\n\n this.system.removeFollower(this);\n }\n\n /**\n * Called when the entity is removed from the physics system.\n * Override this to handle custom removal logic.\n */\n public onRemoved(): void {\n if (!this._isDestroyed) {\n this.destroy();\n }\n }\n\n /**\n * Sets a new view for the entity and updates its position.\n *\n * @param view - The new view to use\n */\n public setView(view: PhysicsEntityView): void {\n this.view = view;\n this.updateView();\n }\n\n /**\n * Updates the view's position to match the entity's position.\n */\n public updateView(): void {\n if (this.view && this.view.visible && this.view.position) {\n this.view.position.set(this.x, this.y);\n }\n }\n\n /**\n * Gets the entity's bounding rectangle.\n *\n * @returns Rectangle representing the entity's bounds\n */\n public getBounds(): Rectangle {\n return {\n x: this.x,\n y: this.y,\n width: this.width,\n height: this.height,\n };\n }\n\n /**\n * Sets the entity's position, resetting any movement remainders.\n *\n * @param x - New X position\n * @param y - New Y position\n */\n public setPosition(x: number, y: number): void {\n this._x = x;\n this._y = y;\n this._xRemainder = 0;\n this._yRemainder = 0;\n this.updateView();\n }\n\n /**\n * Alias for setPosition.\n *\n * @param x - New X position\n * @param y - New Y position\n */\n public moveTo(x: number, y: number): void {\n this.setPosition(x, y);\n }\n\n /**\n * Adds signal connections to the entity.\n *\n * @param args - Signal connections to add\n */\n public addSignalConnection(...args: SignalConnection[]) {\n for (const connection of args) {\n this.signalConnections.add(connection);\n }\n }\n\n /**\n * Alias for addSignalConnection.\n *\n * @param args - Signal connections to add\n */\n public connectSignal(...args: SignalConnection[]) {\n for (const connection of args) {\n this.signalConnections.add(connection);\n }\n }\n\n /**\n * Alias for addSignalConnection, specifically for action signals.\n *\n * @param args - Action signal connections to add\n */\n public connectAction(...args: SignalConnection[]) {\n for (const connection of args) {\n this.signalConnections.add(connection);\n }\n }\n\n /**\n * Checks if this entity can collide with another entity\n */\n public canCollideWithEntity(entity: Entity): boolean {\n // Check if the entities can collide based on their collision layers and masks\n // A collision occurs when (A.layer & B.mask) !== 0 && (B.layer & A.mask) !== 0\n return (this.collisionLayer & entity.collisionMask) !== 0 && (entity.collisionLayer & this.collisionMask) !== 0;\n }\n\n /**\n * Sets the collision layer for this entity\n *\n * @param layer The collision layer or layers (can be combined with bitwise OR)\n */\n public setCollisionLayer(layer: number): void {\n this.collisionLayer = layer;\n }\n\n /**\n * Adds the specified layers to this entity's collision layer\n *\n * @param layers The layers to add (can be combined with bitwise OR)\n */\n public addCollisionLayer(layers: number): void {\n this.collisionLayer |= layers;\n }\n\n /**\n * Removes the specified layers from this entity's collision layer\n *\n * @param layers The layers to remove (can be combined with bitwise OR)\n */\n public removeCollisionLayer(layers: number): void {\n this.collisionLayer &= ~layers;\n }\n\n /**\n * Sets the collision mask for this entity\n *\n * @param mask The collision mask (can be combined with bitwise OR)\n */\n public setCollisionMask(...mask: number[]): void {\n this.collisionMask = this.physics.createCollisionMask(...mask);\n }\n\n /**\n * Adds the specified layers to this entity's collision mask\n *\n * @param layers The layers to add to the mask (can be combined with bitwise OR)\n */\n public addCollisionMask(layers: number): void {\n this.collisionMask |= layers;\n }\n\n /**\n * Removes the specified layers from this entity's collision mask\n *\n * @param layers The layers to remove from the mask (can be combined with bitwise OR)\n */\n public removeCollisionMask(layers: number): void {\n this.collisionMask &= ~layers;\n }\n\n /**\n * Checks if this entity belongs to a specific collision layer\n *\n * @param layer The layer to check\n * @returns True if the entity belongs to the specified layer\n *\n * @example\n * ```typescript\n * // Check if entity is on the PLAYER layer\n * if (entity.hasCollisionLayer(CollisionLayer.PLAYER)) {\n * console.log('Entity is a player');\n * }\n *\n * // Check if entity is on a custom layer\n * const WATER_LAYER = CollisionLayers.createLayer(0);\n * if (entity.hasCollisionLayer(WATER_LAYER)) {\n * console.log('Entity is water');\n * }\n * ```\n */\n public hasCollisionLayer(layer: number): boolean {\n return (this.collisionLayer & layer) !== 0;\n }\n\n /**\n * Checks if this entity can collide with a specific collision layer\n *\n * @param layer The layer to check\n * @returns True if the entity can collide with the specified layer\n *\n * @example\n * ```typescript\n * // Check if entity can collide with players\n * if (entity.canCollideWithLayer(CollisionLayer.PLAYER)) {\n * console.log('Entity can collide with players');\n * }\n *\n * // Check if entity can collide with a custom layer\n * const WATER_LAYER = CollisionLayers.createLayer(0);\n * if (entity.canCollideWithLayer(WATER_LAYER)) {\n * console.log('Entity can collide with water');\n * }\n * ```\n */\n public canCollideWithLayer(layer: number): boolean {\n return (this.collisionMask & layer) !== 0;\n }\n}\n","import { Entity } from './Entity';\nimport { Solid } from './Solid';\nimport {\n ActorCollisionResult,\n CollisionResult,\n EntityData,\n PhysicsEntityConfig,\n PhysicsEntityType,\n Vector2,\n} from './types';\n\n/**\n * Dynamic physics entity that can move and collide with other entities.\n * Actors are typically used for players, enemies, projectiles, and other moving game objects.\n *\n * Features:\n * - Velocity-based movement with gravity\n * - Collision detection and response\n * - Solid surface detection (riding)\n * - Automatic culling when out of bounds\n * - Actor-to-actor collision detection\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a player actor\n * class Player extends Actor {\n * constructor() {\n * super({\n * type: 'Player',\n * position: [100, 100],\n * size: [32, 64],\n * view: playerSprite\n * });\n * }\n *\n * // Handle collisions\n * onCollide(result: CollisionResult) {\n * if (result.solid.type === 'Spike') {\n * this.die();\n * }\n * }\n *\n * // Handle actor-to-actor collisions\n * onActorCollide(result: ActorCollisionResult) {\n * if (result.actor.type === 'Enemy') {\n * this.takeDamage(10);\n * }\n * }\n *\n * // Custom movement\n * update(dt: number) {\n * super.update(dt);\n *\n * // Move left/right\n * if (this.app.input.isKeyDown('ArrowLeft')) {\n * this.velocity.x = -200;\n * } else if (this.app.input.isKeyDown('ArrowRight')) {\n * this.velocity.x = 200;\n * }\n *\n * // Jump when on ground\n * if (this.app.input.isKeyPressed('Space') && this.isRidingSolid()) {\n * this.velocity.y = -400;\n * }\n * }\n * }\n * ```\n */\nexport class Actor<D extends EntityData = EntityData> extends Entity<D> {\n public readonly entityType: PhysicsEntityType = 'Actor';\n\n /** Current velocity in pixels per second */\n public velocity: Vector2 = { x: 0, y: 0 };\n\n /** Whether actor-to-actor collisions are disabled for this actor */\n public disableActorCollisions: boolean = false;\n\n /** Whether the actor should be removed when culled (out of bounds) */\n public shouldRemoveOnCull: boolean = true;\n\n /** List of current frame collisions */\n public collisions: CollisionResult[] = [];\n\n /** List of current frame actor-to-actor collisions */\n public actorCollisions: ActorCollisionResult[] = [];\n\n /** Cache for isRidingSolid check */\n private _isRidingSolidCache: boolean | null = null;\n\n /** Tracks which solid is currently carrying this actor in the current frame */\n private _carriedBy: Solid | null = null;\n private _carriedByOverlap: number = 0;\n\n /** Tracks the grid cells this actor currently occupies */\n private _currentGridCells: string[] = [];\n\n /**\n * Initialize or reinitialize the actor with new configuration.\n *\n * @param config - Configuration for the actor\n */\n public init(config: PhysicsEntityConfig<D>): void {\n super.init(config);\n // Reset velocity and carried state\n this.velocity = { x: 0, y: 0 };\n this._isRidingSolidCache = null;\n this._carriedBy = null;\n this._carriedByOverlap = 0;\n this.actorCollisions = [];\n this._currentGridCells = [];\n\n if (config.disableActorCollisions !== undefined) {\n this.disableActorCollisions = config.disableActorCollisions;\n }\n\n // Add actor to grid initially if actor collisions are enabled\n if (this.system.enableActorCollisions && !this.disableActorCollisions) {\n this.updateGridCells();\n }\n }\n\n /**\n * Called at the start of each update to prepare for collision checks.\n */\n public preUpdate(): void {\n if (!this.active) return;\n\n this.collisions = [];\n this.actorCollisions = [];\n // Reset the cache at the start of each update\n this._isRidingSolidCache = null;\n this._carriedBy = null;\n this._carriedByOverlap = 0;\n }\n\n /**\n * Updates the actor's position based on velocity and handles collisions.\n *\n * @param dt - Delta time in seconds\n */\n public update(dt: number): void {\n if (!this.active) return;\n\n // Ensure velocity is valid\n if (!this.isRidingSolid()) {\n this.velocity.y += this.system.gravity * dt;\n }\n\n // Clamp velocity\n this.velocity.x = Math.min(Math.max(this.velocity.x, -this.system.maxVelocity), this.system.maxVelocity);\n this.velocity.y = Math.min(Math.max(this.velocity.y, -this.system.maxVelocity), this.system.maxVelocity);\n\n // Move horizontally\n if (this.velocity.x !== 0) {\n this.moveX(this.velocity.x * dt);\n }\n\n // Move vertically\n if (this.velocity.y !== 0) {\n this.moveY(this.velocity.y * dt);\n }\n\n if (this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n\n // Update view\n this.updateView();\n }\n\n /**\n * Called after update to handle post-movement effects.\n */\n public postUpdate(): void {\n if (!this.active) return;\n\n if (this.isRidingSolid()) {\n this.velocity.y = 0;\n }\n }\n\n /**\n * Resets the actor to its initial state.\n */\n public reset(): void {\n super.reset();\n\n this._isRidingSolidCache = null;\n this._carriedBy = null;\n this._carriedByOverlap = 0;\n this.velocity = { x: 0, y: 0 };\n\n this.updatePosition();\n }\n\n /**\n * Called when the actor is culled (goes out of bounds).\n * Override this to handle culling differently.\n */\n public onCull(): void {\n // Default behavior: destroy the view\n this.view?.destroy();\n }\n\n /**\n * Called when this actor collides with a solid.\n * Override this method to implement custom collision response.\n *\n * @param result - Information about the collision\n */\n public onCollide(result: CollisionResult): void {\n // Default implementation does nothing\n // Override this in your actor subclass to handle collisions\n void result;\n }\n\n /**\n * Called when this actor collides with another actor.\n * Override this method to implement custom actor-to-actor collision response.\n *\n * @param result - Information about the actor collision\n */\n public onActorCollide(result: ActorCollisionResult): void {\n // Default implementation does nothing\n // Override this in your actor subclass to handle actor-to-actor collisions\n void result;\n }\n\n /**\n * Checks if this actor is riding the given solid.\n * An actor is riding if it's directly above the solid.\n *\n * @param solid - The solid to check against\n * @returns True if riding the solid\n */\n public isRiding(solid: Solid): boolean {\n // Skip if solid has no collisions\n if (!solid.collideable) return false;\n\n // Check collision layers and masks\n // An actor can only ride a solid if their collision layers/masks allow interaction\n if ((this.collisionLayer & solid.collisionMask) === 0 || (solid.collisionLayer & this.collisionMask) === 0) {\n return false;\n }\n\n // If we're already being carried by a different solid this frame,\n // we can't be riding this one\n if (this._carriedBy && this._carriedBy !== solid) {\n return false;\n }\n\n // Must be directly above the solid (within 1 pixel)\n const actorBottom = this.y + this.height;\n const onTop = Math.abs(actorBottom - solid.y) <= 1;\n\n // Must be horizontally overlapping\n const overlap = this.x + this.width > solid.x && this.x < solid.x + solid.width;\n const overlapWidth = Math.min(this.x + this.width, solid.x + solid.width) - Math.max(this.x, solid.x);\n\n const isRiding = onTop && overlap;\n\n if (isRiding && overlapWidth > this._carriedByOverlap) {\n this._carriedBy = solid;\n this._carriedByOverlap = overlapWidth;\n }\n return isRiding;\n }\n\n /**\n * Checks if this actor is riding any solid in the physics system.\n * Uses caching to optimize multiple checks per frame.\n *\n * @returns True if riding any solid\n */\n public isRidingSolid(): boolean {\n // Return cached value if available\n if (this._isRidingSolidCache !== null) {\n return this._isRidingSolidCache;\n }\n\n // Calculate and cache the result\n const solids = this.getSolidsAt(this.x, this.y + 1);\n this._isRidingSolidCache = solids.some((solid) => this.isRiding(solid));\n return this._isRidingSolidCache;\n }\n\n /**\n * Called when the actor is squeezed between solids.\n * Override this to handle squishing differently.\n */\n public squish(result: CollisionResult): void {\n void result;\n // do something\n }\n\n /**\n * Updates the actor's grid cells in the spatial partitioning system.\n * This is called when the actor moves or when its size changes.\n */\n public updateGridCells(): void {\n // Skip if actor collisions are disabled system-wide or for this actor specifically\n if (this.disableActorCollisions) return;\n\n this.system.updateActorInGrid(this);\n }\n\n /**\n * Gets the current grid cells this actor occupies\n */\n public get currentGridCells(): string[] {\n return this._currentGridCells;\n }\n\n /**\n * Sets the current grid cells this actor occupies\n */\n public set currentGridCells(cells: string[]) {\n this._currentGridCells = cells;\n }\n\n /**\n * Moves the actor horizontally, checking for collisions with solids.\n *\n * @param amount - Distance to move in pixels\n * @param collisionHandler - Optional callback for handling collisions\n * @returns Array of collision results\n */\n public moveX(\n amount: number,\n collisionHandler?: (result: CollisionResult) => void,\n pushingSolid?: Solid,\n ): CollisionResult[] {\n // Early return if inactive or zero movement\n if (!this.active || amount === 0) return [];\n\n this._xRemainder += amount;\n const move = Math.round(this._xRemainder);\n\n // Early return if rounded movement is zero\n if (move === 0) return [];\n\n const collisions: CollisionResult[] = [];\n\n // Cache collision layer and mask for faster access\n const actorLayer = this.collisionLayer;\n const actorMask = this.collisionMask;\n\n // Skip collision checks if no collision mask\n if (actorMask === 0) {\n // Just move without checking collisions\n this._xRemainder -= move;\n this._x += move;\n this.updateView();\n\n return [];\n }\n\n this._xRemainder -= move;\n const sign = Math.sign(move);\n let remaining = Math.abs(move);\n const step = sign;\n\n // If we're being pushed by a solid, temporarily make it non-collidable\n if (pushingSolid) {\n pushingSolid.collideable = false;\n }\n\n // Move one pixel at a time, checking for collisions\n while (remaining > 0) {\n const nextX = this._x + step;\n\n // Get solids at the next position\n const solids = this.getSolidsAt(nextX, this._y);\n let collided = false;\n\n // Check for collisions with each solid\n for (const solid of solids) {\n // Skip if solid can't collide\n if (!solid.canCollide) continue;\n\n // Skip if collision layers don't match\n if ((actorLayer & solid.collisionMask) === 0 || (solid.collisionLayer & actorMask) === 0) {\n continue;\n }\n\n // Calculate collision details\n const result: CollisionResult = {\n collided: true,\n solid,\n normal: { x: -sign, y: 0 },\n penetration: step > 0 ? this.x + this.width - solid.x : solid.x + solid.width - this.x,\n pushingSolid,\n };\n\n // Add to collisions array\n collisions.push(result);\n\n // Call collision handler if provided\n if (collisionHandler) {\n collisionHandler(result);\n }\n\n // Call actor's collision handler\n this.onCollide(result);\n\n collided = true;\n }\n\n if (collided) {\n // Stop movement on collision\n break;\n } else {\n // Move to next position\n this._x = nextX;\n remaining--;\n\n // Update view every few pixels for better performance\n // This reduces the number of view updates during movement\n if (remaining % 4 === 0 || remaining === 0) {\n this.updateView();\n }\n }\n }\n\n // Restore solid's collidable state\n if (pushingSolid) {\n pushingSolid.collideable = true;\n }\n\n // Final view update if we moved\n if (Math.abs(move) - remaining > 0) {\n this.updateView();\n }\n\n return collisions;\n }\n\n /**\n * Moves the actor vertically, checking for collisions with solids.\n *\n * @param amount - Distance to move in pixels\n * @param collisionHandler - Optional callback for handling collisions\n * @returns Array of collision results\n */\n public moveY(\n amount: number,\n collisionHandler?: (result: CollisionResult) => void,\n pushingSolid?: Solid,\n ): CollisionResult[] {\n // Early return if inactive or zero movement\n if (!this.active || amount === 0) return [];\n\n this._yRemainder += amount;\n const move = Math.round(this._yRemainder);\n\n // Early return if rounded movement is zero\n if (move === 0) return [];\n\n const collisions: CollisionResult[] = [];\n\n // Cache collision layer and mask for faster access\n const actorLayer = this.collisionLayer;\n const actorMask = this.collisionMask;\n\n // Skip collision checks if no collision mask\n if (actorMask === 0) {\n // Just move without checking collisions\n this._yRemainder -= move;\n this._y += move;\n this.updateView();\n\n return [];\n }\n\n this._yRemainder -= move;\n const sign = Math.sign(move);\n let remaining = Math.abs(move);\n const step = sign;\n\n // If we're being pushed by a solid, temporarily make it non-collidable\n if (pushingSolid) {\n pushingSolid.collideable = false;\n }\n\n // Move one pixel at a time, checking for collisions\n while (remaining > 0) {\n const nextY = this._y + step;\n\n // Get solids at the next position\n const solids = this.getSolidsAt(this._x, nextY);\n let collided = false;\n\n // Check for collisions with each solid\n for (const solid of solids) {\n // Skip if solid can't collide\n if (!solid.canCollide) continue;\n\n // Skip if collision layers don't match\n if ((actorLayer & solid.collisionMask) === 0 || (solid.collisionLayer & actorMask) === 0) {\n continue;\n }\n\n // Calculate collision details\n const result: CollisionResult = {\n collided: true,\n solid,\n normal: { x: 0, y: -sign },\n penetration: step > 0 ? this.y + this.height - solid.y : solid.y + solid.height - this.y,\n pushingSolid,\n };\n\n // Add to collisions array\n collisions.push(result);\n\n // Call collision handler if provided\n if (collisionHandler) {\n collisionHandler(result);\n }\n\n // Call actor's collision handler\n this.onCollide(result);\n\n collided = true;\n }\n\n if (collided) {\n // Stop movement on collision\n break;\n } else {\n // Move to next position\n this._y = nextY;\n remaining--;\n\n // Update view every few pixels for better performance\n // This reduces the number of view updates during movement\n if (remaining % 4 === 0 || remaining === 0) {\n this.updateView();\n }\n }\n }\n\n // Restore solid's collidable state\n if (pushingSolid) {\n pushingSolid.collideable = true;\n }\n\n // Final view update if we moved\n if (Math.abs(move) - remaining > 0) {\n this.updateView();\n\n // Update grid cells if actor moved and actor collisions are enabled\n }\n\n return collisions;\n }\n\n /**\n * Updates the actor's view position.\n */\n public updateView(): void {\n if (this.view && this.view.visible) {\n this.view.x = this._x;\n this.view.y = this._y;\n }\n }\n\n /**\n * Gets all solids at the specified position that could collide with this actor.\n *\n * @param _x - X position to check\n * @param _y - Y position to check\n * @returns Array of solids at the position\n */\n protected getSolidsAt(_x: number, _y: number): Solid[] {\n return this.system.getSolidsAt(_x, _y, this);\n }\n\n /**\n * Checks if this actor is colliding with another actor.\n * The collision will only occur if:\n * 1. Both actors are active\n * 2. Neither actor has disabled actor collisions\n * 3. The collision layers and masks match:\n * - (this.collisionLayer & other.collisionMask) !== 0\n * - (other.collisionLayer & this.collisionMask) !== 0\n *\n * @param actor - The actor to check collision with\n * @returns Collision result with information about the collision\n */\n public checkActorCollision(actor: Actor): ActorCollisionResult {\n // Skip if either actor is not active\n if (!this.active || !actor.active) {\n return { collided: false, actor };\n }\n\n // Skip if either actor has disabled actor collisions\n if (this.disableActorCollisions || actor.disableActorCollisions) {\n return { collided: false, actor };\n }\n\n // Skip if the actors can't collide based on collision layers\n if ((this.collisionLayer & actor.collisionMask) === 0 || (actor.collisionLayer & this.collisionMask) === 0) {\n return { collided: false, actor };\n }\n\n // Simple AABB collision check\n const thisLeft = this.x;\n const thisRight = this.x + this.width;\n const thisTop = this.y;\n const thisBottom = this.y + this.height;\n\n const otherLeft = actor.x;\n const otherRight = actor.x + actor.width;\n const otherTop = actor.y;\n const otherBottom = actor.y + actor.height;\n\n // Check if the bounding boxes overlap\n if (thisRight > otherLeft && thisLeft < otherRight && thisBottom > otherTop && thisTop < otherBottom) {\n // Calculate penetration and normal\n const overlapX = Math.min(thisRight - otherLeft, otherRight - thisLeft);\n const overlapY = Math.min(thisBottom - otherTop, otherBottom - thisTop);\n\n let normal: Vector2;\n let penetration: number;\n\n // Determine the collision normal based on the smallest overlap\n if (overlapX < overlapY) {\n penetration = overlapX;\n normal = {\n x: thisLeft < otherLeft ? -1 : 1,\n y: 0,\n };\n } else {\n penetration = overlapY;\n normal = {\n x: 0,\n y: thisTop < otherTop ? -1 : 1,\n };\n }\n\n return {\n collided: true,\n actor,\n normal,\n penetration,\n };\n }\n\n return { collided: false, actor };\n }\n\n /**\n * Resolves a collision with another actor.\n *\n * @param result - The collision result to resolve\n * @param shouldMove - Whether this actor should move to resolve the collision\n * @returns The updated collision result\n */\n public resolveActorCollision(result: ActorCollisionResult): ActorCollisionResult {\n if (!result.collided || !result.normal || !result.penetration) {\n return result;\n }\n\n // Call the collision handler\n this.onActorCollide(result);\n\n // Example:\n // Move this actor to resolve the collision\n // this.x += result.normal.x * result.penetration * 0.5;\n // this.y += result.normal.y * result.penetration * 0.5;\n\n return result;\n }\n\n /**\n * Sets the actor's size and updates grid cells if needed.\n *\n * @param width - New width in pixels\n * @param height - New height in pixels\n */\n public setSize(width: number, height: number): void {\n const sizeChanged = this.width !== width || this.height !== height;\n\n this.width = width;\n this.height = height;\n\n // Update grid cells if size changed and actor collisions are enabled\n if (sizeChanged && this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n }\n\n /**\n * Sets the actor's width and updates grid cells if needed.\n *\n * @param value - New width in pixels\n */\n public setWidth(value: number): void {\n if (this.width !== value) {\n this.width = value;\n\n // Update grid cells if size changed and actor collisions are enabled\n if (this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n }\n }\n\n /**\n * Sets the actor's height and updates grid cells if needed.\n *\n * @param value - New height in pixels\n */\n public setHeight(value: number): void {\n if (this.height !== value) {\n this.height = value;\n\n // Update grid cells if size changed and actor collisions are enabled\n if (this.system.enableActorCollisions) {\n this.updateGridCells();\n }\n }\n }\n}\n","import { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\nimport { EntityData, PhysicsEntityType } from './types';\n\n/**\n * A container for managing collections of physics entities that move together.\n * Groups are useful for creating compound objects, moving platforms with riders,\n * and other scenarios where multiple entities need to move as one unit.\n *\n * Features:\n * - Maintains relative positioning of child entities\n * - Supports actors, solids, and sensors as children\n * - Preserves world positions when adding entities\n * - Provides type-safe access to children by entity type\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a moving platform with obstacles\n * const platformGroup = physics.createGroup({\n * type: 'MovingPlatform',\n * position: [100, 300]\n * });\n *\n * // Add platform and spikes\n * const platform = physics.createSolid({\n * type: 'Platform',\n * size: [200, 32]\n * });\n *\n * const spikes = physics.createSensor({\n * type: 'Spikes',\n * position: [0, -32],\n * size: [200, 32]\n * });\n *\n * platformGroup.add(platform);\n * platformGroup.add(spikes);\n *\n * // Move the entire group\n * gsap.to(platformGroup, {\n * x: 500,\n * duration: 2,\n * yoyo: true,\n * repeat: -1\n * });\n * ```\n */\nexport class Group<D extends EntityData = EntityData> extends Entity<D> {\n protected _visible: boolean = true;\n\n public entityType: PhysicsEntityType = 'Group';\n /** Map of child entities to their relative offsets from the group's position */\n private childOffsets: Map<Entity, { x: number; y: number }> = new Map();\n /** Whether this group's position is static (not affected by physics) */\n public isStatic: boolean = true;\n\n /**\n * Gets the relative offset of a child entity from the group's position.\n * @param entity - The child entity to get the offset for\n * @returns The relative {x, y} offset of the entity\n */\n public getChildOffset(entity: Entity): { x: number; y: number } {\n return this.childOffsets.get(entity) ?? { x: 0, y: 0 };\n }\n\n set visible(value: boolean) {\n this.system.getEntitiesInGroup(this).forEach((entity) => {\n entity.view.visible = value;\n });\n }\n\n get visible(): boolean {\n return this._visible;\n }\n\n /**\n * Sets the group's X position, affecting all child entities.\n */\n set x(value: number) {\n this._x = value;\n }\n\n get x(): number {\n return this._x;\n }\n\n /**\n * Sets the group's Y position, affecting all child entities.\n */\n set y(value: number) {\n this._y = value;\n }\n\n get y(): number {\n return this._y;\n }\n\n /**\n * Add an entity to this container\n * @param entity The entity to add\n * @param preserveWorldPosition If true, the entity's world position will be preserved\n */\n public add(entity: Entity, offset: { x: number; y: number } = { x: 0, y: 0 }): Group {\n entity.setGroup(this, offset);\n return this;\n }\n\n /**\n * Remove an entity from this container\n */\n public remove(entity: Entity): Group {\n entity.setGroup(null);\n return this;\n }\n\n /**\n * Move the container and all its children\n */\n public move(x: number, y: number): void {\n // Update container position\n this._x += x;\n this._y += y;\n\n this.updateView();\n }\n\n /**\n * Force move the container and all its children to an absolute position\n */\n public moveTo(x: number, y: number): void {\n const dx = x - this.x;\n const dy = y - this.y;\n this.move(dx, dy);\n }\n\n /**\n * Get all children of a specific type\n */\n public getChildrenByType<E extends Entity>(type: PhysicsEntityType): E[] {\n return this.system\n .getEntitiesInGroup(this)\n .filter((child): child is E => child.entityType === type || child.type === type);\n }\n\n /**\n * Get all actors in this container\n */\n public getActors(): Actor[] {\n return this.getChildrenByType('Actor');\n }\n\n /**\n * Get all solids in this container\n */\n public getSolids(): Solid[] {\n return this.getChildrenByType('Solid');\n }\n\n /**\n * Get all sensors in this container\n */\n public getSensors(): Sensor[] {\n return this.getChildrenByType('Sensor');\n }\n\n /**\n * Get all groups in this container\n */\n public getGroups(): Group[] {\n return this.getChildrenByType('Group');\n }\n\n public destroy(): void {\n this.system.removeGroup(this);\n super.destroy();\n }\n}\n","import { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Solid } from './Solid';\nimport { EntityData, PhysicsEntityConfig, PhysicsEntityType, SensorOverlap, Vector2 } from './types';\n\n/**\n * A trigger zone that can detect overlaps with actors.\n * Sensors are typically used for collectibles, triggers, and detection zones.\n *\n * Features:\n * - Overlap detection with specific actor types\n * - Optional gravity and movement\n * - Can be static or dynamic\n * - Callbacks for enter/exit events\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a coin pickup sensor\n * class Coin extends Sensor {\n * constructor() {\n * super({\n * type: 'Coin',\n * position: [100, 100],\n * size: [32, 32],\n * view: coinSprite\n * });\n *\n * // Only detect overlaps with player\n * this.collidableTypes = ['Player'];\n * }\n *\n * // Called when a player enters the coin\n * onActorEnter(actor: Actor) {\n * if (actor.type === 'Player') {\n * increaseScore(10);\n * this.physics.removeSensor(this);\n * }\n * }\n * }\n *\n * // Create a damage zone\n * class Spikes extends Sensor {\n * constructor() {\n * super({\n * type: 'Spikes',\n * position: [300, 500],\n * size: [100, 32],\n * view: spikesSprite\n * });\n *\n * this.collidableTypes = ['Player', 'Enemy'];\n * this.isStatic = true; // Don't move or fall\n * }\n *\n * onActorEnter(actor: Actor) {\n * if (actor.type === 'Player') {\n * actor.damage(10);\n * }\n * }\n * }\n * ```\n */\nexport class Sensor<D extends EntityData = EntityData> extends Entity<D> {\n public readonly entityType: PhysicsEntityType = 'Sensor';\n /** Whether this sensor should be removed when culled */\n public shouldRemoveOnCull = false;\n\n /** List of actor types this sensor can detect */\n public collidableTypes: string[] = [];\n\n /** Current velocity in pixels per second */\n public velocity: Vector2 = { x: 0, y: 0 };\n\n /** Whether this sensor should stay in place */\n public isStatic: boolean = false;\n\n /** Set of actors currently overlapping this sensor */\n private overlappingActors: Set<Actor> = new Set();\n\n /** Cache for isRidingSolid check */\n private _isRidingSolidCache: boolean | null = null;\n\n private _currentSensorOverlaps = new Set<SensorOverlap>();\n private _currentOverlaps = new Set<Actor>();\n\n public setPosition(x: number, y: number): void {\n if (this.isStatic) {\n this._x = x;\n this._y = y;\n this._xRemainder = 0;\n this._yRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n } else {\n super.setPosition(x, y);\n }\n }\n\n set x(value: number) {\n super.x = value;\n if (this.isStatic) {\n this._xRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n }\n }\n\n get x(): number {\n return super.x;\n }\n\n set y(value: number) {\n super.y = value;\n if (this.isStatic) {\n this._yRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n }\n }\n\n get y(): number {\n return super.y;\n }\n\n /**\n * Initializes or reinitializes the sensor with new configuration.\n *\n * @param config - Configuration for the sensor\n */\n public init(config: PhysicsEntityConfig<D>): void {\n super.init(config);\n if (!this.velocity) {\n this.velocity = { x: 0, y: 0 };\n }\n this.velocity.x = 0;\n this.velocity.y = 0;\n if (!this.overlappingActors) {\n this.overlappingActors = new Set();\n }\n this._isRidingSolidCache = null;\n }\n\n /**\n * Checks if this sensor is riding the given solid.\n * Takes into account gravity direction for proper riding detection.\n *\n * @param solid - The solid to check against\n * @returns True if riding the solid\n */\n public isRiding(solid: Solid): boolean {\n const gravityDirection = Math.sign(this.system.gravity);\n if (gravityDirection > 0) {\n // Normal gravity - check if we're on top of the solid\n const sensorBottom = this.y + this.height;\n const onTop = Math.abs(sensorBottom - solid.y) <= 1;\n const overlap = this.x + this.width > solid.x && this.x < solid.x + solid.width;\n return onTop && overlap;\n } else {\n // Reversed gravity - check if we're on bottom of the solid\n const sensorTop = this.y;\n const onBottom = Math.abs(sensorTop - (solid.y + solid.height)) <= 1;\n const overlap = this.x + this.width > solid.x && this.x < solid.x + solid.width;\n return onBottom && overlap;\n }\n }\n\n /**\n * Checks if this sensor is riding any solid.\n * Uses caching to optimize multiple checks per frame.\n *\n * @returns True if riding any solid\n */\n public isRidingSolid(): boolean {\n // Return cached value if available\n if (this._isRidingSolidCache !== null) {\n return this._isRidingSolidCache;\n }\n\n // Calculate and cache the result\n const solids = this.getSolidsAt(this.x, this.system.gravity > 0 ? this.y + 1 : this.y - 1);\n this._isRidingSolidCache = solids.some((solid) => this.isRiding(solid));\n return this._isRidingSolidCache;\n }\n\n /**\n * Force moves the sensor to a new position, ignoring static state and collisions.\n *\n * @param x - New X position\n * @param y - New Y position\n */\n public moveStatic(x: number, y: number): void {\n this._x = x;\n this._y = y;\n this._xRemainder = 0;\n this._yRemainder = 0;\n this.updateView();\n this.checkActorOverlaps();\n }\n\n /**\n * Moves the sensor horizontally, passing through solids.\n *\n * @param amount - Distance to move in pixels\n */\n public moveX(amount: number): void {\n if (this.isStatic) {\n return;\n }\n\n this._xRemainder += amount;\n const move = Math.round(this._xRemainder);\n\n if (move !== 0) {\n this._xRemainder -= move;\n const sign = Math.sign(move);\n let remaining = Math.abs(move);\n const candidates = this.getOverlapCandidates(move, 0);\n while (remaining > 0) {\n const step = sign;\n const nextX = this.x + step;\n\n // Check for collision with any solid\n let collided = false;\n for (const solid of this.getSolidsAt(nextX, this.y)) {\n if (solid.canCollide) {\n collided = true;\n break;\n }\n }\n\n if (!collided) {\n this._x = nextX;\n remaining--;\n this.updateView();\n this.checkActorOverlaps(candidates);\n } else {\n // Stop horizontal movement when hitting a solid\n this.velocity.x = 0;\n break;\n }\n }\n }\n }\n\n /**\n * Moves the sensor vertically, colliding with solids for riding.\n *\n * @param amount - Distance to move in pixels\n */\n public moveY(amount: number): void {\n if (this.isStatic) {\n return;\n }\n\n this._yRemainder += amount;\n const move = Math.round(this._yRemainder);\n\n if (move !== 0) {\n this._yRemainder -= move;\n const sign = Math.sign(move);\n\n let remaining = Math.abs(move);\n const candidates = this.getOverlapCandidates(0, move);\n while (remaining > 0) {\n const step = sign;\n const nextY = this.y + step;\n\n // Check for collision with any solid\n let collided = false;\n // Only check collisions when moving in the direction of gravity\n if (Math.sign(this.system.gravity) === sign) {\n for (const solid of this.getSolidsAt(this.x, nextY)) {\n if (solid.canCollide) {\n collided = true;\n break;\n }\n }\n }\n\n if (!collided) {\n this._y = nextY;\n remaining--;\n this.updateView();\n this.checkActorOverlaps(candidates);\n } else {\n // Stop vertical movement when landing on a solid\n this.velocity.y = 0;\n break;\n }\n }\n }\n }\n\n /**\n * Updates the sensor's position and checks for overlapping actors.\n *\n * @param deltaTime - Delta time in seconds\n */\n public update(deltaTime: number): void {\n // Reset the cache at the start of each update\n this._isRidingSolidCache = null;\n\n if (!this.active) {\n return;\n }\n\n // Only apply gravity if not static and not riding a solid\n if (!this.isStatic && !this.isRidingSolid()) {\n this.velocity.y += this.system.gravity * deltaTime;\n }\n\n // Clamp velocity, as Actor does. Without it a sensor with nothing beneath it\n // accelerates forever, and every extra pixel per frame is another pass through\n // the move loops below.\n const maxVelocity = this.system.maxVelocity;\n this.velocity.x = Math.min(Math.max(this.velocity.x, -maxVelocity), maxVelocity);\n this.velocity.y = Math.min(Math.max(this.velocity.y, -maxVelocity), maxVelocity);\n\n // Move\n if (this.velocity.x !== 0) {\n this.moveX(this.velocity.x * deltaTime);\n }\n if (this.velocity.y !== 0) {\n this.moveY(this.velocity.y * deltaTime);\n }\n }\n\n /**\n * The only actors a move of (dx, dy) pixels can start or stop overlapping: those\n * touching the area swept by the move, plus the ones overlapping right now (they\n * may be left behind). Built once per move so the per-pixel loop scans a handful\n * of actors instead of rebuilding and walking the whole list for every pixel.\n */\n private getOverlapCandidates(dx: number, dy: number): Set<Actor> {\n const candidates = new Set<Actor>(this.overlappingActors);\n const left = Math.min(this.x, this.x + dx);\n const top = Math.min(this.y, this.y + dy);\n const right = Math.max(this.x, this.x + dx) + this.width;\n const bottom = Math.max(this.y, this.y + dy) + this.height;\n\n for (const actor of this.system.getActorsByType(this.collidableTypes)) {\n if (actor.x < right && actor.x + actor.width > left && actor.y < bottom && actor.y + actor.height > top) {\n candidates.add(actor);\n }\n }\n return candidates;\n }\n\n /**\n * Checks for overlapping actors and triggers callbacks.\n *\n * @param candidates - Restrict the scan to these actors (see getOverlapCandidates).\n * Omit for a full scan of every actor of the collidable types.\n * @returns Set of current overlaps\n */\n public checkActorOverlaps(candidates?: Iterable<Actor>): Set<SensorOverlap> {\n // Skip if sensor has no collision mask or is inactive\n if (this.collisionMask === 0 || !this.active) {\n return new Set();\n }\n\n this._currentSensorOverlaps.clear();\n this._currentOverlaps.clear();\n\n // Get all actors at current position - use spatial filtering first\n // Only get actors that are potentially in the same grid cells\n\n // Cache collision layer and mask for faster access\n const sensorLayer = this.collisionLayer;\n const sensorMask = this.collisionMask;\n\n // Get actors by type, but only process those that could be nearby\n const nearbyActors = candidates ?? this.system.getActorsByType(this.collidableTypes);\n\n for (const actor of nearbyActors) {\n // Skip inactive actors\n if (!actor.active) continue;\n\n // Fast collision layer check\n if ((sensorLayer & actor.collisionMask) === 0 || (actor.collisionLayer & sensorMask) === 0) {\n continue;\n }\n\n // Fast AABB check before detailed overlap\n if (this.system.aabbOverlap(this, actor)) {\n this._currentOverlaps.add(actor);\n if (!this.overlappingActors.has(actor)) {\n // New overlap\n this._currentSensorOverlaps.add({\n actor,\n sensor: this,\n type: `${actor.type}|${this.type}`,\n });\n\n this.onActorEnter(actor);\n }\n }\n }\n\n // Check for actors that are no longer overlapping\n for (const actor of this.overlappingActors) {\n if (!this._currentOverlaps.has(actor)) {\n this.onActorExit(actor);\n }\n }\n\n // Swap the sets to avoid unnecessary clear and forEach operations\n const temp = this.overlappingActors;\n this.overlappingActors = this._currentOverlaps;\n this._currentOverlaps = temp;\n this._currentOverlaps.clear();\n\n return this._currentSensorOverlaps;\n }\n\n public reset(): void {\n super.reset();\n this._currentSensorOverlaps.clear();\n this._currentOverlaps.clear();\n this._isRidingSolidCache = null;\n this.velocity = { x: 0, y: 0 };\n this.overlappingActors = new Set();\n }\n\n /**\n * Called when an actor starts overlapping with this sensor.\n * Override this to handle overlap start events.\n *\n * @param actor - The actor that entered\n */\n public onActorEnter<A extends Actor = Actor>(actor: A): void {\n // Override in subclass\n void actor;\n }\n\n /**\n * Called when an actor stops overlapping with this sensor.\n * Override this to handle overlap end events.\n *\n * @param actor - The actor that exited\n */\n public onActorExit<A extends Actor = Actor>(actor: A): void {\n // Override in subclass\n void actor;\n }\n\n /**\n * Gets all solids at the specified position.\n *\n * @param x - X position to check\n * @param y - Y position to check\n * @returns Array of solids at the position\n */\n protected getSolidsAt(x: number, y: number): Solid[] {\n return this.system.getSolidsAt(x, y, this);\n }\n}\n","import { Actor } from './Actor';\nimport { Entity } from './Entity';\nimport { Sensor } from './Sensor';\nimport { EntityData, PhysicsEntityConfig, PhysicsEntityType } from './types';\n\n/**\n * Static or moving solid object that other entities can collide with.\n * Solids are typically used for platforms, walls, and other obstacles.\n *\n * Features:\n * - Static collision boundaries\n * - Support for moving platforms\n * - Carries actors and sensors that are riding it\n * - Pushes overlapping entities out of the way\n *\n * @typeParam T - Application type, defaults to base Application\n *\n * @example\n * ```typescript\n * // Create a static platform\n * const platform = physics.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32],\n * view: platformSprite\n * });\n *\n * // Create a moving platform\n * const movingPlatform = physics.createSolid({\n * type: 'Platform',\n * position: [100, 300],\n * size: [200, 32],\n * view: platformSprite\n * });\n *\n * // Move the platform back and forth\n * gsap.to(movingPlatform, {\n * x: 500,\n * duration: 2,\n * yoyo: true,\n * repeat: -1\n * });\n * ```\n */\nexport class Solid<D extends EntityData = EntityData> extends Entity<D> {\n public readonly entityType: PhysicsEntityType = 'Solid';\n /** Whether this solid should be removed when culled (typically false) */\n public shouldRemoveOnCull = false;\n\n /** Whether this solid can be collided with */\n private _canCollide: boolean = true;\n\n get canCollide(): boolean {\n return this._canCollide;\n }\n\n /** Whether this solid has collisions enabled */\n public collideable: boolean = true;\n\n /** Whether this solid is currently moving */\n public moving: boolean = false;\n\n private _nextX: number;\n private _nextY: number;\n\n public setPosition(x: number, y: number): void {\n this.x = x;\n this.y = y;\n }\n\n /**\n * Sets the solid's X position.\n * For moving solids, this queues the movement to be applied on next update.\n */\n public set x(value: number) {\n this._nextX = Math.round(value);\n this.moving = true;\n }\n\n public get x(): number {\n return this._x;\n }\n\n /**\n * Sets the solid's Y position.\n * For moving solids, this queues the movement to be applied on next update.\n */\n public set y(value: number) {\n this._nextY = Math.round(value);\n this.moving = true;\n }\n\n public get y(): number {\n return this._y;\n }\n\n /**\n * Initializes or reinitializes the solid with new configuration.\n *\n * @param config - Configuration for the solid\n */\n public init(config: PhysicsEntityConfig<D>): void {\n super.init(config);\n if (config) {\n this._nextX = this._x;\n this._nextY = this._y;\n }\n }\n\n /** Right edge X coordinate */\n public get right(): number {\n return this.x + this.width;\n }\n\n /** Left edge X coordinate */\n public get left(): number {\n return this.x;\n }\n\n /** Top edge Y coordinate */\n public get top(): number {\n return this.y;\n }\n\n /** Bottom edge Y coordinate */\n public get bottom(): number {\n return this.y + this.height;\n }\n\n /**\n * Checks if this solid can collide with the given entity type.\n * This method is kept for backward compatibility.\n *\n * @returns Always returns true as we're now using only collision layers/masks\n */\n canCollideWith(): boolean {\n // First check if collisions are enabled at all\n if (!this.collideable) {\n return false;\n }\n\n // We're now using only collision layers/masks, so this method is simplified\n return true;\n }\n\n /**\n * Moves the solid by the specified amount, carrying any riding entities.\n * Also pushes any overlapping entities out of the way.\n *\n * @param x - X distance to move\n * @param y - Y distance to move\n * @param actors - Set of actors to check for riding/pushing\n * @param sensors - Set of sensors to check for riding/pushing\n */\n public move(\n x: number,\n y: number,\n actors: Set<Actor> = this.system.actors,\n sensors: Set<Sensor> = this.system.sensors,\n force: boolean = false,\n ): void {\n if (!this.active) {\n return;\n }\n\n // Calculate total movement including remainder\n const totalX = x + (this._nextX - this._x);\n const totalY = y + (this._nextY - this._y);\n\n this._xRemainder += totalX;\n this._yRemainder += totalY;\n\n const moveX = Math.round(this._xRemainder);\n const moveY = Math.round(this._yRemainder);\n\n if (moveX !== 0 || moveY !== 0 || force) {\n if (this.collideable) {\n // Get all riding actors and sensors before movement\n const ridingActors = new Set<Actor>();\n const ridingSensors = new Set<Sensor>();\n\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0 &&\n actor.isRiding(this)\n ) {\n ridingActors.add(actor);\n }\n }\n\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0 &&\n sensor.isRiding(this)\n ) {\n ridingSensors.add(sensor);\n }\n }\n\n // Make this solid non-collidable so actors don't get stuck\n this._canCollide = false;\n\n if (moveX !== 0) {\n this._xRemainder -= moveX;\n this._x += moveX;\n\n if (moveX > 0) {\n // Moving right\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push right\n actor.moveX(this.right - actor.x, actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry right\n actor.moveX(moveX);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push right\n sensor.moveX(this.right - sensor.x);\n } else if (ridingSensors.has(sensor)) {\n // Carry right\n sensor.moveX(moveX);\n }\n }\n } else {\n // Moving left\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push left\n actor.moveX(this.left - (actor.x + actor.width), actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry left\n actor.moveX(moveX);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push left\n sensor.moveX(this.left - (sensor.x + sensor.width));\n } else if (ridingSensors.has(sensor)) {\n // Carry left\n sensor.moveX(moveX);\n }\n }\n }\n }\n\n if (moveY !== 0) {\n this._yRemainder -= moveY;\n this._y += moveY;\n\n if (moveY > 0) {\n // Moving down\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push down\n actor.moveY(this.bottom - actor.y, actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry down\n actor.moveY(moveY);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push down\n sensor.moveY(this.bottom - sensor.y);\n } else if (ridingSensors.has(sensor)) {\n // Carry down\n sensor.moveY(moveY);\n }\n }\n } else {\n // Moving up\n for (const actor of actors) {\n // Check if actor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(actor) &&\n (actor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & actor.collisionMask) !== 0\n ) {\n // Push up\n actor.moveY(this.top - (actor.y + actor.height), actor.squish, this);\n } else if (ridingActors.has(actor)) {\n // Carry up\n actor.moveY(moveY);\n }\n }\n for (const sensor of sensors) {\n // Check if sensor can collide with this solid based on collision layers/masks\n if (\n this.overlaps(sensor) &&\n (sensor.collisionLayer & this.collisionMask) !== 0 &&\n (this.collisionLayer & sensor.collisionMask) !== 0\n ) {\n // Push up\n sensor.moveY(this.top - (sensor.y + sensor.height));\n } else if (ridingSensors.has(sensor)) {\n // Carry up\n sensor.moveY(moveY);\n }\n }\n }\n }\n\n // Re-enable collisions\n this._canCollide = true;\n } else {\n // When collisions are disabled, just move without affecting other entities\n if (moveX !== 0) {\n this._xRemainder -= moveX;\n this._x += moveX;\n }\n if (moveY !== 0) {\n this._yRemainder -= moveY;\n this._y += moveY;\n }\n }\n\n // Update next positions to match current\n this._nextX = this._x;\n this._nextY = this._y;\n\n // Update the view position\n this.updateView();\n }\n }\n\n /**\n * Checks if this solid overlaps with the given entity.\n *\n * @param entity - Entity to check for overlap\n * @returns True if overlapping\n */\n private overlaps(entity: Actor | Sensor): boolean {\n // Check collision layers and masks\n // A collision occurs when (A.layer & B.mask) !== 0 && (B.layer & A.mask) !== 0\n if ((this.collisionLayer & entity.collisionMask) === 0 || (entity.collisionLayer & this.collisionMask) === 0) {\n return false;\n }\n\n // Check for AABB overlap\n return this.system.aabbOverlap(this, entity);\n }\n}\n","import { Container, Graphics } from 'pixi.js';\nimport { Actor } from './Actor';\nimport CrunchPhysicsPlugin from './CrunchPhysicsPlugin';\nimport { Entity } from './Entity';\nimport { Group } from './Group';\nimport { AABBLike } from './interfaces';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\nimport {\n ActorCollision,\n ActorCollisionResult,\n Collision,\n PhysicsEntityConfig,\n PhysicsEntityView,\n Rectangle,\n SensorOverlap,\n} from './types';\n\n/**\n * Configuration options for the Crunch physics system.\n * These options control the core behavior of the physics simulation.\n *\n * @example\n * ```typescript\n * const options: PhysicsSystemOptions = {\n * gridSize: 32,\n * gravity: 980,\n * maxVelocity: 1000,\n * debug: true,\n * boundary: { x: 0, y: 0, width: 800, height: 600 },\n * culling: true,\n * collisionResolver: (collisions) => {\n * for (const collision of collisions) {\n * handleCollision(collision);\n * }\n * }\n * };\n * ```\n */\nexport interface PhysicsSystemOptions {\n /** Reference to the parent Crunch physics plugin */\n plugin: CrunchPhysicsPlugin;\n /** Size of each grid cell for spatial partitioning (in pixels) */\n gridSize: number;\n /** Gravity strength in pixels per second squared */\n gravity: number;\n /** Maximum velocity for any entity in pixels per second */\n maxVelocity: number;\n /** Whether to render debug visualizations */\n debug?: boolean;\n /** World boundary for culling entities */\n boundary?: Rectangle;\n /** Whether to automatically cull out-of-bounds entities */\n culling?: boolean;\n /** Custom handler for resolving collisions */\n collisionResolver?: (collisions: Collision[]) => void;\n /** Custom handler for resolving sensor overlaps */\n overlapResolver?: (overlaps: SensorOverlap[]) => void;\n /** Custom handler for resolving actor-to-actor collisions */\n actorCollisionResolver?: (collisions: ActorCollision[]) => void;\n /** Whether to enable actor-to-actor collisions */\n enableActorCollisions?: boolean;\n /** Default collision layer for new entities */\n defaultCollisionLayer?: number;\n /** Default collision mask for new entities */\n defaultCollisionMask?: number;\n}\n\n/**\n * Represents a cell in the spatial partitioning grid that can contain both solids and actors\n */\ninterface GridCell {\n solids: Set<Solid>;\n actors: Set<Actor>;\n}\n\n/**\n * Core physics system that manages all physics entities and their interactions.\n * Handles spatial partitioning, collision detection, and entity lifecycle.\n *\n * Features:\n * - Grid-based spatial partitioning for efficient collision checks\n * - Entity management (actors, solids, sensors, groups)\n * - Collision and overlap detection\n * - Debug visualization\n * - Culling system for out-of-bounds entities\n *\n * @example\n * ```typescript\n * // Create the physics system\n * const system = new System({\n * gridSize: 32,\n * gravity: 980,\n * maxVelocity: 1000\n * });\n *\n * // Add entities\n * const player = system.createActor({\n * type: 'Player',\n * position: [100, 100]\n * });\n *\n * const platform = system.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32]\n * });\n *\n * // Update physics (in game loop)\n * system.update(deltaTime);\n * ```\n */\nexport class System {\n private readonly options: PhysicsSystemOptions;\n // Public collections\n public entities: Set<Entity> = new Set();\n private _flaggedEntities: Set<Entity> = new Set();\n // Type-based lookup maps\n public actors: Set<Actor> = new Set();\n public solids: Set<Solid> = new Set();\n public sensors: Set<Sensor> = new Set();\n public groups: Set<Group> = new Set();\n // Type-based lookup maps\n private actorsByType: Map<string, Set<Actor>> = new Map();\n private solidsByType: Map<string, Set<Solid>> = new Map();\n private sensorsByType: Map<string, Set<Sensor>> = new Map();\n private groupsByType: Map<string, Set<Group>> = new Map();\n\n // Followers tracking\n private followers: Map<Entity, Set<Entity>> = new Map();\n\n // groups tracking\n private groupWithEntities: Map<Group, Set<Entity>> = new Map();\n\n // Single grid for both solids and actors\n private grid: Map<string, GridCell> = new Map();\n\n // Collision tracking\n private collisions: Collision[] = [];\n private sensorOverlaps: SensorOverlap[] = [];\n private actorCollisions: ActorCollision[] = [];\n\n // Reusable data structures for actor collision detection\n private _checkedPairs: Set<string> = new Set();\n\n // Object pools for collision results to reduce GC pressure\n private _collisionResultPool: ActorCollisionResult[] = [];\n private _collisionResultPoolIndex: number = 0;\n private _actorCollisionPool: ActorCollision[] = [];\n private _actorCollisionPoolIndex: number = 0;\n\n // Debugging\n private _debugContainer: Container;\n private _debugGfx: Graphics | null = null;\n private _debug: boolean = false;\n\n private _movedActors: Set<Actor> = new Set();\n private _activeGridCells: Set<string> = new Set();\n private _potentialCollisions: Map<string, [Actor, Actor]> = new Map();\n\n set debug(value: boolean) {\n this._debug = value;\n\n if (this._debug) {\n if (!this._debugContainer) {\n this._debugContainer = this.options.plugin.container.addChild(new Container());\n }\n if (!this._debugGfx) {\n this._debugGfx = new Graphics();\n }\n this._debugContainer.addChild(this._debugGfx);\n } else {\n this._debugGfx?.clear();\n this._debugContainer?.removeChildren();\n }\n }\n\n set gridSize(value: number) {\n this.options.gridSize = value;\n this.grid.clear();\n\n for (const solid of this.solids) {\n this.addSolidToGrid(solid);\n }\n }\n\n set gravity(value: number) {\n this.options.gravity = value;\n }\n\n get gravity(): number {\n return this.options.gravity;\n }\n\n set maxVelocity(value: number) {\n this.options.maxVelocity = value;\n }\n\n get maxVelocity(): number {\n return this.options.maxVelocity;\n }\n\n set boundary(value: Rectangle) {\n this.options.boundary = value;\n }\n\n get boundary(): Rectangle {\n return this.options.boundary!;\n }\n\n get container(): Container {\n return this.options.plugin.container;\n }\n\n public addView(view: PhysicsEntityView): void {\n this.container.addChild(view);\n }\n\n constructor(options: PhysicsSystemOptions) {\n this.options = {\n ...options,\n culling: options.culling ?? false,\n };\n this.debug = options.debug ?? false;\n this._debugContainer = new Container();\n options.plugin.container.addChild(this._debugContainer);\n }\n\n private _resetPositionFlags(entity: Entity): void {\n entity.updatedFollowPosition = false;\n entity.updatedGroupPosition = false;\n }\n\n public update(dt: number): void {\n if (!this.options.plugin.enabled) return;\n\n // Clear collections at the start with pre-allocated capacity\n this.collisions.length = 0;\n this.sensorOverlaps.length = 0;\n this.actorCollisions.length = 0;\n\n // Reset object pools\n this.resetCollisionPools();\n\n // Convert delta time to seconds (cache this calculation)\n const deltaTime = dt / 60;\n\n // Update containers first (groups)\n for (const group of this.groups) {\n group.update(deltaTime);\n }\n\n // Update solids with optimized grid management\n this.updateSolids(deltaTime);\n\n // Update sensors (before actors so they can detect entry/exit in the same frame)\n for (const sensor of this.sensors) {\n this.updateSensor(sensor, deltaTime);\n }\n\n // Clear actor collision lists before updating actors\n for (const actor of this.actors) {\n if (actor.actorCollisions.length > 0) {\n actor.actorCollisions.length = 0;\n }\n }\n\n // Update actors with potential batching\n this.updateActors(deltaTime);\n\n // Process position updates for followers and group entities\n this.updateEntityPositions();\n\n // Process overlaps, collisions, and culling in batch operations\n this.processCollisionsAndOverlaps();\n\n // Cull out-of-bounds entities if enabled and boundary is set\n if (this.options.culling && this.options.boundary) {\n this.cullOutOfBounds();\n }\n\n // Update debug rendering if enabled\n if (this._debug) {\n this.debugRender();\n }\n }\n\n // New optimized methods to break up the update loop\n private updateSolids(deltaTime: number): void {\n // Fast path if no solids are moving\n let hasMovingSolids = false;\n\n for (const solid of this.solids) {\n solid.preUpdate();\n solid.update(deltaTime);\n solid.postUpdate();\n\n if (solid.moving) {\n hasMovingSolids = true;\n }\n }\n\n // Skip grid updates if nothing is moving\n if (!hasMovingSolids) return;\n\n // Batch grid updates for moving solids\n const movedSolids = new Set<Solid>();\n\n for (const solid of this.solids) {\n if (solid.moving) {\n // Remove from old grid cells\n this.removeSolidFromGrid(solid);\n // Move the solid (which will handle pushing/carrying actors and sensors)\n solid.move(0, 0, this.actors, this.sensors, true);\n // Track for batch grid update\n movedSolids.add(solid);\n }\n }\n\n // Batch add to grid after all movements are complete\n for (const solid of movedSolids) {\n this.addSolidToGrid(solid);\n }\n }\n\n private updateActors(deltaTime: number): void {\n // Skip if no active actors\n if (this.actors.size === 0) return;\n\n // Use for...of instead of forEach for better performance\n for (const actor of this.actors) {\n if (actor.active) {\n actor.preUpdate();\n actor.update(deltaTime);\n actor.postUpdate();\n\n // Collect collisions in batch\n const actorCollisions = actor.collisions;\n if (actorCollisions.length > 0) {\n for (const result of actorCollisions) {\n this.collisions.push({\n type: `${actor.type}|${result.solid!.type}`,\n entity1: actor,\n entity2: result.solid!,\n result: {\n collided: result.collided,\n normal: result.normal,\n penetration: result.penetration,\n solid: result.solid,\n },\n });\n }\n }\n }\n }\n }\n\n private updateEntityPositions(): void {\n this._flaggedEntities.clear();\n\n // Process followers first (most dependent)\n for (const [entity, followers] of this.followers) {\n const followX = entity.x;\n const followY = entity.y;\n\n for (const follower of followers) {\n if (!follower.updatedFollowPosition) {\n // Cache group checks to avoid repeated property access\n const hasGroup = follower.group !== null;\n const sameGroup = hasGroup && follower.group === entity.group;\n\n if (sameGroup) {\n // Get the entity's position relative to its group using the offset\n const entityGroupOffset = entity.groupOffset;\n const groupX = follower.group!.x;\n const groupY = follower.group!.y;\n\n // Set position using combined offsets relative to group\n follower.setPosition(\n groupX + entityGroupOffset.x + follower.followOffset.x,\n groupY + entityGroupOffset.y + follower.followOffset.y,\n );\n } else if (hasGroup) {\n // Different groups or followed entity not in a group\n follower.setPosition(\n followX + follower.followOffset.x + follower.group!.x + follower.groupOffset.x,\n followY + follower.followOffset.y + follower.group!.y + follower.groupOffset.y,\n );\n } else {\n // No group, just following\n follower.setPosition(followX + follower.followOffset.x, followY + follower.followOffset.y);\n }\n\n follower.updatedFollowPosition = true;\n this._flaggedEntities.add(follower);\n\n if (hasGroup) {\n follower.updatedGroupPosition = true;\n }\n }\n }\n }\n\n // Process remaining group entities\n for (const [group, groupEntities] of this.groupWithEntities) {\n const groupX = group.x;\n const groupY = group.y;\n\n for (const entity of groupEntities) {\n if (!entity.updatedGroupPosition && !entity.updatedFollowPosition) {\n entity.setPosition(groupX + entity.groupOffset.x, groupY + entity.groupOffset.y);\n entity.updatedGroupPosition = true;\n this._flaggedEntities.add(entity);\n }\n }\n }\n\n // Reset flags in batch\n this._flaggedEntities.forEach(this._resetPositionFlags);\n }\n\n private processCollisionsAndOverlaps(): void {\n // Process overlaps if resolver is set and we have overlaps\n if (this.options.overlapResolver && this.sensorOverlaps.length > 0) {\n this.options.overlapResolver(this.sensorOverlaps);\n }\n\n // Check actor-to-actor collisions if enabled and we have multiple actors\n if (this.options.enableActorCollisions && this.actors.size > 1) {\n this.checkActorCollisions();\n }\n\n // Process collisions if resolver is set and we have collisions\n if (this.collisions.length > 0 && this.options.collisionResolver) {\n this.options.collisionResolver(this.collisions);\n }\n\n // Process actor-to-actor collisions if resolver is set and we have collisions\n if (this.actorCollisions.length > 0 && this.options.actorCollisionResolver) {\n this.options.actorCollisionResolver(this.actorCollisions);\n }\n }\n\n // Optimize the cullOutOfBounds method\n private cullOutOfBounds(): void {\n // Skip if no boundary\n if (!this.options.boundary) return;\n\n const boundary = this.options.boundary;\n // Pre-allocate removal arrays with reasonable capacity\n const toRemoveActors: Actor[] = [];\n const toRemoveSolids: Solid[] = [];\n const toRemoveSensors: Sensor[] = [];\n const toRemoveGroups: Group[] = [];\n\n // Cache boundary values to avoid repeated property access\n const boundX = boundary.x;\n const boundY = boundary.y;\n const boundRight = boundX + boundary.width;\n const boundBottom = boundY + boundary.height;\n\n // Check actors with optimized bounds check\n for (const actor of this.actors) {\n const actorRight = actor.x + actor.width;\n const actorBottom = actor.y + actor.height;\n\n const inBounds = !(\n actor.x >= boundRight || // Completely to the right\n actorRight <= boundX || // Completely to the left\n actor.y >= boundBottom || // Completely below\n actorBottom <= boundY // Completely above\n );\n\n if (!inBounds) {\n if (!actor.isCulled) {\n actor.onCull();\n }\n if (actor.shouldRemoveOnCull) {\n toRemoveActors.push(actor);\n }\n } else if (actor.isCulled) {\n // Uncull if back in bounds\n actor.onUncull();\n }\n }\n\n // Check solids with the same optimized bounds check\n for (const solid of this.solids) {\n const solidRight = solid.x + solid.width;\n const solidBottom = solid.y + solid.height;\n\n const inBounds = !(\n solid.x >= boundRight ||\n solidRight <= boundX ||\n solid.y >= boundBottom ||\n solidBottom <= boundY\n );\n\n if (!inBounds) {\n if (!solid.isCulled) {\n solid.onCull();\n }\n if (solid.shouldRemoveOnCull) {\n toRemoveSolids.push(solid);\n }\n } else if (solid.isCulled) {\n solid.onUncull();\n }\n }\n\n // Check sensors with the same optimized bounds check\n for (const sensor of this.sensors) {\n const sensorRight = sensor.x + sensor.width;\n const sensorBottom = sensor.y + sensor.height;\n\n const inBounds = !(\n sensor.x >= boundRight ||\n sensorRight <= boundX ||\n sensor.y >= boundBottom ||\n sensorBottom <= boundY\n );\n\n if (!inBounds) {\n if (!sensor.isCulled) {\n sensor.onCull();\n }\n if (sensor.shouldRemoveOnCull) {\n toRemoveSensors.push(sensor);\n }\n } else if (sensor.isCulled) {\n sensor.onUncull();\n }\n }\n\n // Check groups with the same optimized bounds check\n for (const group of this.groups) {\n const groupRight = group.x + group.width;\n const groupBottom = group.y + group.height;\n\n const inBounds = !(\n group.x >= boundRight ||\n groupRight <= boundX ||\n group.y >= boundBottom ||\n groupBottom <= boundY\n );\n\n if (!inBounds) {\n if (!group.isCulled) {\n group.onCull();\n }\n if (group.shouldRemoveOnCull) {\n toRemoveGroups.push(group);\n }\n } else if (group.isCulled) {\n group.onUncull();\n }\n }\n\n // Batch remove culled entities if any exist\n const actorCount = toRemoveActors.length;\n const solidCount = toRemoveSolids.length;\n const sensorCount = toRemoveSensors.length;\n const groupCount = toRemoveGroups.length;\n\n if (actorCount + solidCount + sensorCount + groupCount === 0) return;\n\n // Use for loops instead of forEach for better performance\n for (let i = 0; i < actorCount; i++) {\n this.removeActor(toRemoveActors[i]);\n }\n\n for (let i = 0; i < solidCount; i++) {\n this.removeSolid(toRemoveSolids[i]);\n }\n\n for (let i = 0; i < sensorCount; i++) {\n this.removeSensor(toRemoveSensors[i]);\n }\n\n for (let i = 0; i < groupCount; i++) {\n this.removeGroup(toRemoveGroups[i]);\n }\n }\n\n private updateSensor(sensor: Sensor, dt: number): void {\n sensor.preUpdate();\n sensor.update(dt);\n sensor.postUpdate();\n\n const overlaps = sensor.checkActorOverlaps();\n this.sensorOverlaps.push(...overlaps);\n }\n\n public createEntity(config: PhysicsEntityConfig): Actor | Solid | Sensor | Group {\n if (config.type === 'actor') {\n return this.createActor(config);\n } else if (config.type === 'solid') {\n return this.createSolid(config);\n } else if (config.type === 'sensor') {\n return this.createSensor(config);\n } else if (config.type === 'group') {\n return this.createGroup(config);\n }\n throw new Error(`Invalid entity type: ${config.type}`);\n }\n\n public addEntity(entity: Entity | Actor | Solid | Sensor | Group): Actor | Solid | Sensor | Group {\n if (!entity || !entity.entityType) {\n throw new Error('Entity is required');\n }\n if (entity.entityType === 'Actor') {\n return this.addActor(entity as Actor);\n } else if (entity.entityType === 'Solid') {\n return this.addSolid(entity as Solid);\n } else if (entity.entityType === 'Sensor') {\n return this.addSensor(entity as Sensor);\n } else if (entity.entityType === 'Group') {\n return this.addGroup(entity as Group);\n }\n throw new Error(`Invalid entity type: ${entity!.entityType}`);\n }\n\n public removeEntity(entity: Entity | Actor | Solid | Sensor | Group): void {\n if (entity.entityType === 'Actor') {\n this.removeActor(entity as Actor);\n } else if (entity.entityType === 'Solid') {\n this.removeSolid(entity as Solid);\n } else if (entity.entityType === 'Sensor') {\n this.removeSensor(entity as Sensor);\n } else if (entity.entityType === 'Group') {\n this.removeGroup(entity as Group);\n }\n }\n\n public createActor(config: PhysicsEntityConfig): Actor {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const actor = config.class ? (new config.class(config) as Actor) : new Actor(config);\n return this.addActor(actor);\n }\n\n public addActor(actor: Actor): Actor {\n this.entities.add(actor);\n this.actors.add(actor);\n\n // Add to type index\n if (!this.actorsByType.has(actor.type)) {\n this.actorsByType.set(actor.type, new Set());\n }\n this.actorsByType.get(actor.type)!.add(actor);\n\n // Add to actor grid if actor collisions are enabled\n if (this.options.enableActorCollisions) {\n actor.updateGridCells();\n }\n\n return actor;\n }\n\n public createSensor(config: PhysicsEntityConfig): Sensor {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const sensor = config.class ? (new config.class(config) as Sensor) : new Sensor(config);\n return this.addSensor(sensor);\n }\n\n public addSensor(sensor: Sensor): Sensor {\n this.entities.add(sensor);\n this.sensors.add(sensor);\n // Add to type index\n if (!this.sensorsByType.has(sensor.type)) {\n this.sensorsByType.set(sensor.type, new Set());\n }\n this.sensorsByType.get(sensor.type)!.add(sensor);\n return sensor;\n }\n\n public createSolid(config: PhysicsEntityConfig): Solid {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const solid = config.class ? (new config.class(config) as Solid) : new Solid(config);\n return this.addSolid(solid);\n }\n\n public addSolid(solid: Solid): Solid {\n this.entities.add(solid);\n this.solids.add(solid);\n // Add to type index\n if (!this.solidsByType.has(solid.type)) {\n this.solidsByType.set(solid.type, new Set());\n }\n this.solidsByType.get(solid.type)!.add(solid);\n // Add to spatial grid\n this.addSolidToGrid(solid);\n\n return solid;\n }\n\n public removeActor(actor: Actor, destroyView: boolean = true): void {\n this.entities.delete(actor);\n this.actors.delete(actor);\n // Remove from type index\n const typeSet = this.actorsByType.get(actor.type);\n if (typeSet) {\n typeSet.delete(actor);\n if (typeSet.size === 0) {\n this.actorsByType.delete(actor.type);\n }\n }\n actor.onRemoved();\n\n if (destroyView) {\n actor.view?.removeFromParent();\n }\n\n // Remove from actor grid\n if (this.options.enableActorCollisions) {\n this.removeActorFromGrid(actor);\n }\n }\n\n public removeSolid(solid: Solid, destroyView: boolean = true): void {\n this.entities.delete(solid);\n this.solids.delete(solid);\n // Remove from type index\n const typeSet = this.solidsByType.get(solid.type);\n if (typeSet) {\n typeSet.delete(solid);\n if (typeSet.size === 0) {\n this.solidsByType.delete(solid.type);\n }\n }\n this.removeSolidFromGrid(solid);\n\n if (destroyView) {\n solid.view?.removeFromParent();\n }\n\n solid.onRemoved();\n }\n\n public removeSensor(sensor: Sensor, destroyView: boolean = true): void {\n this.entities.delete(sensor);\n this.sensors.delete(sensor);\n // Remove from type index\n const typeSet = this.sensorsByType.get(sensor.type);\n if (typeSet) {\n typeSet.delete(sensor);\n if (typeSet.size === 0) {\n this.sensorsByType.delete(sensor.type);\n }\n }\n\n sensor.onRemoved();\n\n if (destroyView) {\n sensor.view?.removeFromParent();\n }\n }\n\n public moveSolid(solid: Solid, x: number, y: number): void {\n // Remove from old grid cells\n this.removeSolidFromGrid(solid);\n\n // Move the solid (which will handle pushing/carrying actors)\n solid.move(x, y, this.actors, this.sensors);\n\n // Add to new grid cells\n this.addSolidToGrid(solid);\n\n solid.updateView();\n }\n\n public getSolidsAt(x: number, y: number, entity: Actor | Sensor): Solid[] {\n // Early return if entity has no collision mask\n if (entity.collisionMask === 0) return [];\n\n const bounds = {\n x,\n y,\n width: entity.width,\n height: entity.height,\n };\n\n // Calculate movement direction from current position to check position\n const dx = x - entity.x;\n const dy = y - entity.y;\n\n // Get the base cells that the bounds intersect\n const cells = this.getCells(bounds);\n\n // Add one extra cell in the direction of movement\n if (dx !== 0) {\n const extraX =\n dx > 0 ? Math.ceil((x + bounds.width) / this.options.gridSize) : Math.floor(x / this.options.gridSize) - 1;\n\n for (\n let y = Math.floor(bounds.y / this.options.gridSize);\n y < Math.ceil((bounds.y + bounds.height) / this.options.gridSize);\n y++\n ) {\n cells.push(`${extraX},${y}`);\n }\n }\n\n if (dy !== 0) {\n const extraY =\n dy > 0 ? Math.ceil((y + bounds.height) / this.options.gridSize) : Math.floor(y / this.options.gridSize) - 1;\n\n for (\n let x = Math.floor(bounds.x / this.options.gridSize);\n x < Math.ceil((bounds.x + bounds.width) / this.options.gridSize);\n x++\n ) {\n cells.push(`${x},${extraY}`);\n }\n }\n\n // Create a Set of excluded types for O(1) lookups\n // Pre-allocate result array with a reasonable size to avoid resizing\n const result: Solid[] = [];\n const seen = new Set<Solid>();\n\n // Cache entity collision layer and mask for faster access\n const entityLayer = entity.collisionLayer;\n const entityMask = entity.collisionMask;\n\n for (const cell of cells) {\n const gridCell = this.grid.get(cell);\n if (gridCell) {\n for (const solid of gridCell.solids) {\n // Skip already seen solids\n if (seen.has(solid)) continue;\n\n // Fast collision layer check\n if ((entityLayer & solid.collisionMask) === 0 || (solid.collisionLayer & entityMask) === 0) continue;\n\n seen.add(solid);\n\n // Only add solids that actually overlap with the entity's bounds\n if (\n this.overlaps(bounds, {\n x: solid.x,\n y: solid.y,\n width: solid.width,\n height: solid.height,\n })\n ) {\n result.push(solid);\n }\n }\n }\n }\n\n return result;\n }\n\n public addFollower(entity: Entity, follower: Entity): void {\n // Logger.log('adding follower', entity.type, follower.type);\n if (!this.followers.has(entity)) {\n this.followers.set(entity, new Set());\n }\n this.followers.get(entity)!.add(follower);\n }\n\n public removeFollower(entity: Entity): void {\n // remove the entity from any set in the followers map\n for (const followers of this.followers.values()) {\n followers.delete(entity);\n }\n }\n\n public getFollowersOf(entity: Entity): Entity[] {\n return Array.from(this.followers.get(entity) || []);\n }\n\n public removeFollowersOf(entity: Entity): void {\n const set = this.followers.get(entity);\n if (set) {\n set.forEach((follower) => {\n follower.destroy();\n });\n set.clear();\n this.followers.delete(entity);\n }\n }\n\n public addToGroup(group: Group, entity: Entity): Entity {\n if (!this.groupWithEntities.has(group)) {\n this.groupWithEntities.set(group, new Set());\n }\n this.groupWithEntities.get(group)!.add(entity);\n return entity;\n }\n\n public removeFromGroup(entity: Entity): Entity {\n for (const group of this.groupWithEntities.values()) {\n group.delete(entity);\n }\n return entity;\n }\n\n public getEntitiesInGroup(group: Group): Entity[] {\n return Array.from(this.groupWithEntities.get(group) || []);\n }\n\n public removeEntitiesOfGroup(group: Group): Entity[] {\n const entities = Array.from(this.groupWithEntities.get(group) || []) || [];\n entities.forEach((entity) => {\n entity.setGroup(null);\n });\n this.groupWithEntities.delete(group);\n return entities;\n }\n\n private overlaps(a: Rectangle, b: Rectangle): boolean {\n return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;\n }\n\n private getCells(bounds: Rectangle): string[] {\n const cells: string[] = [];\n const { gridSize } = this.options;\n\n // Calculate the exact grid cells that the bounds intersect with\n const startX = Math.floor(bounds.x / gridSize);\n const startY = Math.floor(bounds.y / gridSize);\n const endX = Math.ceil((bounds.x + bounds.width) / gridSize);\n const endY = Math.ceil((bounds.y + bounds.height) / gridSize);\n\n // Pre-allocate array with exact size to avoid resizing\n cells.length = (endX - startX) * (endY - startY);\n\n // Use a single index counter to avoid array.push() operations\n let index = 0;\n\n // Only check one additional cell in the direction of movement for actors\n // For solids or static bounds, just use the exact cells\n for (let x = startX; x < endX; x++) {\n for (let y = startY; y < endY; y++) {\n // Use template string only once per iteration\n cells[index++] = `${x},${y}`;\n }\n }\n\n return cells;\n }\n\n private addSolidToGrid(solid: Solid): void {\n const bounds = {\n x: solid.x,\n y: solid.y,\n width: solid.width,\n height: solid.height,\n };\n const cells = this.getCells(bounds);\n\n for (const cell of cells) {\n if (!this.grid.has(cell)) {\n this.grid.set(cell, { solids: new Set(), actors: new Set() });\n }\n this.grid.get(cell)!.solids.add(solid);\n }\n }\n\n private removeSolidFromGrid(solid: Solid): void {\n const bounds = {\n x: solid.x,\n y: solid.y,\n width: solid.width,\n height: solid.height,\n };\n const cells = this.getCells(bounds);\n\n for (const cell of cells) {\n const gridCell = this.grid.get(cell);\n if (gridCell) {\n gridCell.solids.delete(solid);\n if (gridCell.solids.size === 0 && gridCell.actors.size === 0) {\n this.grid.delete(cell);\n }\n }\n }\n }\n\n public debugRender(): void {\n if (!this._debugGfx) {\n return;\n }\n const gfx = this._debugGfx!;\n gfx.clear();\n\n // Draw boundary if set\n if (this.options.boundary) {\n const b = this.options.boundary;\n gfx.rect(b.x, b.y, b.width, b.height);\n gfx.stroke({ color: 0xff0000, width: 2, alignment: 0.5 });\n }\n\n // Draw grid\n for (const cell of this.grid.keys()) {\n const [x, y] = cell.split(',').map(Number);\n gfx.rect(x * this.options.gridSize, y * this.options.gridSize, this.options.gridSize, this.options.gridSize);\n }\n gfx.stroke({ color: 0x00ff00, width: 1, join: 'miter', cap: 'butt' });\n\n // Draw solids\n for (const solid of this.solids) {\n gfx.rect(solid.x, solid.y, solid.width, solid.height);\n gfx.stroke({ color: solid.debugColor ?? 0x00ff00, alpha: 1 });\n }\n\n // Draw actors\n for (const actor of this.actors) {\n gfx.rect(actor.x, actor.y, actor.width, actor.height);\n gfx.stroke({ color: actor.debugColor ?? 0xff0000, alpha: 1 });\n }\n\n // Draw sensors\n for (const sensor of this.sensors) {\n gfx.rect(sensor.x, sensor.y, sensor.width, sensor.height);\n gfx.stroke({ color: sensor.debugColor ?? 0xffff00, alpha: 1 });\n }\n }\n\n /**\n * Get all entities of a specific type\n * @param type The type to look for\n * @returns Array of entities matching the type\n */\n public getByType(type: string): (Actor | Solid)[] {\n const actors = this.actorsByType.get(type) || new Set<Actor>();\n const solids = this.solidsByType.get(type) || new Set<Solid>();\n return [...actors, ...solids];\n }\n\n /**\n * Get all actors of a specific type\n * @param type The type to look for\n * @returns Array of actors matching the type\n */\n public getActorsByType(type: string | string[]): Actor[] {\n if (Array.isArray(type)) {\n return type.flatMap((t) => Array.from(this.actorsByType.get(t) || new Set()));\n }\n return Array.from(this.actorsByType.get(type) || new Set());\n }\n /**\n * Get all solids of a specific type\n * @param type The type to look for\n * @returns Array of solids matching the type\n */\n public getSolidsByType(type: string | string[]): Solid[] {\n if (Array.isArray(type)) {\n return type.flatMap((t) => Array.from(this.solidsByType.get(t) || new Set()));\n }\n return Array.from(this.solidsByType.get(type) || new Set());\n }\n /**\n * Get all sensors of a specific type\n * @param type The type to look for\n * @returns Array of sensors matching the type\n */\n public getSensorsByType(type: string | string[]): Sensor[] {\n if (Array.isArray(type)) {\n return type.flatMap((t) => Array.from(this.sensorsByType.get(t) || new Set()));\n }\n return Array.from(this.sensorsByType.get(type) || new Set());\n }\n\n public clearGrid(): void {\n this.grid.clear();\n }\n\n public clearAll(destroy: boolean = true) {\n this.grid.clear();\n this.entities.clear();\n\n if (destroy) {\n this.solids.forEach((solid) => {\n solid.destroy();\n });\n this.actors.forEach((actor) => {\n actor.destroy();\n });\n this.sensors.forEach((sensor) => sensor.destroy());\n }\n\n this.solids.clear();\n this.actors.clear();\n this.sensors.clear();\n\n this.solidsByType.clear();\n this.actorsByType.clear();\n this.sensorsByType.clear();\n }\n\n public destroy(): void {\n this.debug = false;\n this.gravity = 0;\n this.maxVelocity = 0;\n\n this.clearAll();\n }\n\n /**\n * Gets a collision result object from the pool or creates a new one if needed\n */\n private getCollisionResult(): ActorCollisionResult {\n if (this._collisionResultPoolIndex < this._collisionResultPool.length) {\n const result = this._collisionResultPool[this._collisionResultPoolIndex++];\n result.collided = false;\n result.actor = null as any;\n result.normal = undefined;\n result.penetration = 0;\n return result;\n }\n\n // Create a new object and add it to the pool\n const newResult: ActorCollisionResult = {\n collided: false,\n actor: null as any,\n normal: undefined,\n penetration: 0,\n };\n\n this._collisionResultPool.push(newResult);\n this._collisionResultPoolIndex++;\n return newResult;\n }\n\n /**\n * Gets an actor collision object from the pool or creates a new one if needed\n */\n private getActorCollision(): ActorCollision {\n if (this._actorCollisionPoolIndex < this._actorCollisionPool.length) {\n return this._actorCollisionPool[this._actorCollisionPoolIndex++];\n }\n\n // Create a new object and add it to the pool\n const newCollision: ActorCollision = {\n type: 'unknown|unknown' as `${string}|${string}`,\n actor1: null as any,\n actor2: null as any,\n result: null as any,\n };\n\n this._actorCollisionPool.push(newCollision);\n this._actorCollisionPoolIndex++;\n return newCollision;\n }\n\n /**\n * Resets the collision result pools for the next frame\n */\n private resetCollisionPools(): void {\n this._collisionResultPoolIndex = 0;\n this._actorCollisionPoolIndex = 0;\n }\n\n /**\n * Updates an actor's position in the grid.\n * This is called when an actor moves or when its size changes.\n *\n * @param actor - The actor to update in the grid\n */\n public updateActorInGrid(actor: Actor): void {\n // Skip if actor collisions are disabled\n if (!this.options.enableActorCollisions) return;\n\n // Skip if actor can't collide\n if (!actor.active || actor.collisionLayer === 0 || actor.collisionMask === 0) {\n // If actor has cells, remove it from them\n if (actor.currentGridCells.length > 0) {\n this.removeActorFromGrid(actor);\n }\n return;\n }\n\n // First remove from current cells\n this.removeActorFromGrid(actor);\n\n // Calculate new cells\n const bounds = {\n x: actor.x,\n y: actor.y,\n width: actor.width,\n height: actor.height,\n };\n\n const cells = this.getCells(bounds);\n\n // Store new cells in actor\n actor.currentGridCells = cells;\n\n // Add to new cells\n for (const cell of cells) {\n if (!this.grid.has(cell)) {\n this.grid.set(cell, { solids: new Set(), actors: new Set() });\n }\n\n const gridCell = this.grid.get(cell)!;\n gridCell.actors.add(actor);\n }\n }\n\n /**\n * Removes an actor from its current grid cells.\n *\n * @param actor - The actor to remove from the grid\n */\n public removeActorFromGrid(actor: Actor): void {\n const currentCells = actor.currentGridCells;\n\n for (const cell of currentCells) {\n const gridCell = this.grid.get(cell);\n\n if (gridCell) {\n gridCell.actors.delete(actor);\n\n // Remove the cell if it's empty\n if (gridCell.actors.size === 0 && gridCell.solids.size === 0) {\n this.grid.delete(cell);\n }\n }\n }\n\n // Clear the actor's cells\n actor.currentGridCells = [];\n }\n\n /**\n * Checks for collisions between all active actors.\n * This is an O(n²) operation, so it can be expensive with many actors.\n */\n private checkActorCollisions(): void {\n // Skip if there are no actors or only one actor\n if (this.actors.size <= 1 || !this.options.enableActorCollisions) return;\n\n // Clear the checked pairs set and potential collisions\n this._checkedPairs.clear();\n this._potentialCollisions.clear();\n this._activeGridCells.clear();\n\n // First pass: Mark cells with moving actors as active\n for (const [cellKey, gridCell] of this.grid.entries()) {\n for (const actor of gridCell.actors) {\n if (!actor.active) continue;\n\n // If actor is moving or recently moved\n if (actor.velocity.x !== 0 || actor.velocity.y !== 0) {\n this._activeGridCells.add(cellKey);\n break;\n }\n }\n }\n\n // Second pass: Generate potential collision pairs from active cells\n for (const cellKey of this._activeGridCells) {\n const gridCell = this.grid.get(cellKey);\n if (!gridCell || gridCell.actors.size <= 1) continue;\n\n const actorsInCell = Array.from(gridCell.actors);\n const cellActorCount = actorsInCell.length;\n\n // Check each actor against others in the same cell\n for (let i = 0; i < cellActorCount; i++) {\n const actor1 = actorsInCell[i];\n if (!actor1.active) continue;\n\n for (let j = i + 1; j < cellActorCount; j++) {\n const actor2 = actorsInCell[j];\n if (!actor2.active) continue;\n\n // Create a unique key for this actor pair\n const pairKey = actor1.id < actor2.id ? `${actor1.id}|${actor2.id}` : `${actor2.id}|${actor1.id}`;\n\n // Skip if we've already checked this pair\n if (this._checkedPairs.has(pairKey)) continue;\n this._checkedPairs.add(pairKey);\n\n // Fast collision layer check\n if (\n (actor1.collisionLayer & actor2.collisionMask) === 0 &&\n (actor2.collisionLayer & actor1.collisionMask) === 0\n )\n continue;\n\n // Fast AABB check\n if (\n actor1.x < actor2.x + actor2.width &&\n actor1.x + actor1.width > actor2.x &&\n actor1.y < actor2.y + actor2.height &&\n actor1.y + actor1.height > actor2.y\n ) {\n // Store potential collision pair\n this._potentialCollisions.set(pairKey, [actor1, actor2]);\n }\n }\n }\n }\n\n // Final pass: Detailed collision checks for potential pairs\n for (const [actor1, actor2] of this._potentialCollisions.values()) {\n // Check for collision\n const collisionResult = actor1.checkActorCollision(actor2);\n\n if (collisionResult.collided) {\n // Add to actor1's collision list\n actor1.actorCollisions.push(collisionResult);\n\n // Get a mirrored result from the pool\n const mirroredResult = this.getCollisionResult();\n mirroredResult.collided = true;\n mirroredResult.actor = actor1;\n\n if (collisionResult.normal) {\n if (!mirroredResult.normal) {\n mirroredResult.normal = { x: 0, y: 0 };\n }\n mirroredResult.normal.x = -collisionResult.normal.x;\n mirroredResult.normal.y = -collisionResult.normal.y;\n } else {\n mirroredResult.normal = undefined;\n }\n\n mirroredResult.penetration = collisionResult.penetration;\n\n // Add to actor2's collision list\n actor2.actorCollisions.push(mirroredResult);\n\n // Get an actor collision from the pool and add to system's collision list\n const actorCollision = this.getActorCollision();\n actorCollision.type = `${actor1.type}|${actor2.type}`;\n actorCollision.actor1 = actor1;\n actorCollision.actor2 = actor2;\n actorCollision.result = collisionResult;\n this.actorCollisions[this._actorCollisionPoolIndex++] = actorCollision;\n\n // Call collision handlers\n actor1.onActorCollide(collisionResult);\n actor2.onActorCollide(mirroredResult);\n\n // Resolve the collision\n actor1.resolveActorCollision(collisionResult);\n actor2.resolveActorCollision(mirroredResult);\n }\n }\n\n // Process collisions with resolver if provided\n if (this._actorCollisionPoolIndex > 0 && this.options.actorCollisionResolver) {\n this.options.actorCollisionResolver(this.actorCollisions.slice(0, this._actorCollisionPoolIndex));\n }\n }\n\n setCollisionResolver(resolver: (collisions: Collision[]) => void): void {\n this.options.collisionResolver = resolver;\n }\n\n /**\n * Sets a custom resolver for actor-to-actor collisions.\n *\n * @param resolver - Function to handle actor-to-actor collisions\n */\n public setActorCollisionResolver(resolver: (collisions: ActorCollision[]) => void): void {\n this.options.actorCollisionResolver = resolver;\n }\n\n /**\n * Returns whether actor-to-actor collision detection is enabled.\n *\n * @returns True if actor collisions are enabled\n */\n public get enableActorCollisions(): boolean {\n return !!this.options.enableActorCollisions;\n }\n\n /**\n * Enables or disables actor-to-actor collision detection.\n * When enabled, it initializes the grid for all existing actors.\n *\n * @param enabled - Whether to enable actor-to-actor collisions\n */\n public setActorCollisionsEnabled(enabled: boolean): void {\n const wasEnabled = this.options.enableActorCollisions;\n this.options.enableActorCollisions = enabled;\n\n // If newly enabled, add all actors to the grid\n if (enabled && !wasEnabled) {\n for (const actor of this.actors) {\n actor.updateGridCells();\n }\n } else if (!enabled && wasEnabled) {\n // If newly disabled, clear the actor grid\n this.grid.clear();\n\n // Clear all actors' grid cells\n for (const actor of this.actors) {\n actor.currentGridCells = [];\n }\n }\n }\n\n public createGroup(config: PhysicsEntityConfig): Group {\n // Apply default collision settings if not specified in config\n if (this.options.defaultCollisionLayer !== undefined && config.collisionLayer === undefined) {\n config.collisionLayer = this.options.defaultCollisionLayer;\n }\n if (this.options.defaultCollisionMask !== undefined && config.collisionMask === undefined) {\n config.collisionMask = this.options.defaultCollisionMask;\n }\n\n const group = config.class ? (new config.class(config) as unknown as Group) : new Group(config);\n return this.addGroup(group);\n }\n\n public addGroup(group: Group): Group {\n this.entities.add(group);\n this.groups.add(group);\n\n // Add to type index\n if (!this.groupsByType.has(group.type)) {\n this.groupsByType.set(group.type, new Set());\n }\n this.groupsByType.get(group.type)!.add(group);\n\n return group;\n }\n\n public removeGroup(group: Group, destroyView: boolean = true): void {\n // Early return if group doesn't exist\n if (!this.groups.has(group)) return;\n\n this.entities.delete(group);\n this.groups.delete(group);\n\n // Remove from type index\n const typeSet = this.groupsByType.get(group.type);\n if (typeSet) {\n typeSet.delete(group);\n if (typeSet.size === 0) {\n this.groupsByType.delete(group.type);\n }\n }\n\n // Get all entities in the group before removing them\n const groupEntities = this.groupWithEntities.get(group);\n if (groupEntities && groupEntities.size > 0) {\n // Create arrays for each entity type to batch process\n const actors: Actor[] = [];\n const solids: Solid[] = [];\n const sensors: Sensor[] = [];\n\n // Sort entities by type for batch processing\n for (const entity of groupEntities) {\n if (entity.entityType === 'Actor') {\n actors.push(entity as Actor);\n } else if (entity.entityType === 'Solid') {\n solids.push(entity as Solid);\n } else if (entity.entityType === 'Sensor') {\n sensors.push(entity as Sensor);\n }\n }\n\n // Batch remove entities by type\n for (const actor of actors) {\n this.removeActor(actor, destroyView);\n }\n\n for (const solid of solids) {\n this.removeSolid(solid, destroyView);\n }\n\n for (const sensor of sensors) {\n this.removeSensor(sensor, destroyView);\n }\n }\n\n // Clean up the group's entry in the tracking map\n this.groupWithEntities.delete(group);\n\n group.onRemoved();\n }\n\n /**\n * Get all groups of a specific type\n * @param type The type to look for\n * @returns Array of groups matching the type\n */\n public getGroupsByType(type: string | string[]): Group[] {\n if (Array.isArray(type)) {\n // Pre-calculate total size to avoid array resizing\n let totalSize = 0;\n for (const t of type) {\n const set = this.groupsByType.get(t);\n if (set) totalSize += set.size;\n }\n\n // Pre-allocate result array\n const result = new Array<Group>(totalSize);\n let index = 0;\n\n // Fill array without using flatMap (more efficient)\n for (const t of type) {\n const set = this.groupsByType.get(t);\n if (set) {\n for (const group of set) {\n result[index++] = group;\n }\n }\n }\n\n return result;\n }\n\n return Array.from(this.groupsByType.get(type) || new Set());\n }\n\n /**\n * Checks if two AABB rectangles overlap.\n *\n * @param a - The first AABB rectangle\n * @param b - The second AABB rectangle\n * @returns True if the rectangles overlap, false otherwise\n */\n public aabbOverlap(a: AABBLike, b: AABBLike): boolean {\n return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;\n }\n}\n","import { IApplication, IPlugin, isDev, Logger, Plugin, version } from '@caperjs/core';\nimport { Container as PIXIContainer, Rectangle, Ticker } from 'pixi.js';\nimport { Actor } from './Actor';\nimport { Group } from './Group';\nimport { AABBLike, CrunchPhysicsOptions } from './interfaces';\nimport { Sensor } from './Sensor';\nimport { Solid } from './Solid';\nimport { System } from './System';\nimport {\n ActorCollision,\n Collision,\n CollisionLayer,\n CollisionLayers,\n PhysicsEntityConfig,\n RegisteredCollisionLayer,\n SensorOverlap,\n} from './types';\n\n/**\n * Interface for the Crunch physics plugin, providing a simple yet powerful 2D physics system\n * inspired by Towerfall's physics mechanics. Supports dynamic actors, static solids, and trigger sensors.\n *\n * @example\n * ```typescript\n * // Initialize the physics system\n * const physics = this.app.getPlugin('crunch') as ICrunchPhysicsPlugin;\n *\n * const physics = await this.physics.initialize({\n * gravity: 900,\n * debug: true,\n * gridSize: 32\n * });\n *\n * // Create a player character\n * const playerSprite = this.add.sprite({asset:Texture.WHITE, width: 32, height: 64});\n *\n * const player = physics.createActor({\n * type: 'Player',\n * position: [100, 100],\n * size: [32, 64],\n * view: playerSprite\n * });\n *\n * // Create a platform\n * const platformSprite = this.add.sprite({asset:Texture.WHITE, width: 800, height: 32, tint: 0x00FF00});\n * const platform = physics.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32],\n * view: platformSprite\n * });\n *\n * // Create a coin pickup sensor\n * const coinSprite = this.add.sprite({asset:Texture.WHITE, width: 32, height: 32, tint: 0x00FF00});\n * const coin = physics.createSensor({\n * type: 'Coin',\n * position: [400, 400],\n * size: [32, 32],\n * view: coinSprite\n * });\n * ```\n */\nexport interface ICrunchPhysicsPlugin extends IPlugin<CrunchPhysicsOptions> {\n system: System;\n container: PIXIContainer;\n enabled: boolean;\n\n /**\n * Initializes the physics system with the specified options.\n */\n initialize(options?: Partial<CrunchPhysicsOptions>, app?: IApplication): Promise<void>;\n\n /**\n * Sets a custom collision resolver function that will be called when collisions occur.\n *\n * @param resolver - Function to handle collisions\n *\n * @example\n * ```typescript\n * physics.setCollisionResolver((collisions) => {\n * collisions.forEach(collision => {\n * if (collision.actor.type === 'Player' && collision.solid.type === 'Spike') {\n * player.damage(10);\n * }\n * });\n * });\n * ```\n */\n setCollisionResolver(resolver: (collisions: Collision[]) => void): void;\n\n /**\n * Sets a custom resolver for actor-to-actor collisions.\n *\n * @param resolver - Function to handle actor-to-actor collisions\n *\n * @example\n * ```typescript\n * physics.setActorCollisionResolver((collisions) => {\n * collisions.forEach(collision => {\n * if (collision.actor1.type === 'Player' && collision.actor2.type === 'Enemy') {\n * player.takeDamage(10);\n * }\n * });\n * });\n * ```\n */\n setActorCollisionResolver(resolver: (collisions: ActorCollision[]) => void): void;\n\n /**\n * Enables or disables actor-to-actor collision detection.\n *\n * @param enabled - Whether to enable actor-to-actor collisions\n *\n * @example\n * ```typescript\n * // Enable actor-to-actor collisions\n * physics.setActorCollisionsEnabled(true);\n *\n * // Disable actor-to-actor collisions for performance\n * physics.setActorCollisionsEnabled(false);\n * ```\n */\n setActorCollisionsEnabled(enabled: boolean): void;\n\n /**\n * Creates a generic physics entity based on the provided configuration.\n * Use this when you need to create an entity without knowing its specific type at compile time.\n *\n * @param config - Configuration for the physics entity\n * @returns The created physics entity\n *\n * @example\n * ```typescript\n * const entityConfig = {\n * type: 'Platform',\n * position: [100, 100],\n * size: [200, 32],\n * view: sprite\n * };\n * const entity = physics.createEntity(entityConfig);\n * ```\n */\n createEntity(config: PhysicsEntityConfig): Actor | Solid | Sensor | Group;\n\n /**\n * Adds an existing physics entity to the system.\n * Useful when you want to manage entity creation yourself.\n *\n * @param entity - The physics entity to add\n * @returns The added entity\n *\n * @example\n * ```typescript\n * class CustomActor extends Actor {\n * constructor() {\n * super({ type: 'Custom', position: [0, 0], size: [32, 32] });\n * }\n * }\n *\n * const customActor = new CustomActor();\n * physics.addEntity(customActor);\n * ```\n */\n addEntity(entity: Actor | Solid | Sensor | Group): Actor | Solid | Sensor | Group;\n\n /**\n * Creates a dynamic physics actor that can move and collide with other entities.\n * Actors are typically used for players, enemies, or any moving game objects.\n *\n * @param config - Configuration for the actor\n * @returns The created actor\n *\n * @example\n * ```typescript\n * // Create a player character\n * const player = physics.createActor({\n * type: 'Player',\n * position: [100, 100],\n * size: [32, 64],\n * view: playerSprite\n * });\n *\n * // Update player in game loop\n * player.velocity.x = 300; // Move right\n * player.velocity.y = -600; // Jump\n * ```\n */\n createActor(config: PhysicsEntityConfig): Actor;\n\n /**\n * Creates a static solid object that other entities can collide with.\n * Solids are typically used for platforms, walls, and other immovable objects.\n *\n * @param config - Configuration for the solid\n * @returns The created solid\n *\n * @example\n * ```typescript\n * // Create a static platform\n * const platform = physics.createSolid({\n * type: 'Platform',\n * position: [0, 500],\n * size: [800, 32],\n * view: platformSprite\n * });\n *\n * // Create a moving platform\n * const movingPlatform = physics.createSolid({\n * type: 'Platform',\n * position: [100, 300],\n * size: [200, 32],\n * view: platformSprite\n * });\n *\n * // Update platform position\n * gsap.to(movingPlatform, {\n * x: 500,\n * duration: 2,\n * yoyo: true,\n * repeat: -1\n * });\n * ```\n */\n createSolid(config: PhysicsEntityConfig): Solid;\n\n /**\n * Creates a sensor zone that can detect overlaps with other entities.\n * Sensors are typically used for triggers, collectibles, or detection zones.\n *\n * @param config - Configuration for the sensor\n * @returns The created sensor\n *\n * @example\n * ```typescript\n * // Create a coin pickup\n * const coin = physics.createSensor({\n * type: 'Coin',\n * position: [400, 300],\n * size: [32, 32],\n * view: coinSprite\n * });\n *\n * // Handle coin collection\n * coin.onActorEnter = (actor) => {\n * if (actor.type === 'Player') {\n * increaseScore(10);\n * physics.removeSensor(coin);\n * }\n * };\n * ```\n */\n createSensor(config: PhysicsEntityConfig): Sensor;\n\n createGroup(config: PhysicsEntityConfig): Group;\n\n addActor(actor: Actor): Actor;\n\n addSolid(solid: Solid): Solid;\n\n addSensor(sensor: Sensor): Sensor;\n\n addGroup(group: Group): Group;\n\n /**\n * Removes an actor from the physics system.\n *\n * @param actor - The actor to remove\n *\n * @example\n * ```typescript\n * // Remove player when they die\n * function killPlayer(player: Actor) {\n * playDeathAnimation();\n * physics.removeActor(player);\n * }\n * ```\n */\n removeActor(actor: Actor): void;\n\n /**\n * Removes a solid from the physics system.\n *\n * @param solid - The solid to remove\n *\n * @example\n * ```typescript\n * // Remove a platform when it's destroyed\n * function destroyPlatform(platform: Solid) {\n * playBreakAnimation();\n * physics.removeSolid(platform);\n * }\n * ```\n */\n removeSolid(solid: Solid): void;\n\n /**\n * Removes a sensor from the physics system.\n *\n * @param sensor - The sensor to remove\n *\n * @example\n * ```typescript\n * // Remove a coin when it's collected\n * function collectCoin(coin: Sensor) {\n * playCollectSound();\n * physics.removeSensor(coin);\n * }\n * ```\n */\n removeSensor(sensor: Sensor): void;\n\n /**\n * Cleans up the physics system and removes all entities.\n * Call this when transitioning between scenes or shutting down the game.\n *\n * @example\n * ```typescript\n * class GameScene extends Scene {\n * destroy() {\n * this.physics.destroy();\n * super.destroy();\n * }\n * }\n * ```\n */\n destroy(): void;\n\n /**\n * Creates a custom collision layer.\n *\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @returns A unique collision layer value\n *\n * @example\n * ```typescript\n * // Create custom collision layers\n * const WATER_LAYER = physics.createCollisionLayer(0);\n * const LAVA_LAYER = physics.createCollisionLayer(1);\n *\n * // Use in entity creation\n * const waterEntity = physics.createActor({\n * type: 'Water',\n * position: [100, 400],\n * size: [800, 100],\n * collisionLayer: WATER_LAYER,\n * collisionMask: CollisionLayer.PLAYER | CollisionLayer.ENEMY\n * });\n * ```\n */\n createCollisionLayer(index: number): number;\n\n /**\n * Creates a collision mask from multiple layers.\n *\n * @param layers Array of collision layers to combine\n * @returns A combined collision mask\n *\n * @example\n * ```typescript\n * // Create a mask that collides with players, enemies and projectiles\n * const mask = physics.createCollisionMask([\n * CollisionLayer.PLAYER,\n * CollisionLayer.ENEMY,\n * CollisionLayer.PROJECTILE\n * ]);\n * ```\n */\n createCollisionMask(...layers: number[]): number;\n\n /**\n * Registers a named collision layer for better intellisense support.\n *\n * @param name Name of the collision layer (used for intellisense)\n * @param indexOrDescription Index or description of the layer\n * @param description Optional description of the layer\n * @returns The numeric value of the registered collision layer\n *\n * @example\n * ```typescript\n * // Register named collision layers\n * const WATER = physics.registerCollisionLayer('WATER', 0, 'Water surfaces');\n * const LAVA = physics.registerCollisionLayer('LAVA', 1, 'Lava surfaces that damage players');\n *\n * // Use in entity creation\n * const waterEntity = physics.createActor({\n * type: 'Water',\n * position: [100, 400],\n * size: [800, 100],\n * collisionLayer: WATER,\n * collisionMask: CollisionLayer.PLAYER | CollisionLayer.ENEMY\n * });\n * ```\n */\n registerCollisionLayer(name: string, indexOrDescription?: number | string, description?: string): number;\n\n /**\n * Gets a registered collision layer by name.\n *\n * @param name Name of the collision layer\n * @returns The numeric value of the registered collision layer or undefined if not found\n *\n * @example\n * ```typescript\n * // Get a registered collision layer\n * const waterLayer = physics.getCollisionLayer('WATER');\n * if (waterLayer !== undefined) {\n * // Use the layer\n * entity.setCollisionLayer(waterLayer);\n * }\n * ```\n */\n getCollisionLayer(name: string): number | undefined;\n\n /**\n * Gets all registered collision layers.\n *\n * @returns Array of all registered collision layers\n *\n * @example\n * ```typescript\n * // Get all registered collision layers\n * const layers = physics.getCollisionLayers();\n * console.log(`Registered layers: ${layers.map(l => l.name).join(', ')}`);\n * ```\n */\n getCollisionLayers(): RegisteredCollisionLayer[];\n\n /**\n * Removes a registered collision layer.\n *\n * @param name Name of the collision layer to remove\n * @returns True if the layer was removed, false if it didn't exist\n *\n * @example\n * ```typescript\n * // Remove a registered collision layer\n * physics.removeCollisionLayer('WATER');\n * ```\n */\n removeCollisionLayer(name: string): boolean;\n\n /**\n * Clears all registered collision layers.\n *\n * @example\n * ```typescript\n * // Clear all registered collision layers\n * physics.clearCollisionLayers();\n * ```\n */\n clearCollisionLayers(): void;\n}\n\nconst defaultOptions: Partial<CrunchPhysicsOptions> = {\n gridSize: 128,\n gravity: 900,\n maxVelocity: 400,\n debug: false,\n culling: true,\n boundary: new Rectangle(0, 0, 800, 600),\n};\n\n/**\n * Implementation of the Crunch physics plugin.\n * See {@link ICrunchPhysicsPlugin} for detailed API documentation and examples.\n */\nexport default class CrunchPhysicsPlugin extends Plugin<CrunchPhysicsOptions> implements ICrunchPhysicsPlugin {\n public system: System;\n public container: PIXIContainer;\n public enabled = false;\n private collisionResolver?: (collisions: Collision[]) => void;\n private overlapResolver?: (overlaps: SensorOverlap[]) => void;\n private actorCollisionResolver?: (collisions: ActorCollision[]) => void;\n\n constructor() {\n super('crunch');\n }\n\n private hello() {\n const hello = `%c Caper Crunch Physics Plugin v${version}`;\n console.log(hello, 'background: rgba(31, 41, 55, 1);color: #74b64c');\n }\n\n public async initialize(options?: Partial<CrunchPhysicsOptions>, _app: IApplication): Promise<void> {\n this.hello();\n if (!options) {\n return;\n }\n\n this._options = { ...defaultOptions, ...options } as CrunchPhysicsOptions;\n\n this.enabled = true;\n this.container = options.container || this.app.stage;\n\n if (isDev) {\n Logger.log(this._options);\n }\n\n // Create the physics system\n const gridSize = options.gridSize ?? 32;\n const gravity = options.gravity ?? 900;\n const maxVelocity = options.maxVelocity ?? 400;\n const debug = options.debug ?? false;\n const enableActorCollisions = options.enableActorCollisions ?? false;\n\n // Create the system with default collision layers set to NONE for better performance\n this.system = new System({\n gridSize,\n gravity,\n maxVelocity,\n boundary: options.boundary,\n culling: options.culling,\n plugin: this,\n collisionResolver: this.collisionResolver,\n overlapResolver: this.overlapResolver,\n actorCollisionResolver: this.actorCollisionResolver,\n enableActorCollisions,\n defaultCollisionLayer: CollisionLayer.NONE,\n defaultCollisionMask: CollisionLayer.NONE,\n });\n\n if (debug) {\n this.system.debug = true;\n }\n\n // Register update loop\n this.app.ticker.add(this.update);\n }\n\n public setCollisionResolver(resolver: (collisions: Collision[]) => void): void {\n this.collisionResolver = resolver;\n this.system.setCollisionResolver(resolver);\n }\n\n public setActorCollisionResolver(resolver: (collisions: ActorCollision[]) => void): void {\n this.actorCollisionResolver = resolver;\n this.system.setActorCollisionResolver(resolver);\n }\n\n public setActorCollisionsEnabled(enabled: boolean): void {\n this.system.setActorCollisionsEnabled(enabled);\n }\n\n public createEntity(config: PhysicsEntityConfig): Actor | Solid | Sensor | Group {\n return this.system.createEntity(config);\n }\n\n public addEntity(entity: Actor | Solid | Sensor | Group): Actor | Solid | Sensor | Group {\n return this.system.addEntity(entity);\n }\n\n public createActor(config: PhysicsEntityConfig): Actor {\n return this.system.createActor(config);\n }\n\n public createSolid(config: PhysicsEntityConfig): Solid {\n return this.system.createSolid(config);\n }\n\n public createSensor(config: PhysicsEntityConfig): Sensor {\n return this.system.createSensor(config);\n }\n\n public createGroup(config: PhysicsEntityConfig): Group {\n return this.system.createGroup(config);\n }\n\n public addActor(actor: Actor): Actor {\n return this.system.addActor(actor);\n }\n\n public addSolid(solid: Solid): Solid {\n return this.system.addSolid(solid);\n }\n\n public addSensor(sensor: Sensor): Sensor {\n return this.system.addSensor(sensor);\n }\n\n public addGroup(group: Group): Group {\n return this.system.addGroup(group);\n }\n\n public removeActor(actor: Actor): void {\n this.system.removeActor(actor);\n }\n\n public removeSolid(solid: Solid): void {\n this.system.removeSolid(solid);\n }\n\n public removeSensor(sensor: Sensor): void {\n this.system.removeSensor(sensor);\n }\n\n public removeGroup(group: Group): void {\n this.system.removeGroup(group);\n }\n\n public destroy(): void {\n this.enabled = false;\n this.app.ticker.remove(this.update);\n if (this.system) {\n this.system.destroy();\n // @ts-expect-error system can't be null\n this.system = null;\n }\n super.destroy();\n }\n\n private update(_ticker: Ticker) {\n if (!this.enabled) return;\n this.system.update(_ticker.deltaTime);\n }\n\n /**\n * Creates a custom collision layer.\n *\n * @param index Index from 0-15 representing which user bit to use (gets shifted to bits 16-31)\n * @returns A unique collision layer value\n */\n public createCollisionLayer(index: number): number {\n return CollisionLayers.createLayer(index);\n }\n\n /**\n * Creates a collision mask from multiple layers.\n *\n * @param layers Array of collision layers to combine\n * @returns A combined collision mask\n */\n public createCollisionMask(...layers: number[]): number {\n return CollisionLayers.createMask(layers);\n }\n\n /**\n * Registers a named collision layer for better intellisense support.\n *\n * @param name Name of the collision layer (used for intellisense)\n * @param indexOrDescription Index or description of the layer\n * @param description Optional description of the layer\n * @returns The numeric value of the registered collision layer\n */\n public registerCollisionLayer(name: string, indexOrDescription?: number | string, description?: string): number {\n const registry = CollisionLayers.getRegistry();\n\n // Handle overloaded method signature\n if (typeof indexOrDescription === 'number') {\n // First overload: registerCollisionLayer(name, index, description?)\n const index = indexOrDescription;\n const layer = registry.register(name, index, description);\n return layer.value;\n } else {\n // Second overload: registerCollisionLayer(name, description?)\n const layerDescription = indexOrDescription;\n const nextIndex = registry.getNextAvailableIndex();\n\n if (nextIndex === -1) {\n throw new Error('No more collision layer indices available (maximum 16)');\n }\n\n const layer = registry.register(name, nextIndex, layerDescription);\n return layer.value;\n }\n }\n\n /**\n * Gets a registered collision layer by name.\n *\n * @param name Name of the collision layer\n * @returns The numeric value of the registered collision layer or undefined if not found\n */\n public getCollisionLayer(name: string): number | undefined {\n const registry = CollisionLayers.getRegistry();\n const layer = registry.get(name);\n return layer?.value;\n }\n\n /**\n * Gets all registered collision layers.\n *\n * @returns Array of all registered collision layers\n */\n public getCollisionLayers(): RegisteredCollisionLayer[] {\n const registry = CollisionLayers.getRegistry();\n return registry.getAll();\n }\n\n /**\n * Removes a registered collision layer.\n *\n * @param name Name of the collision layer to remove\n * @returns True if the layer was removed, false if it didn't exist\n */\n public removeCollisionLayer(name: string): boolean {\n const registry = CollisionLayers.getRegistry();\n return registry.remove(name);\n }\n\n /**\n * Clears all registered collision layers.\n */\n public clearCollisionLayers(): void {\n const registry = CollisionLayers.getRegistry();\n registry.clear();\n }\n\n /**\n * Checks if two AABB rectangles overlap.\n *\n * @param a - The first AABB rectangle\n * @param b - The second AABB rectangle\n * @returns True if the rectangles overlap, false otherwise\n */\n public aabbOverlap(a: AABBLike, b: AABBLike): boolean {\n return this.system.aabbOverlap(a, b);\n }\n}\n","import CrunchPhysicsPlugin from './CrunchPhysicsPlugin';\n\nexport * from './Actor';\n// Don't export from collision-layers.d.ts as it's a declaration file\nexport * from './Entity';\nexport * from './Group';\nexport * from './interfaces';\nexport * from './Sensor';\nexport * from './Solid';\nexport * from './System';\nexport * from './types';\n\nexport type { ICrunchPhysicsPlugin } from './CrunchPhysicsPlugin';\n\n// Export specific types for easier access\nexport * from './types';\n\nexport default CrunchPhysicsPlugin;\n"],"mappings":"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"}
|