Refactor inventory drag to use dragDropEventEffect
Replace manual pointer event handling with the framework's dragDropEventEffect utility. Update DragController to manage DisposableBag for cleanup and pass containers instead of pointers to drag callbacks. Add framework path alias to tsconfig and fix loop variable shadowing in LostItemManager.
This commit is contained in:
parent
88d0c5bf55
commit
a7095c37fc
|
|
@ -1,13 +1,13 @@
|
||||||
import { h } from 'preact';
|
import { h } from "preact";
|
||||||
import { PhaserGame, PhaserScene } from 'boardgame-phaser';
|
import { PhaserGame, PhaserScene } from "boardgame-phaser";
|
||||||
import { useMemo } from 'preact/hooks';
|
import { useMemo } from "preact/hooks";
|
||||||
import { IndexScene } from '@/scenes/IndexScene';
|
import { IndexScene } from "@/scenes/IndexScene";
|
||||||
import { MapViewerScene } from '@/scenes/MapViewerScene';
|
import { MapViewerScene } from "@/scenes/MapViewerScene";
|
||||||
import { GridViewerScene } from '@/scenes/GridViewerScene';
|
import { GridViewerScene } from "@/scenes/GridViewerScene";
|
||||||
import { ShapeViewerScene } from '@/scenes/ShapeViewerScene';
|
import { ShapeViewerScene } from "@/scenes/ShapeViewerScene";
|
||||||
import { GameFlowScene } from '@/scenes/GameFlowScene';
|
import { GameFlowScene } from "@/scenes/GameFlowScene";
|
||||||
import { PlaceholderEncounterScene } from '@/scenes/PlaceholderEncounterScene';
|
import { PlaceholderEncounterScene } from "@/scenes/PlaceholderEncounterScene";
|
||||||
import { createGameState } from '@/state/gameState';
|
import { createGameState } from "@/state/gameState";
|
||||||
|
|
||||||
// 全局游戏状态单例
|
// 全局游戏状态单例
|
||||||
const gameState = createGameState();
|
const gameState = createGameState();
|
||||||
|
|
@ -18,18 +18,36 @@ export default function App() {
|
||||||
const gridViewerScene = useMemo(() => new GridViewerScene(), []);
|
const gridViewerScene = useMemo(() => new GridViewerScene(), []);
|
||||||
const shapeViewerScene = useMemo(() => new ShapeViewerScene(), []);
|
const shapeViewerScene = useMemo(() => new ShapeViewerScene(), []);
|
||||||
const gameFlowScene = useMemo(() => new GameFlowScene(gameState), []);
|
const gameFlowScene = useMemo(() => new GameFlowScene(gameState), []);
|
||||||
const placeholderEncounterScene = useMemo(() => new PlaceholderEncounterScene(gameState), []);
|
const placeholderEncounterScene = useMemo(
|
||||||
|
() => new PlaceholderEncounterScene(gameState),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-screen">
|
<div className="flex flex-col h-screen">
|
||||||
<div className="flex-1 flex relative justify-center items-center">
|
<div className="flex-1 flex relative justify-center items-center">
|
||||||
<PhaserGame initialScene="IndexScene" config={{ width: 1920, height: 1080 }}>
|
<PhaserGame
|
||||||
<PhaserScene sceneKey="IndexScene" scene={indexScene} />
|
initialScene="IndexScene"
|
||||||
<PhaserScene sceneKey="MapViewerScene" scene={mapViewerScene} />
|
config={{ width: 1920, height: 1080 }}
|
||||||
<PhaserScene sceneKey="GridViewerScene" scene={gridViewerScene} />
|
>
|
||||||
<PhaserScene sceneKey="ShapeViewerScene" scene={shapeViewerScene} />
|
<PhaserScene sceneKey="IndexScene" scene={indexScene as any} />
|
||||||
<PhaserScene sceneKey="GameFlowScene" scene={gameFlowScene} />
|
<PhaserScene
|
||||||
<PhaserScene sceneKey="PlaceholderEncounterScene" scene={placeholderEncounterScene} />
|
sceneKey="MapViewerScene"
|
||||||
|
scene={mapViewerScene as any}
|
||||||
|
/>
|
||||||
|
<PhaserScene
|
||||||
|
sceneKey="GridViewerScene"
|
||||||
|
scene={gridViewerScene as any}
|
||||||
|
/>
|
||||||
|
<PhaserScene
|
||||||
|
sceneKey="ShapeViewerScene"
|
||||||
|
scene={shapeViewerScene as any}
|
||||||
|
/>
|
||||||
|
<PhaserScene sceneKey="GameFlowScene" scene={gameFlowScene as any} />
|
||||||
|
<PhaserScene
|
||||||
|
sceneKey="PlaceholderEncounterScene"
|
||||||
|
scene={placeholderEncounterScene as any}
|
||||||
|
/>
|
||||||
</PhaserGame>
|
</PhaserGame>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,19 @@ import {
|
||||||
type GameItemMeta,
|
type GameItemMeta,
|
||||||
type GridInventory,
|
type GridInventory,
|
||||||
validatePlacement,
|
validatePlacement,
|
||||||
placeItem,
|
|
||||||
transformShape,
|
transformShape,
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
} 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;
|
itemId: string;
|
||||||
itemShape: InventoryItem<GameItemMeta>["shape"];
|
itemShape: InventoryItem<GameItemMeta>["shape"];
|
||||||
itemTransform: InventoryItem<GameItemMeta>["transform"];
|
itemTransform: InventoryItem<GameItemMeta>["transform"];
|
||||||
itemMeta: InventoryItem<GameItemMeta>["meta"];
|
itemMeta: InventoryItem<GameItemMeta>["meta"];
|
||||||
ghostContainer: Phaser.GameObjects.Container;
|
ghostContainer: Phaser.GameObjects.Container;
|
||||||
previewGraphics: Phaser.GameObjects.Graphics;
|
previewGraphics: Phaser.GameObjects.Graphics;
|
||||||
dragOffsetX: number;
|
disposables: DisposableBag;
|
||||||
dragOffsetY: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DragControllerOptions {
|
export interface DragControllerOptions {
|
||||||
|
|
@ -28,21 +28,20 @@ export interface DragControllerOptions {
|
||||||
gridY: number;
|
gridY: number;
|
||||||
getInventory: () => GridInventory<GameItemMeta>;
|
getInventory: () => GridInventory<GameItemMeta>;
|
||||||
getItemColor: (itemId: string) => number;
|
getItemColor: (itemId: string) => number;
|
||||||
getItemCells: (
|
|
||||||
item: InventoryItem<GameItemMeta>,
|
|
||||||
) => { x: number; y: number }[];
|
|
||||||
onPlaceItem: (item: InventoryItem<GameItemMeta>) => void;
|
onPlaceItem: (item: InventoryItem<GameItemMeta>) => void;
|
||||||
onCreateLostItem: (
|
onCreateLostItem: (
|
||||||
itemId: string,
|
itemId: string,
|
||||||
shape: InventoryItem<GameItemMeta>["shape"],
|
shape: InventoryItem<GameItemMeta>["shape"],
|
||||||
transform: InventoryItem<GameItemMeta>["transform"],
|
transform: InventoryItem<GameItemMeta>["transform"],
|
||||||
meta: InventoryItem<GameItemMeta>["meta"],
|
meta: InventoryItem<GameItemMeta>["meta"],
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages drag-and-drop state and logic for inventory items.
|
* Event-driven drag controller using dragDropEventEffect from boardgame-phaser.
|
||||||
* Handles ghost visuals, placement preview, rotation, and validation.
|
* Manages ghost visuals, placement preview, rotation, and validation.
|
||||||
*/
|
*/
|
||||||
export class DragController {
|
export class DragController {
|
||||||
private scene: Phaser.Scene;
|
private scene: Phaser.Scene;
|
||||||
|
|
@ -53,18 +52,17 @@ export class DragController {
|
||||||
private gridY: number;
|
private gridY: number;
|
||||||
private getInventory: () => GridInventory<GameItemMeta>;
|
private getInventory: () => GridInventory<GameItemMeta>;
|
||||||
private getItemColor: (itemId: string) => number;
|
private getItemColor: (itemId: string) => number;
|
||||||
private getItemCells: (
|
|
||||||
item: InventoryItem<GameItemMeta>,
|
|
||||||
) => { x: number; y: number }[];
|
|
||||||
private onPlaceItem: (item: InventoryItem<GameItemMeta>) => void;
|
private onPlaceItem: (item: InventoryItem<GameItemMeta>) => void;
|
||||||
private onCreateLostItem: (
|
private onCreateLostItem: (
|
||||||
itemId: string,
|
itemId: string,
|
||||||
shape: InventoryItem<GameItemMeta>["shape"],
|
shape: InventoryItem<GameItemMeta>["shape"],
|
||||||
transform: InventoryItem<GameItemMeta>["transform"],
|
transform: InventoryItem<GameItemMeta>["transform"],
|
||||||
meta: InventoryItem<GameItemMeta>["meta"],
|
meta: InventoryItem<GameItemMeta>["meta"],
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
private dragState: DragState | null = null;
|
private activeSession: DragSession | null = null;
|
||||||
|
|
||||||
constructor(options: DragControllerOptions) {
|
constructor(options: DragControllerOptions) {
|
||||||
this.scene = options.scene;
|
this.scene = options.scene;
|
||||||
|
|
@ -75,66 +73,44 @@ export class DragController {
|
||||||
this.gridY = options.gridY;
|
this.gridY = options.gridY;
|
||||||
this.getInventory = options.getInventory;
|
this.getInventory = options.getInventory;
|
||||||
this.getItemColor = options.getItemColor;
|
this.getItemColor = options.getItemColor;
|
||||||
this.getItemCells = options.getItemCells;
|
|
||||||
this.onPlaceItem = options.onPlaceItem;
|
this.onPlaceItem = options.onPlaceItem;
|
||||||
this.onCreateLostItem = options.onCreateLostItem;
|
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 {
|
startDrag(
|
||||||
const inventory = this.getInventory();
|
itemId: string,
|
||||||
const item = inventory.items.get(itemId);
|
item: InventoryItem<GameItemMeta>,
|
||||||
if (!item) return;
|
itemContainer: Phaser.GameObjects.Container,
|
||||||
|
): () => void {
|
||||||
const cells = this.getItemCells(item);
|
const cells = this.getItemCells(item);
|
||||||
const firstCell = cells[0];
|
const firstCell = cells[0];
|
||||||
const itemWorldX =
|
const worldX =
|
||||||
this.container.x +
|
this.container.x +
|
||||||
this.gridX +
|
this.gridX +
|
||||||
firstCell.x * (this.cellSize + this.gridGap);
|
firstCell.x * (this.cellSize + this.gridGap);
|
||||||
const itemWorldY =
|
const worldY =
|
||||||
this.container.y +
|
this.container.y +
|
||||||
this.gridY +
|
this.gridY +
|
||||||
firstCell.y * (this.cellSize + this.gridGap);
|
firstCell.y * (this.cellSize + this.gridGap);
|
||||||
const dragOffsetX = pointer.x - itemWorldX;
|
|
||||||
const dragOffsetY = pointer.y - itemWorldY;
|
|
||||||
|
|
||||||
const ghostContainer = this.scene.add
|
const ghostContainer = this.createGhostContainer(
|
||||||
.container(itemWorldX, itemWorldY)
|
worldX,
|
||||||
.setDepth(1000);
|
worldY,
|
||||||
const ghostGraphics = this.scene.add.graphics();
|
item.shape,
|
||||||
const color = this.getItemColor(itemId);
|
item.transform,
|
||||||
|
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 previewGraphics = this.scene.add
|
const previewGraphics = this.scene.add
|
||||||
.graphics()
|
.graphics()
|
||||||
.setDepth(999)
|
.setDepth(999)
|
||||||
.setAlpha(0.5);
|
.setAlpha(0.5);
|
||||||
|
|
||||||
this.dragState = {
|
const disposables = new DisposableBag();
|
||||||
|
const session: DragSession = {
|
||||||
itemId,
|
itemId,
|
||||||
itemShape: item.shape,
|
itemShape: item.shape,
|
||||||
itemTransform: {
|
itemTransform: {
|
||||||
|
|
@ -144,26 +120,160 @@ export class DragController {
|
||||||
itemMeta: item.meta,
|
itemMeta: item.meta,
|
||||||
ghostContainer,
|
ghostContainer,
|
||||||
previewGraphics,
|
previewGraphics,
|
||||||
dragOffsetX,
|
disposables,
|
||||||
dragOffsetY,
|
};
|
||||||
|
|
||||||
|
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(
|
startLostItemDrag(
|
||||||
itemId: string,
|
itemId: string,
|
||||||
shape: InventoryItem<GameItemMeta>["shape"],
|
shape: InventoryItem<GameItemMeta>["shape"],
|
||||||
transform: InventoryItem<GameItemMeta>["transform"],
|
transform: InventoryItem<GameItemMeta>["transform"],
|
||||||
meta: InventoryItem<GameItemMeta>["meta"],
|
meta: InventoryItem<GameItemMeta>["meta"],
|
||||||
pointer: Phaser.Input.Pointer,
|
lostContainer: Phaser.GameObjects.Container,
|
||||||
): void {
|
): () => void {
|
||||||
const ghostContainer = this.scene.add
|
const pointer = this.scene.input.activePointer;
|
||||||
.container(pointer.x, pointer.y)
|
const ghostContainer = this.createGhostContainer(
|
||||||
.setDepth(1000);
|
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<GameItemMeta>["shape"],
|
||||||
|
transform: InventoryItem<GameItemMeta>["transform"],
|
||||||
|
color: number,
|
||||||
|
): Phaser.GameObjects.Container {
|
||||||
|
const ghostContainer = this.scene.add.container(x, y).setDepth(1000);
|
||||||
const ghostGraphics = this.scene.add.graphics();
|
const ghostGraphics = this.scene.add.graphics();
|
||||||
const color = this.getItemColor(itemId);
|
|
||||||
|
|
||||||
const cells = transformShape(shape, transform);
|
const cells = transformShape(shape, transform);
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
|
|
@ -184,52 +294,15 @@ export class DragController {
|
||||||
}
|
}
|
||||||
ghostContainer.add(ghostGraphics);
|
ghostContainer.add(ghostGraphics);
|
||||||
|
|
||||||
const previewGraphics = this.scene.add
|
return ghostContainer;
|
||||||
.graphics()
|
|
||||||
.setDepth(999)
|
|
||||||
.setAlpha(0.5);
|
|
||||||
|
|
||||||
this.dragState = {
|
|
||||||
itemId,
|
|
||||||
itemShape: shape,
|
|
||||||
itemTransform: { ...transform, offset: { ...transform.offset } },
|
|
||||||
itemMeta: meta,
|
|
||||||
ghostContainer,
|
|
||||||
previewGraphics,
|
|
||||||
dragOffsetX: 0,
|
|
||||||
dragOffsetY: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private updateGhostVisuals(session: DragSession): void {
|
||||||
* Rotate the currently dragged item by 90 degrees.
|
session.ghostContainer.removeAll(true);
|
||||||
*/
|
|
||||||
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);
|
|
||||||
const ghostGraphics = this.scene.add.graphics();
|
const ghostGraphics = this.scene.add.graphics();
|
||||||
const color = this.getItemColor(this.dragState.itemId);
|
const color = this.getItemColor(session.itemId);
|
||||||
|
|
||||||
const cells = transformShape(
|
const cells = transformShape(session.itemShape, session.itemTransform);
|
||||||
this.dragState.itemShape,
|
|
||||||
this.dragState.itemTransform,
|
|
||||||
);
|
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
ghostGraphics.fillStyle(color, 0.7);
|
ghostGraphics.fillStyle(color, 0.7);
|
||||||
ghostGraphics.fillRect(
|
ghostGraphics.fillRect(
|
||||||
|
|
@ -246,68 +319,58 @@ export class DragController {
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.dragState.ghostContainer.add(ghostGraphics);
|
session.ghostContainer.add(ghostGraphics);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private handleDragMove(session: DragSession): void {
|
||||||
* Handle pointer movement during drag: update ghost position and placement preview.
|
const pointer = this.scene.input.activePointer;
|
||||||
*/
|
session.ghostContainer.setPosition(pointer.x, pointer.y);
|
||||||
onPointerMove(pointer: Phaser.Input.Pointer): void {
|
|
||||||
if (!this.dragState) return;
|
|
||||||
|
|
||||||
this.dragState.ghostContainer.setPosition(
|
const gridCell = this.getWorldGridCell(pointer.x, pointer.y);
|
||||||
pointer.x - this.dragState.dragOffsetX,
|
session.previewGraphics.clear();
|
||||||
pointer.y - this.dragState.dragOffsetY,
|
|
||||||
);
|
|
||||||
|
|
||||||
const gridCell = this.getWorldGridCell(
|
|
||||||
pointer.x - this.dragState.dragOffsetX,
|
|
||||||
pointer.y - this.dragState.dragOffsetY,
|
|
||||||
);
|
|
||||||
this.dragState.previewGraphics.clear();
|
|
||||||
|
|
||||||
if (gridCell) {
|
if (gridCell) {
|
||||||
const inventory = this.getInventory();
|
const inventory = this.getInventory();
|
||||||
const testTransform = {
|
const testTransform = {
|
||||||
...this.dragState.itemTransform,
|
...session.itemTransform,
|
||||||
offset: { x: gridCell.x, y: gridCell.y },
|
offset: { x: gridCell.x, y: gridCell.y },
|
||||||
};
|
};
|
||||||
const validation = validatePlacement(
|
const validation = validatePlacement(
|
||||||
inventory,
|
inventory,
|
||||||
this.dragState.itemShape,
|
session.itemShape,
|
||||||
testTransform,
|
testTransform,
|
||||||
);
|
);
|
||||||
|
|
||||||
const cells = transformShape(this.dragState.itemShape, testTransform);
|
const cells = transformShape(session.itemShape, testTransform);
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
const px = this.gridX + cell.x * (this.cellSize + this.gridGap);
|
const px = this.gridX + cell.x * (this.cellSize + this.gridGap);
|
||||||
const py = this.gridY + cell.y * (this.cellSize + this.gridGap);
|
const py = this.gridY + cell.y * (this.cellSize + this.gridGap);
|
||||||
|
|
||||||
if (validation.valid) {
|
if (validation.valid) {
|
||||||
this.dragState.previewGraphics.fillStyle(0x33ff33, 0.3);
|
session.previewGraphics.fillStyle(0x33ff33, 0.3);
|
||||||
this.dragState.previewGraphics.fillRect(
|
session.previewGraphics.fillRect(
|
||||||
px,
|
px,
|
||||||
py,
|
py,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
);
|
);
|
||||||
this.dragState.previewGraphics.lineStyle(2, 0x33ff33);
|
session.previewGraphics.lineStyle(2, 0x33ff33);
|
||||||
this.dragState.previewGraphics.strokeRect(
|
session.previewGraphics.strokeRect(
|
||||||
px,
|
px,
|
||||||
py,
|
py,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.dragState.previewGraphics.fillStyle(0xff3333, 0.3);
|
session.previewGraphics.fillStyle(0xff3333, 0.3);
|
||||||
this.dragState.previewGraphics.fillRect(
|
session.previewGraphics.fillRect(
|
||||||
px,
|
px,
|
||||||
py,
|
py,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
);
|
);
|
||||||
this.dragState.previewGraphics.lineStyle(2, 0xff3333);
|
session.previewGraphics.lineStyle(2, 0xff3333);
|
||||||
this.dragState.previewGraphics.strokeRect(
|
session.previewGraphics.strokeRect(
|
||||||
px,
|
px,
|
||||||
py,
|
py,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
|
|
@ -318,63 +381,56 @@ export class DragController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private handleDragEnd(session: DragSession): void {
|
||||||
* Handle pointer release: validate placement and either place item or create lost item.
|
const pointer = this.scene.input.activePointer;
|
||||||
*/
|
const gridCell = this.getWorldGridCell(pointer.x, pointer.y);
|
||||||
onPointerUp(pointer: Phaser.Input.Pointer): void {
|
|
||||||
if (!this.dragState) return;
|
|
||||||
|
|
||||||
const gridCell = this.getWorldGridCell(
|
|
||||||
pointer.x - this.dragState.dragOffsetX,
|
|
||||||
pointer.y - this.dragState.dragOffsetY,
|
|
||||||
);
|
|
||||||
const inventory = this.getInventory();
|
const inventory = this.getInventory();
|
||||||
|
|
||||||
this.dragState.ghostContainer.destroy();
|
session.ghostContainer.destroy();
|
||||||
this.dragState.previewGraphics.destroy();
|
session.previewGraphics.destroy();
|
||||||
|
session.disposables.dispose();
|
||||||
|
|
||||||
if (gridCell) {
|
if (gridCell) {
|
||||||
const testTransform = {
|
const testTransform = {
|
||||||
...this.dragState.itemTransform,
|
...session.itemTransform,
|
||||||
offset: { x: gridCell.x, y: gridCell.y },
|
offset: { x: gridCell.x, y: gridCell.y },
|
||||||
};
|
};
|
||||||
const validation = validatePlacement(
|
const validation = validatePlacement(
|
||||||
inventory,
|
inventory,
|
||||||
this.dragState.itemShape,
|
session.itemShape,
|
||||||
testTransform,
|
testTransform,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (validation.valid) {
|
if (validation.valid) {
|
||||||
const item: InventoryItem<GameItemMeta> = {
|
const item: InventoryItem<GameItemMeta> = {
|
||||||
id: this.dragState.itemId,
|
id: session.itemId,
|
||||||
shape: this.dragState.itemShape,
|
shape: session.itemShape,
|
||||||
transform: testTransform,
|
transform: testTransform,
|
||||||
meta: this.dragState.itemMeta,
|
meta: session.itemMeta,
|
||||||
};
|
};
|
||||||
this.onPlaceItem(item);
|
this.onPlaceItem(item);
|
||||||
} else {
|
} else {
|
||||||
this.onCreateLostItem(
|
this.onCreateLostItem(
|
||||||
this.dragState.itemId,
|
session.itemId,
|
||||||
this.dragState.itemShape,
|
session.itemShape,
|
||||||
this.dragState.itemTransform,
|
session.itemTransform,
|
||||||
this.dragState.itemMeta,
|
session.itemMeta,
|
||||||
|
session.ghostContainer.x,
|
||||||
|
session.ghostContainer.y,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.onCreateLostItem(
|
this.onCreateLostItem(
|
||||||
this.dragState.itemId,
|
session.itemId,
|
||||||
this.dragState.itemShape,
|
session.itemShape,
|
||||||
this.dragState.itemTransform,
|
session.itemTransform,
|
||||||
this.dragState.itemMeta,
|
session.itemMeta,
|
||||||
|
session.ghostContainer.x,
|
||||||
|
session.ghostContainer.y,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dragState = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert world coordinates to grid cell coordinates.
|
|
||||||
*/
|
|
||||||
private getWorldGridCell(
|
private getWorldGridCell(
|
||||||
worldX: number,
|
worldX: number,
|
||||||
worldY: number,
|
worldY: number,
|
||||||
|
|
@ -398,36 +454,24 @@ export class DragController {
|
||||||
return { x: cellX, y: cellY };
|
return { x: cellX, y: cellY };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private getItemCells(
|
||||||
* Check if an item is currently being dragged.
|
item: InventoryItem<GameItemMeta>,
|
||||||
*/
|
): { x: number; y: number }[] {
|
||||||
isDragging(): boolean {
|
const cells: { x: number; y: number }[] = [];
|
||||||
return this.dragState !== null;
|
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 {
|
||||||
* Get the ID of the item being dragged, or null.
|
session.disposables.dispose();
|
||||||
*/
|
session.ghostContainer.destroy();
|
||||||
getDraggedItemId(): string | null {
|
session.previewGraphics.destroy();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ export interface InventoryWidgetOptions {
|
||||||
/**
|
/**
|
||||||
* Thin orchestrator for the inventory grid widget.
|
* Thin orchestrator for the inventory grid widget.
|
||||||
* Delegates rendering, drag logic, and lost-item management to focused modules.
|
* Delegates rendering, drag logic, and lost-item management to focused modules.
|
||||||
|
* Uses event-driven drag via dragDropEventEffect from boardgame-phaser.
|
||||||
*/
|
*/
|
||||||
export class InventoryWidget {
|
export class InventoryWidget {
|
||||||
private scene: Phaser.Scene;
|
private scene: Phaser.Scene;
|
||||||
|
|
@ -40,9 +41,7 @@ export class InventoryWidget {
|
||||||
private dragController: DragController;
|
private dragController: DragController;
|
||||||
private lostItemManager: LostItemManager;
|
private lostItemManager: LostItemManager;
|
||||||
|
|
||||||
private pointerMoveHandler!: (pointer: Phaser.Input.Pointer) => void;
|
private rightClickHandler!: (pointer: Phaser.Input.Pointer) => void;
|
||||||
private pointerUpHandler!: (pointer: Phaser.Input.Pointer) => void;
|
|
||||||
private pointerDownHandler!: (pointer: Phaser.Input.Pointer) => void;
|
|
||||||
|
|
||||||
constructor(options: InventoryWidgetOptions) {
|
constructor(options: InventoryWidgetOptions) {
|
||||||
this.scene = options.scene;
|
this.scene = options.scene;
|
||||||
|
|
@ -52,14 +51,9 @@ export class InventoryWidget {
|
||||||
this.isLocked = options.isLocked ?? false;
|
this.isLocked = options.isLocked ?? false;
|
||||||
|
|
||||||
const inventory = this.gameState.value.inventory;
|
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);
|
this.container = this.scene.add.container(options.x, options.y);
|
||||||
|
|
||||||
// Initialize sub-modules
|
|
||||||
this.renderer = new ItemRenderer({
|
this.renderer = new ItemRenderer({
|
||||||
scene: this.scene,
|
scene: this.scene,
|
||||||
container: this.container,
|
container: this.container,
|
||||||
|
|
@ -78,10 +72,9 @@ export class InventoryWidget {
|
||||||
gridY: this.gridY,
|
gridY: this.gridY,
|
||||||
getInventory: () => this.getInventory(),
|
getInventory: () => this.getInventory(),
|
||||||
getItemColor: (id) => this.renderer.getItemColor(id),
|
getItemColor: (id) => this.renderer.getItemColor(id),
|
||||||
getItemCells: (item) => this.renderer.getItemCells(item),
|
|
||||||
onPlaceItem: (item) => this.handlePlaceItem(item),
|
onPlaceItem: (item) => this.handlePlaceItem(item),
|
||||||
onCreateLostItem: (id, shape, transform, meta) =>
|
onCreateLostItem: (id, shape, transform, meta, x, y) =>
|
||||||
this.handleCreateLostItem(id, shape, transform, meta),
|
this.handleCreateLostItem(id, shape, transform, meta, x, y),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lostItemManager = new LostItemManager({
|
this.lostItemManager = new LostItemManager({
|
||||||
|
|
@ -89,13 +82,13 @@ export class InventoryWidget {
|
||||||
cellSize: this.cellSize,
|
cellSize: this.cellSize,
|
||||||
gridGap: this.gridGap,
|
gridGap: this.gridGap,
|
||||||
getItemColor: (id) => this.renderer.getItemColor(id),
|
getItemColor: (id) => this.renderer.getItemColor(id),
|
||||||
onLostItemDragStart: (id, pointer) =>
|
onLostItemDragStart: (id, lostContainer) =>
|
||||||
this.dragController.startLostItemDrag(
|
this.dragController.startLostItemDrag(
|
||||||
id,
|
id,
|
||||||
this.getLostItemShape(id),
|
this.getLostItemShape(id),
|
||||||
this.getLostItemTransform(id),
|
this.getLostItemTransform(id),
|
||||||
this.getLostItemMeta(id),
|
this.getLostItemMeta(id),
|
||||||
pointer,
|
lostContainer,
|
||||||
),
|
),
|
||||||
isDragging: () => this.dragController.isDragging(),
|
isDragging: () => this.dragController.isDragging(),
|
||||||
});
|
});
|
||||||
|
|
@ -117,13 +110,14 @@ export class InventoryWidget {
|
||||||
for (const [itemId, item] of inventory.items) {
|
for (const [itemId, item] of inventory.items) {
|
||||||
if (this.renderer.hasItem(itemId)) continue;
|
if (this.renderer.hasItem(itemId)) continue;
|
||||||
const visuals = this.renderer.createItemVisuals(itemId, item);
|
const visuals = this.renderer.createItemVisuals(itemId, item);
|
||||||
this.setupItemInteraction(itemId, visuals);
|
this.setupItemInteraction(itemId, visuals, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupItemInteraction(
|
private setupItemInteraction(
|
||||||
itemId: string,
|
itemId: string,
|
||||||
visuals: ReturnType<typeof ItemRenderer.prototype.createItemVisuals>,
|
visuals: ReturnType<typeof ItemRenderer.prototype.createItemVisuals>,
|
||||||
|
item: InventoryItem<GameItemMeta>,
|
||||||
): void {
|
): void {
|
||||||
visuals.container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
visuals.container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
||||||
if (this.isLocked) return;
|
if (this.isLocked) return;
|
||||||
|
|
@ -133,30 +127,20 @@ export class InventoryWidget {
|
||||||
removeItemFromGrid(state.inventory, itemId);
|
removeItemFromGrid(state.inventory, itemId);
|
||||||
});
|
});
|
||||||
this.renderer.removeItemVisuals(itemId);
|
this.renderer.removeItemVisuals(itemId);
|
||||||
this.dragController.startDrag(itemId, pointer);
|
this.dragController.startDrag(itemId, item, visuals.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupInput(): void {
|
private setupInput(): void {
|
||||||
this.pointerDownHandler = (pointer: Phaser.Input.Pointer) => {
|
this.rightClickHandler = (pointer: Phaser.Input.Pointer) => {
|
||||||
if (!this.dragController.isDragging()) return;
|
if (!this.dragController.isDragging()) return;
|
||||||
if (pointer.button === 1) {
|
if (pointer.button === 1) {
|
||||||
this.dragController.rotateDraggedItem();
|
this.dragController.rotateDraggedItem();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.pointerMoveHandler = (pointer: Phaser.Input.Pointer) => {
|
this.scene.input.on("pointerdown", this.rightClickHandler);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlePlaceItem(item: InventoryItem<GameItemMeta>): void {
|
private handlePlaceItem(item: InventoryItem<GameItemMeta>): void {
|
||||||
|
|
@ -167,7 +151,7 @@ export class InventoryWidget {
|
||||||
const placedItem = inventory.items.get(item.id);
|
const placedItem = inventory.items.get(item.id);
|
||||||
if (placedItem) {
|
if (placedItem) {
|
||||||
const visuals = this.renderer.createItemVisuals(item.id, 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<GameItemMeta>["shape"],
|
shape: InventoryItem<GameItemMeta>["shape"],
|
||||||
transform: InventoryItem<GameItemMeta>["transform"],
|
transform: InventoryItem<GameItemMeta>["transform"],
|
||||||
meta: InventoryItem<GameItemMeta>["meta"],
|
meta: InventoryItem<GameItemMeta>["meta"],
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
): void {
|
): void {
|
||||||
this.lostItemManager.createLostItem(
|
this.lostItemManager.createLostItem(itemId, shape, transform, meta, x, y);
|
||||||
itemId,
|
|
||||||
shape,
|
|
||||||
transform,
|
|
||||||
meta,
|
|
||||||
this.dragController.getDraggedItemPosition().x,
|
|
||||||
this.dragController.getDraggedItemPosition().y,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLostItemShape(itemId: string) {
|
private getLostItemShape(itemId: string) {
|
||||||
|
|
@ -214,13 +193,6 @@ export class InventoryWidget {
|
||||||
public refresh(): void {
|
public refresh(): void {
|
||||||
const inventory = this.getInventory();
|
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.destroy();
|
||||||
this.renderer = new ItemRenderer({
|
this.renderer = new ItemRenderer({
|
||||||
scene: this.scene,
|
scene: this.scene,
|
||||||
|
|
@ -235,9 +207,7 @@ export class InventoryWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
this.scene.input.off("pointermove", this.pointerMoveHandler);
|
this.scene.input.off("pointerdown", this.rightClickHandler);
|
||||||
this.scene.input.off("pointerup", this.pointerUpHandler);
|
|
||||||
this.scene.input.off("pointerdown", this.pointerDownHandler);
|
|
||||||
|
|
||||||
this.dragController.destroy();
|
this.dragController.destroy();
|
||||||
this.lostItemManager.destroy();
|
this.lostItemManager.destroy();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ export interface LostItemManagerOptions {
|
||||||
cellSize: number;
|
cellSize: number;
|
||||||
gridGap: number;
|
gridGap: number;
|
||||||
getItemColor: (itemId: string) => number;
|
getItemColor: (itemId: string) => number;
|
||||||
onLostItemDragStart: (itemId: string, pointer: Phaser.Input.Pointer) => void;
|
onLostItemDragStart: (
|
||||||
|
itemId: string,
|
||||||
|
container: Phaser.GameObjects.Container,
|
||||||
|
) => void;
|
||||||
isDragging: () => boolean;
|
isDragging: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +35,7 @@ export class LostItemManager {
|
||||||
private getItemColor: (itemId: string) => number;
|
private getItemColor: (itemId: string) => number;
|
||||||
private onLostItemDragStart: (
|
private onLostItemDragStart: (
|
||||||
itemId: string,
|
itemId: string,
|
||||||
pointer: Phaser.Input.Pointer,
|
container: Phaser.GameObjects.Container,
|
||||||
) => void;
|
) => void;
|
||||||
private isDragging: () => boolean;
|
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(
|
createLostItem(
|
||||||
itemId: string,
|
itemId: string,
|
||||||
shape: InventoryItem<GameItemMeta>["shape"],
|
shape: InventoryItem<GameItemMeta>["shape"],
|
||||||
transform: InventoryItem<GameItemMeta>["transform"],
|
transform: InventoryItem<GameItemMeta>["transform"],
|
||||||
meta: InventoryItem<GameItemMeta>["meta"],
|
meta: InventoryItem<GameItemMeta>["meta"],
|
||||||
positionX: number,
|
x: number,
|
||||||
positionY: number,
|
y: number,
|
||||||
): void {
|
): void {
|
||||||
const container = this.scene.add
|
const container = this.scene.add.container(x, y).setDepth(500);
|
||||||
.container(positionX, positionY)
|
|
||||||
.setDepth(500);
|
|
||||||
|
|
||||||
const graphics = this.scene.add.graphics();
|
const graphics = this.scene.add.graphics();
|
||||||
const color = this.getItemColor(itemId);
|
const color = this.getItemColor(itemId);
|
||||||
|
|
||||||
for (let y = 0; y < shape.height; y++) {
|
for (let gy = 0; gy < shape.height; gy++) {
|
||||||
for (let x = 0; x < shape.width; x++) {
|
for (let gx = 0; gx < shape.width; gx++) {
|
||||||
if (shape.grid[y]?.[x]) {
|
if (shape.grid[gy]?.[gx]) {
|
||||||
graphics.fillStyle(color, 0.5);
|
graphics.fillStyle(color, 0.5);
|
||||||
graphics.fillRect(
|
graphics.fillRect(
|
||||||
x * (this.cellSize + this.gridGap),
|
gx * (this.cellSize + this.gridGap),
|
||||||
y * (this.cellSize + this.gridGap),
|
gy * (this.cellSize + this.gridGap),
|
||||||
this.cellSize - 2,
|
this.cellSize - 2,
|
||||||
this.cellSize - 2,
|
this.cellSize - 2,
|
||||||
);
|
);
|
||||||
graphics.lineStyle(2, 0xff4444);
|
graphics.lineStyle(2, 0xff4444);
|
||||||
graphics.strokeRect(
|
graphics.strokeRect(
|
||||||
x * (this.cellSize + this.gridGap),
|
gx * (this.cellSize + this.gridGap),
|
||||||
y * (this.cellSize + this.gridGap),
|
gy * (this.cellSize + this.gridGap),
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
this.cellSize,
|
this.cellSize,
|
||||||
);
|
);
|
||||||
|
|
@ -108,7 +109,7 @@ export class LostItemManager {
|
||||||
container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
container.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
||||||
if (this.isDragging()) return;
|
if (this.isDragging()) return;
|
||||||
if (pointer.button === 0) {
|
if (pointer.button === 0) {
|
||||||
this.onLostItemDragStart(itemId, pointer);
|
this.onLostItemDragStart(itemId, container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"],
|
||||||
|
"boardgame-phaser": ["../framework/src/index.ts"],
|
||||||
},
|
},
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "preact",
|
"jsxImportSource": "preact",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"declarationMap": false,
|
"declarationMap": false,
|
||||||
"sourceMap": false
|
"sourceMap": false,
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"]
|
"include": ["src/**/*"],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue