Fix drag race condition and stale pointer events
Mark items as dragging before updating inventory state to prevent the spawner from destroying containers mid-drag. Move inventory removal to drop time for lost items, and add guards to ignore stale pointer events on inactive containers.
This commit is contained in:
parent
5af7140958
commit
e14e41461f
|
|
@ -37,9 +37,10 @@ export interface InventoryItemSpawnerOptions {
|
||||||
* Items currently being dragged are excluded from getData() to prevent
|
* Items currently being dragged are excluded from getData() to prevent
|
||||||
* the spawner from respawning them while they're in flight.
|
* the spawner from respawning them while they're in flight.
|
||||||
*/
|
*/
|
||||||
export class InventoryItemSpawner
|
export class InventoryItemSpawner implements Spawner<
|
||||||
implements Spawner<InventoryItem<GameItemMeta>, Phaser.GameObjects.Container>
|
InventoryItem<GameItemMeta>,
|
||||||
{
|
Phaser.GameObjects.Container
|
||||||
|
> {
|
||||||
private scene: Phaser.Scene;
|
private scene: Phaser.Scene;
|
||||||
private gameState: MutableSignal<RunState>;
|
private gameState: MutableSignal<RunState>;
|
||||||
private parentContainer: Phaser.GameObjects.Container;
|
private parentContainer: Phaser.GameObjects.Container;
|
||||||
|
|
@ -102,6 +103,7 @@ export class InventoryItemSpawner
|
||||||
obj: Phaser.GameObjects.Container,
|
obj: Phaser.GameObjects.Container,
|
||||||
_item: InventoryItem<GameItemMeta>,
|
_item: InventoryItem<GameItemMeta>,
|
||||||
): void {
|
): void {
|
||||||
|
obj.removeAllListeners();
|
||||||
obj.destroy();
|
obj.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,6 +250,8 @@ export class InventoryItemSpawner
|
||||||
container: Phaser.GameObjects.Container,
|
container: Phaser.GameObjects.Container,
|
||||||
): void {
|
): void {
|
||||||
container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
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.isLocked()) return;
|
||||||
if (this.isDragging()) return;
|
if (this.isDragging()) return;
|
||||||
if (pointer.button === 0) {
|
if (pointer.button === 0) {
|
||||||
|
|
|
||||||
|
|
@ -140,12 +140,9 @@ export class InventoryWidget {
|
||||||
item: InventoryItem<GameItemMeta>,
|
item: InventoryItem<GameItemMeta>,
|
||||||
itemContainer: Phaser.GameObjects.Container,
|
itemContainer: Phaser.GameObjects.Container,
|
||||||
): void {
|
): void {
|
||||||
// Remove from inventory state
|
// Mark as dragging FIRST so spawner excludes it from getData().
|
||||||
this.gameState.produce((state) => {
|
// This prevents the spawner effect from destroying the container
|
||||||
removeItemFromGrid(state.inventory, itemId);
|
// when we later update the inventory state.
|
||||||
});
|
|
||||||
|
|
||||||
// Mark as dragging so spawner excludes it from getData()
|
|
||||||
this.itemSpawner.markDragging(itemId);
|
this.itemSpawner.markDragging(itemId);
|
||||||
|
|
||||||
// Start drag session
|
// Start drag session
|
||||||
|
|
@ -169,9 +166,14 @@ export class InventoryWidget {
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
): void {
|
): 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);
|
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);
|
this.itemSpawner.unmarkDragging(itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ export class LostItemManager {
|
||||||
container.setInteractive(hitRect, Phaser.Geom.Rectangle.Contains);
|
container.setInteractive(hitRect, Phaser.Geom.Rectangle.Contains);
|
||||||
|
|
||||||
container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
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 (this.isDragging()) return;
|
||||||
if (pointer.button === 0) {
|
if (pointer.button === 0) {
|
||||||
this.onLostItemDragStart(itemId, container);
|
this.onLostItemDragStart(itemId, container);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue