diff --git a/packages/sts-like-viewer/src/widgets/InventoryItemSpawner.ts b/packages/sts-like-viewer/src/widgets/InventoryItemSpawner.ts index 4f07071..30f5320 100644 --- a/packages/sts-like-viewer/src/widgets/InventoryItemSpawner.ts +++ b/packages/sts-like-viewer/src/widgets/InventoryItemSpawner.ts @@ -37,9 +37,10 @@ export interface InventoryItemSpawnerOptions { * Items currently being dragged are excluded from getData() to prevent * the spawner from respawning them while they're in flight. */ -export class InventoryItemSpawner - implements Spawner, Phaser.GameObjects.Container> -{ +export class InventoryItemSpawner implements Spawner< + InventoryItem, + Phaser.GameObjects.Container +> { private scene: Phaser.Scene; private gameState: MutableSignal; private parentContainer: Phaser.GameObjects.Container; @@ -102,6 +103,7 @@ export class InventoryItemSpawner obj: Phaser.GameObjects.Container, _item: InventoryItem, ): void { + obj.removeAllListeners(); obj.destroy(); } @@ -248,6 +250,8 @@ export class InventoryItemSpawner container: Phaser.GameObjects.Container, ): void { container.on("pointerdown", (pointer: Phaser.Input.Pointer) => { + // Guard against stale events firing on destroyed containers + if (!container.scene || !container.active) return; if (this.isLocked()) return; if (this.isDragging()) return; if (pointer.button === 0) { diff --git a/packages/sts-like-viewer/src/widgets/InventoryWidget.ts b/packages/sts-like-viewer/src/widgets/InventoryWidget.ts index 04f33d0..3eb58f1 100644 --- a/packages/sts-like-viewer/src/widgets/InventoryWidget.ts +++ b/packages/sts-like-viewer/src/widgets/InventoryWidget.ts @@ -140,12 +140,9 @@ export class InventoryWidget { item: InventoryItem, itemContainer: Phaser.GameObjects.Container, ): void { - // Remove from inventory state - this.gameState.produce((state) => { - removeItemFromGrid(state.inventory, itemId); - }); - - // Mark as dragging so spawner excludes it from getData() + // Mark as dragging FIRST so spawner excludes it from getData(). + // This prevents the spawner effect from destroying the container + // when we later update the inventory state. this.itemSpawner.markDragging(itemId); // Start drag session @@ -169,9 +166,14 @@ export class InventoryWidget { x: number, y: number, ): void { + // Remove from inventory since it's dropped outside valid placement + this.gameState.produce((state) => { + removeItemFromGrid(state.inventory, itemId); + }); + this.lostItemManager.createLostItem(itemId, shape, transform, meta, x, y); - // Unmark dragging — item is now "lost" and no longer in inventory + // Unmark dragging — item is now "lost" and managed by LostItemManager this.itemSpawner.unmarkDragging(itemId); } diff --git a/packages/sts-like-viewer/src/widgets/LostItemManager.ts b/packages/sts-like-viewer/src/widgets/LostItemManager.ts index 23f202f..b7a21ff 100644 --- a/packages/sts-like-viewer/src/widgets/LostItemManager.ts +++ b/packages/sts-like-viewer/src/widgets/LostItemManager.ts @@ -107,6 +107,8 @@ export class LostItemManager { container.setInteractive(hitRect, Phaser.Geom.Rectangle.Contains); container.on("pointerdown", (pointer: Phaser.Input.Pointer) => { + // Guard against stale events firing on destroyed containers + if (!container.scene || !container.active) return; if (this.isDragging()) return; if (pointer.button === 0) { this.onLostItemDragStart(itemId, container);