diff --git a/packages/sts-like-viewer/src/ui/App.tsx b/packages/sts-like-viewer/src/ui/App.tsx
index 959f3c1..10fde21 100644
--- a/packages/sts-like-viewer/src/ui/App.tsx
+++ b/packages/sts-like-viewer/src/ui/App.tsx
@@ -1,13 +1,13 @@
-import { h } from 'preact';
-import { PhaserGame, PhaserScene } from 'boardgame-phaser';
-import { useMemo } from 'preact/hooks';
-import { IndexScene } from '@/scenes/IndexScene';
-import { MapViewerScene } from '@/scenes/MapViewerScene';
-import { GridViewerScene } from '@/scenes/GridViewerScene';
-import { ShapeViewerScene } from '@/scenes/ShapeViewerScene';
-import { GameFlowScene } from '@/scenes/GameFlowScene';
-import { PlaceholderEncounterScene } from '@/scenes/PlaceholderEncounterScene';
-import { createGameState } from '@/state/gameState';
+import { h } from "preact";
+import { PhaserGame, PhaserScene } from "boardgame-phaser";
+import { useMemo } from "preact/hooks";
+import { IndexScene } from "@/scenes/IndexScene";
+import { MapViewerScene } from "@/scenes/MapViewerScene";
+import { GridViewerScene } from "@/scenes/GridViewerScene";
+import { ShapeViewerScene } from "@/scenes/ShapeViewerScene";
+import { GameFlowScene } from "@/scenes/GameFlowScene";
+import { PlaceholderEncounterScene } from "@/scenes/PlaceholderEncounterScene";
+import { createGameState } from "@/state/gameState";
// 全局游戏状态单例
const gameState = createGameState();
@@ -18,18 +18,36 @@ export default function App() {
const gridViewerScene = useMemo(() => new GridViewerScene(), []);
const shapeViewerScene = useMemo(() => new ShapeViewerScene(), []);
const gameFlowScene = useMemo(() => new GameFlowScene(gameState), []);
- const placeholderEncounterScene = useMemo(() => new PlaceholderEncounterScene(gameState), []);
+ const placeholderEncounterScene = useMemo(
+ () => new PlaceholderEncounterScene(gameState),
+ [],
+ );
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/packages/sts-like-viewer/src/widgets/DragController.ts b/packages/sts-like-viewer/src/widgets/DragController.ts
index 4a81b0e..f91ae31 100644
--- a/packages/sts-like-viewer/src/widgets/DragController.ts
+++ b/packages/sts-like-viewer/src/widgets/DragController.ts
@@ -4,19 +4,19 @@ import {
type GameItemMeta,
type GridInventory,
validatePlacement,
- placeItem,
transformShape,
} from "boardgame-core/samples/slay-the-spire-like";
+import { dragDropEventEffect, DragDropEventType } from "boardgame-phaser";
+import { DisposableBag } from "boardgame-phaser";
-export interface DragState {
+export interface DragSession {
itemId: string;
itemShape: InventoryItem["shape"];
itemTransform: InventoryItem["transform"];
itemMeta: InventoryItem["meta"];
ghostContainer: Phaser.GameObjects.Container;
previewGraphics: Phaser.GameObjects.Graphics;
- dragOffsetX: number;
- dragOffsetY: number;
+ disposables: DisposableBag;
}
export interface DragControllerOptions {
@@ -28,21 +28,20 @@ export interface DragControllerOptions {
gridY: number;
getInventory: () => GridInventory;
getItemColor: (itemId: string) => number;
- getItemCells: (
- item: InventoryItem,
- ) => { x: number; y: number }[];
onPlaceItem: (item: InventoryItem) => void;
onCreateLostItem: (
itemId: string,
shape: InventoryItem["shape"],
transform: InventoryItem["transform"],
meta: InventoryItem["meta"],
+ x: number,
+ y: number,
) => void;
}
/**
- * Manages drag-and-drop state and logic for inventory items.
- * Handles ghost visuals, placement preview, rotation, and validation.
+ * Event-driven drag controller using dragDropEventEffect from boardgame-phaser.
+ * Manages ghost visuals, placement preview, rotation, and validation.
*/
export class DragController {
private scene: Phaser.Scene;
@@ -53,18 +52,17 @@ export class DragController {
private gridY: number;
private getInventory: () => GridInventory;
private getItemColor: (itemId: string) => number;
- private getItemCells: (
- item: InventoryItem,
- ) => { x: number; y: number }[];
private onPlaceItem: (item: InventoryItem) => void;
private onCreateLostItem: (
itemId: string,
shape: InventoryItem["shape"],
transform: InventoryItem["transform"],
meta: InventoryItem["meta"],
+ x: number,
+ y: number,
) => void;
- private dragState: DragState | null = null;
+ private activeSession: DragSession | null = null;
constructor(options: DragControllerOptions) {
this.scene = options.scene;
@@ -75,66 +73,44 @@ export class DragController {
this.gridY = options.gridY;
this.getInventory = options.getInventory;
this.getItemColor = options.getItemColor;
- this.getItemCells = options.getItemCells;
this.onPlaceItem = options.onPlaceItem;
this.onCreateLostItem = options.onCreateLostItem;
}
/**
- * Start dragging an item from the inventory.
+ * Start a drag session for an inventory item.
+ * Uses dragDropEventEffect for pointer tracking and event emission.
*/
- startDrag(itemId: string, pointer: Phaser.Input.Pointer): void {
- const inventory = this.getInventory();
- const item = inventory.items.get(itemId);
- if (!item) return;
-
+ startDrag(
+ itemId: string,
+ item: InventoryItem,
+ itemContainer: Phaser.GameObjects.Container,
+ ): () => void {
const cells = this.getItemCells(item);
const firstCell = cells[0];
- const itemWorldX =
+ const worldX =
this.container.x +
this.gridX +
firstCell.x * (this.cellSize + this.gridGap);
- const itemWorldY =
+ const worldY =
this.container.y +
this.gridY +
firstCell.y * (this.cellSize + this.gridGap);
- const dragOffsetX = pointer.x - itemWorldX;
- const dragOffsetY = pointer.y - itemWorldY;
-
- const ghostContainer = this.scene.add
- .container(itemWorldX, itemWorldY)
- .setDepth(1000);
- const ghostGraphics = this.scene.add.graphics();
- const color = this.getItemColor(itemId);
-
- for (let y = 0; y < item.shape.height; y++) {
- for (let x = 0; x < item.shape.width; x++) {
- if (item.shape.grid[y]?.[x]) {
- ghostGraphics.fillStyle(color, 0.7);
- ghostGraphics.fillRect(
- x * (this.cellSize + this.gridGap),
- y * (this.cellSize + this.gridGap),
- this.cellSize - 2,
- this.cellSize - 2,
- );
- ghostGraphics.lineStyle(2, 0xffffff);
- ghostGraphics.strokeRect(
- x * (this.cellSize + this.gridGap),
- y * (this.cellSize + this.gridGap),
- this.cellSize,
- this.cellSize,
- );
- }
- }
- }
- ghostContainer.add(ghostGraphics);
+ const ghostContainer = this.createGhostContainer(
+ worldX,
+ worldY,
+ item.shape,
+ item.transform,
+ this.getItemColor(itemId),
+ );
const previewGraphics = this.scene.add
.graphics()
.setDepth(999)
.setAlpha(0.5);
- this.dragState = {
+ const disposables = new DisposableBag();
+ const session: DragSession = {
itemId,
itemShape: item.shape,
itemTransform: {
@@ -144,26 +120,160 @@ export class DragController {
itemMeta: item.meta,
ghostContainer,
previewGraphics,
- dragOffsetX,
- dragOffsetY,
+ disposables,
+ };
+
+ this.activeSession = session;
+
+ // Set up drag-drop event handling via framework utility
+ const disposeDrag = dragDropEventEffect(
+ itemContainer as Phaser.GameObjects.GameObject,
+ disposables,
+ );
+
+ itemContainer.on("dragstart", () => {
+ ghostContainer.setVisible(true);
+ });
+
+ itemContainer.on("dragmove", () => {
+ this.handleDragMove(session);
+ });
+
+ itemContainer.on("dragend", () => {
+ this.handleDragEnd(session);
+ disposeDrag();
+ this.activeSession = null;
+ });
+
+ return () => {
+ disposeDrag();
+ this.destroySession(session);
+ this.activeSession = null;
};
}
/**
- * Start dragging a lost item (item that was dropped outside valid placement).
+ * Start a drag session for a lost item.
*/
startLostItemDrag(
itemId: string,
shape: InventoryItem["shape"],
transform: InventoryItem["transform"],
meta: InventoryItem["meta"],
- pointer: Phaser.Input.Pointer,
- ): void {
- const ghostContainer = this.scene.add
- .container(pointer.x, pointer.y)
- .setDepth(1000);
+ lostContainer: Phaser.GameObjects.Container,
+ ): () => void {
+ const pointer = this.scene.input.activePointer;
+ const ghostContainer = this.createGhostContainer(
+ pointer.x,
+ pointer.y,
+ shape,
+ transform,
+ this.getItemColor(itemId),
+ );
+ const previewGraphics = this.scene.add
+ .graphics()
+ .setDepth(999)
+ .setAlpha(0.5);
+
+ const disposables = new DisposableBag();
+ const session: DragSession = {
+ itemId,
+ itemShape: shape,
+ itemTransform: { ...transform, offset: { ...transform.offset } },
+ itemMeta: meta,
+ ghostContainer,
+ previewGraphics,
+ disposables,
+ };
+
+ this.activeSession = session;
+
+ const disposeDrag = dragDropEventEffect(
+ lostContainer as Phaser.GameObjects.GameObject,
+ disposables,
+ );
+
+ lostContainer.on("dragstart", () => {
+ ghostContainer.setVisible(true);
+ });
+
+ lostContainer.on("dragmove", () => {
+ this.handleDragMove(session);
+ });
+
+ lostContainer.on("dragend", () => {
+ this.handleDragEnd(session);
+ disposeDrag();
+ this.activeSession = null;
+ });
+
+ return () => {
+ disposeDrag();
+ this.destroySession(session);
+ this.activeSession = null;
+ };
+ }
+
+ /**
+ * Rotate the currently dragged item by 90 degrees.
+ */
+ rotateDraggedItem(): void {
+ if (!this.activeSession) return;
+
+ const currentRotation =
+ (this.activeSession.itemTransform.rotation + 90) % 360;
+ this.activeSession.itemTransform = {
+ ...this.activeSession.itemTransform,
+ rotation: currentRotation,
+ };
+
+ this.updateGhostVisuals(this.activeSession);
+ }
+
+ /**
+ * Check if currently dragging.
+ */
+ isDragging(): boolean {
+ return this.activeSession !== null;
+ }
+
+ /**
+ * Get the ID of the item being dragged, or null.
+ */
+ getDraggedItemId(): string | null {
+ return this.activeSession?.itemId ?? null;
+ }
+
+ /**
+ * Get the current position of the dragged ghost container.
+ */
+ getDraggedItemPosition(): { x: number; y: number } {
+ if (!this.activeSession) return { x: 0, y: 0 };
+ return {
+ x: this.activeSession.ghostContainer.x,
+ y: this.activeSession.ghostContainer.y,
+ };
+ }
+
+ /**
+ * Clean up active session and destroy all visuals.
+ */
+ destroy(): void {
+ if (this.activeSession) {
+ this.destroySession(this.activeSession);
+ this.activeSession = null;
+ }
+ }
+
+ private createGhostContainer(
+ x: number,
+ y: number,
+ shape: InventoryItem["shape"],
+ transform: InventoryItem["transform"],
+ color: number,
+ ): Phaser.GameObjects.Container {
+ const ghostContainer = this.scene.add.container(x, y).setDepth(1000);
const ghostGraphics = this.scene.add.graphics();
- const color = this.getItemColor(itemId);
const cells = transformShape(shape, transform);
for (const cell of cells) {
@@ -184,52 +294,15 @@ export class DragController {
}
ghostContainer.add(ghostGraphics);
- const previewGraphics = this.scene.add
- .graphics()
- .setDepth(999)
- .setAlpha(0.5);
-
- this.dragState = {
- itemId,
- itemShape: shape,
- itemTransform: { ...transform, offset: { ...transform.offset } },
- itemMeta: meta,
- ghostContainer,
- previewGraphics,
- dragOffsetX: 0,
- dragOffsetY: 0,
- };
+ return ghostContainer;
}
- /**
- * Rotate the currently dragged item by 90 degrees.
- */
- rotateDraggedItem(): void {
- if (!this.dragState) return;
-
- const currentRotation = (this.dragState.itemTransform.rotation + 90) % 360;
- this.dragState.itemTransform = {
- ...this.dragState.itemTransform,
- rotation: currentRotation,
- };
-
- this.updateGhostVisuals();
- }
-
- /**
- * Update ghost visuals to reflect current drag state (after rotation).
- */
- private updateGhostVisuals(): void {
- if (!this.dragState) return;
-
- this.dragState.ghostContainer.removeAll(true);
+ private updateGhostVisuals(session: DragSession): void {
+ session.ghostContainer.removeAll(true);
const ghostGraphics = this.scene.add.graphics();
- const color = this.getItemColor(this.dragState.itemId);
+ const color = this.getItemColor(session.itemId);
- const cells = transformShape(
- this.dragState.itemShape,
- this.dragState.itemTransform,
- );
+ const cells = transformShape(session.itemShape, session.itemTransform);
for (const cell of cells) {
ghostGraphics.fillStyle(color, 0.7);
ghostGraphics.fillRect(
@@ -246,68 +319,58 @@ export class DragController {
this.cellSize,
);
}
- this.dragState.ghostContainer.add(ghostGraphics);
+ session.ghostContainer.add(ghostGraphics);
}
- /**
- * Handle pointer movement during drag: update ghost position and placement preview.
- */
- onPointerMove(pointer: Phaser.Input.Pointer): void {
- if (!this.dragState) return;
+ private handleDragMove(session: DragSession): void {
+ const pointer = this.scene.input.activePointer;
+ session.ghostContainer.setPosition(pointer.x, pointer.y);
- this.dragState.ghostContainer.setPosition(
- pointer.x - this.dragState.dragOffsetX,
- pointer.y - this.dragState.dragOffsetY,
- );
-
- const gridCell = this.getWorldGridCell(
- pointer.x - this.dragState.dragOffsetX,
- pointer.y - this.dragState.dragOffsetY,
- );
- this.dragState.previewGraphics.clear();
+ const gridCell = this.getWorldGridCell(pointer.x, pointer.y);
+ session.previewGraphics.clear();
if (gridCell) {
const inventory = this.getInventory();
const testTransform = {
- ...this.dragState.itemTransform,
+ ...session.itemTransform,
offset: { x: gridCell.x, y: gridCell.y },
};
const validation = validatePlacement(
inventory,
- this.dragState.itemShape,
+ session.itemShape,
testTransform,
);
- const cells = transformShape(this.dragState.itemShape, testTransform);
+ const cells = transformShape(session.itemShape, testTransform);
for (const cell of cells) {
const px = this.gridX + cell.x * (this.cellSize + this.gridGap);
const py = this.gridY + cell.y * (this.cellSize + this.gridGap);
if (validation.valid) {
- this.dragState.previewGraphics.fillStyle(0x33ff33, 0.3);
- this.dragState.previewGraphics.fillRect(
+ session.previewGraphics.fillStyle(0x33ff33, 0.3);
+ session.previewGraphics.fillRect(
px,
py,
this.cellSize,
this.cellSize,
);
- this.dragState.previewGraphics.lineStyle(2, 0x33ff33);
- this.dragState.previewGraphics.strokeRect(
+ session.previewGraphics.lineStyle(2, 0x33ff33);
+ session.previewGraphics.strokeRect(
px,
py,
this.cellSize,
this.cellSize,
);
} else {
- this.dragState.previewGraphics.fillStyle(0xff3333, 0.3);
- this.dragState.previewGraphics.fillRect(
+ session.previewGraphics.fillStyle(0xff3333, 0.3);
+ session.previewGraphics.fillRect(
px,
py,
this.cellSize,
this.cellSize,
);
- this.dragState.previewGraphics.lineStyle(2, 0xff3333);
- this.dragState.previewGraphics.strokeRect(
+ session.previewGraphics.lineStyle(2, 0xff3333);
+ session.previewGraphics.strokeRect(
px,
py,
this.cellSize,
@@ -318,63 +381,56 @@ export class DragController {
}
}
- /**
- * Handle pointer release: validate placement and either place item or create lost item.
- */
- onPointerUp(pointer: Phaser.Input.Pointer): void {
- if (!this.dragState) return;
-
- const gridCell = this.getWorldGridCell(
- pointer.x - this.dragState.dragOffsetX,
- pointer.y - this.dragState.dragOffsetY,
- );
+ private handleDragEnd(session: DragSession): void {
+ const pointer = this.scene.input.activePointer;
+ const gridCell = this.getWorldGridCell(pointer.x, pointer.y);
const inventory = this.getInventory();
- this.dragState.ghostContainer.destroy();
- this.dragState.previewGraphics.destroy();
+ session.ghostContainer.destroy();
+ session.previewGraphics.destroy();
+ session.disposables.dispose();
if (gridCell) {
const testTransform = {
- ...this.dragState.itemTransform,
+ ...session.itemTransform,
offset: { x: gridCell.x, y: gridCell.y },
};
const validation = validatePlacement(
inventory,
- this.dragState.itemShape,
+ session.itemShape,
testTransform,
);
if (validation.valid) {
const item: InventoryItem = {
- id: this.dragState.itemId,
- shape: this.dragState.itemShape,
+ id: session.itemId,
+ shape: session.itemShape,
transform: testTransform,
- meta: this.dragState.itemMeta,
+ meta: session.itemMeta,
};
this.onPlaceItem(item);
} else {
this.onCreateLostItem(
- this.dragState.itemId,
- this.dragState.itemShape,
- this.dragState.itemTransform,
- this.dragState.itemMeta,
+ session.itemId,
+ session.itemShape,
+ session.itemTransform,
+ session.itemMeta,
+ session.ghostContainer.x,
+ session.ghostContainer.y,
);
}
} else {
this.onCreateLostItem(
- this.dragState.itemId,
- this.dragState.itemShape,
- this.dragState.itemTransform,
- this.dragState.itemMeta,
+ session.itemId,
+ session.itemShape,
+ session.itemTransform,
+ session.itemMeta,
+ session.ghostContainer.x,
+ session.ghostContainer.y,
);
}
-
- this.dragState = null;
}
- /**
- * Convert world coordinates to grid cell coordinates.
- */
private getWorldGridCell(
worldX: number,
worldY: number,
@@ -398,36 +454,24 @@ export class DragController {
return { x: cellX, y: cellY };
}
- /**
- * Check if an item is currently being dragged.
- */
- isDragging(): boolean {
- return this.dragState !== null;
- }
-
- /**
- * Get the ID of the item being dragged, or null.
- */
- getDraggedItemId(): string | null {
- return this.dragState?.itemId ?? null;
- }
-
- getDraggedItemPosition(): { x: number; y: number } {
- if (!this.dragState) return { x: 0, y: 0 };
- return {
- x: this.dragState.ghostContainer.x,
- y: this.dragState.ghostContainer.y,
- };
- }
-
- /**
- * Clean up drag state and visuals.
- */
- destroy(): void {
- if (this.dragState) {
- this.dragState.ghostContainer.destroy();
- this.dragState.previewGraphics.destroy();
- this.dragState = null;
+ private getItemCells(
+ item: InventoryItem,
+ ): { x: number; y: number }[] {
+ const cells: { x: number; y: number }[] = [];
+ const { offset } = item.transform;
+ for (let y = 0; y < item.shape.height; y++) {
+ for (let x = 0; x < item.shape.width; x++) {
+ if (item.shape.grid[y]?.[x]) {
+ cells.push({ x: x + offset.x, y: y + offset.y });
+ }
+ }
}
+ return cells;
+ }
+
+ private destroySession(session: DragSession): void {
+ session.disposables.dispose();
+ session.ghostContainer.destroy();
+ session.previewGraphics.destroy();
}
}
diff --git a/packages/sts-like-viewer/src/widgets/InventoryWidget.ts b/packages/sts-like-viewer/src/widgets/InventoryWidget.ts
index cd15da6..b0428a1 100644
--- a/packages/sts-like-viewer/src/widgets/InventoryWidget.ts
+++ b/packages/sts-like-viewer/src/widgets/InventoryWidget.ts
@@ -25,6 +25,7 @@ export interface InventoryWidgetOptions {
/**
* Thin orchestrator for the inventory grid widget.
* Delegates rendering, drag logic, and lost-item management to focused modules.
+ * Uses event-driven drag via dragDropEventEffect from boardgame-phaser.
*/
export class InventoryWidget {
private scene: Phaser.Scene;
@@ -40,9 +41,7 @@ export class InventoryWidget {
private dragController: DragController;
private lostItemManager: LostItemManager;
- private pointerMoveHandler!: (pointer: Phaser.Input.Pointer) => void;
- private pointerUpHandler!: (pointer: Phaser.Input.Pointer) => void;
- private pointerDownHandler!: (pointer: Phaser.Input.Pointer) => void;
+ private rightClickHandler!: (pointer: Phaser.Input.Pointer) => void;
constructor(options: InventoryWidgetOptions) {
this.scene = options.scene;
@@ -52,14 +51,9 @@ export class InventoryWidget {
this.isLocked = options.isLocked ?? false;
const inventory = this.gameState.value.inventory;
- const gridW =
- inventory.width * this.cellSize + (inventory.width - 1) * this.gridGap;
- const gridH =
- inventory.height * this.cellSize + (inventory.height - 1) * this.gridGap;
this.container = this.scene.add.container(options.x, options.y);
- // Initialize sub-modules
this.renderer = new ItemRenderer({
scene: this.scene,
container: this.container,
@@ -78,10 +72,9 @@ export class InventoryWidget {
gridY: this.gridY,
getInventory: () => this.getInventory(),
getItemColor: (id) => this.renderer.getItemColor(id),
- getItemCells: (item) => this.renderer.getItemCells(item),
onPlaceItem: (item) => this.handlePlaceItem(item),
- onCreateLostItem: (id, shape, transform, meta) =>
- this.handleCreateLostItem(id, shape, transform, meta),
+ onCreateLostItem: (id, shape, transform, meta, x, y) =>
+ this.handleCreateLostItem(id, shape, transform, meta, x, y),
});
this.lostItemManager = new LostItemManager({
@@ -89,13 +82,13 @@ export class InventoryWidget {
cellSize: this.cellSize,
gridGap: this.gridGap,
getItemColor: (id) => this.renderer.getItemColor(id),
- onLostItemDragStart: (id, pointer) =>
+ onLostItemDragStart: (id, lostContainer) =>
this.dragController.startLostItemDrag(
id,
this.getLostItemShape(id),
this.getLostItemTransform(id),
this.getLostItemMeta(id),
- pointer,
+ lostContainer,
),
isDragging: () => this.dragController.isDragging(),
});
@@ -117,13 +110,14 @@ export class InventoryWidget {
for (const [itemId, item] of inventory.items) {
if (this.renderer.hasItem(itemId)) continue;
const visuals = this.renderer.createItemVisuals(itemId, item);
- this.setupItemInteraction(itemId, visuals);
+ this.setupItemInteraction(itemId, visuals, item);
}
}
private setupItemInteraction(
itemId: string,
visuals: ReturnType,
+ item: InventoryItem,
): void {
visuals.container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
if (this.isLocked) return;
@@ -133,30 +127,20 @@ export class InventoryWidget {
removeItemFromGrid(state.inventory, itemId);
});
this.renderer.removeItemVisuals(itemId);
- this.dragController.startDrag(itemId, pointer);
+ this.dragController.startDrag(itemId, item, visuals.container);
}
});
}
private setupInput(): void {
- this.pointerDownHandler = (pointer: Phaser.Input.Pointer) => {
+ this.rightClickHandler = (pointer: Phaser.Input.Pointer) => {
if (!this.dragController.isDragging()) return;
if (pointer.button === 1) {
this.dragController.rotateDraggedItem();
}
};
- this.pointerMoveHandler = (pointer: Phaser.Input.Pointer) => {
- this.dragController.onPointerMove(pointer);
- };
-
- this.pointerUpHandler = (pointer: Phaser.Input.Pointer) => {
- this.dragController.onPointerUp(pointer);
- };
-
- this.scene.input.on("pointermove", this.pointerMoveHandler);
- this.scene.input.on("pointerup", this.pointerUpHandler);
- this.scene.input.on("pointerdown", this.pointerDownHandler);
+ this.scene.input.on("pointerdown", this.rightClickHandler);
}
private handlePlaceItem(item: InventoryItem): void {
@@ -167,7 +151,7 @@ export class InventoryWidget {
const placedItem = inventory.items.get(item.id);
if (placedItem) {
const visuals = this.renderer.createItemVisuals(item.id, placedItem);
- this.setupItemInteraction(item.id, visuals);
+ this.setupItemInteraction(item.id, visuals, placedItem);
}
}
@@ -176,15 +160,10 @@ export class InventoryWidget {
shape: InventoryItem["shape"],
transform: InventoryItem["transform"],
meta: InventoryItem["meta"],
+ x: number,
+ y: number,
): void {
- this.lostItemManager.createLostItem(
- itemId,
- shape,
- transform,
- meta,
- this.dragController.getDraggedItemPosition().x,
- this.dragController.getDraggedItemPosition().y,
- );
+ this.lostItemManager.createLostItem(itemId, shape, transform, meta, x, y);
}
private getLostItemShape(itemId: string) {
@@ -214,13 +193,6 @@ export class InventoryWidget {
public refresh(): void {
const inventory = this.getInventory();
- // Remove visuals for items no longer in inventory
- for (const [itemId] of inventory.items.entries()) {
- // We need a way to track which items have visuals
- // For now, clear and redraw
- }
-
- // Simple approach: destroy all and redraw
this.renderer.destroy();
this.renderer = new ItemRenderer({
scene: this.scene,
@@ -235,9 +207,7 @@ export class InventoryWidget {
}
public destroy(): void {
- this.scene.input.off("pointermove", this.pointerMoveHandler);
- this.scene.input.off("pointerup", this.pointerUpHandler);
- this.scene.input.off("pointerdown", this.pointerDownHandler);
+ this.scene.input.off("pointerdown", this.rightClickHandler);
this.dragController.destroy();
this.lostItemManager.destroy();
diff --git a/packages/sts-like-viewer/src/widgets/LostItemManager.ts b/packages/sts-like-viewer/src/widgets/LostItemManager.ts
index e085e13..23f202f 100644
--- a/packages/sts-like-viewer/src/widgets/LostItemManager.ts
+++ b/packages/sts-like-viewer/src/widgets/LostItemManager.ts
@@ -17,7 +17,10 @@ export interface LostItemManagerOptions {
cellSize: number;
gridGap: number;
getItemColor: (itemId: string) => number;
- onLostItemDragStart: (itemId: string, pointer: Phaser.Input.Pointer) => void;
+ onLostItemDragStart: (
+ itemId: string,
+ container: Phaser.GameObjects.Container,
+ ) => void;
isDragging: () => boolean;
}
@@ -32,7 +35,7 @@ export class LostItemManager {
private getItemColor: (itemId: string) => number;
private onLostItemDragStart: (
itemId: string,
- pointer: Phaser.Input.Pointer,
+ container: Phaser.GameObjects.Container,
) => void;
private isDragging: () => boolean;
@@ -48,37 +51,35 @@ export class LostItemManager {
}
/**
- * Create a visual representation of a lost item.
+ * Create a visual representation of a lost item at the given position.
*/
createLostItem(
itemId: string,
shape: InventoryItem["shape"],
transform: InventoryItem["transform"],
meta: InventoryItem["meta"],
- positionX: number,
- positionY: number,
+ x: number,
+ y: number,
): void {
- const container = this.scene.add
- .container(positionX, positionY)
- .setDepth(500);
+ const container = this.scene.add.container(x, y).setDepth(500);
const graphics = this.scene.add.graphics();
const color = this.getItemColor(itemId);
- for (let y = 0; y < shape.height; y++) {
- for (let x = 0; x < shape.width; x++) {
- if (shape.grid[y]?.[x]) {
+ for (let gy = 0; gy < shape.height; gy++) {
+ for (let gx = 0; gx < shape.width; gx++) {
+ if (shape.grid[gy]?.[gx]) {
graphics.fillStyle(color, 0.5);
graphics.fillRect(
- x * (this.cellSize + this.gridGap),
- y * (this.cellSize + this.gridGap),
+ gx * (this.cellSize + this.gridGap),
+ gy * (this.cellSize + this.gridGap),
this.cellSize - 2,
this.cellSize - 2,
);
graphics.lineStyle(2, 0xff4444);
graphics.strokeRect(
- x * (this.cellSize + this.gridGap),
- y * (this.cellSize + this.gridGap),
+ gx * (this.cellSize + this.gridGap),
+ gy * (this.cellSize + this.gridGap),
this.cellSize,
this.cellSize,
);
@@ -108,7 +109,7 @@ export class LostItemManager {
container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
if (this.isDragging()) return;
if (pointer.button === 0) {
- this.onLostItemDragStart(itemId, pointer);
+ this.onLostItemDragStart(itemId, container);
}
});
diff --git a/packages/sts-like-viewer/tsconfig.json b/packages/sts-like-viewer/tsconfig.json
index d7f3323..9974bb0 100644
--- a/packages/sts-like-viewer/tsconfig.json
+++ b/packages/sts-like-viewer/tsconfig.json
@@ -3,14 +3,15 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
- "@/*": ["src/*"]
+ "@/*": ["src/*"],
+ "boardgame-phaser": ["../framework/src/index.ts"],
},
"jsx": "react-jsx",
"jsxImportSource": "preact",
"noEmit": true,
"declaration": false,
"declarationMap": false,
- "sourceMap": false
+ "sourceMap": false,
},
- "include": ["src/**/*"]
+ "include": ["src/**/*"],
}