refactor: use InventoryItemState in InventoryItemContainer
Replace local signal-based state management with the dedicated `InventoryItemState` class to encapsulate item properties and logic.
This commit is contained in:
parent
19dfafeb30
commit
fd347e7a62
|
|
@ -1,6 +1,6 @@
|
|||
import Phaser from "phaser";
|
||||
import { dragDropEventEffect, DragDropEventType } from "boardgame-phaser";
|
||||
import { GRID_CONFIG, ITEM_COLORS } from "@/config";
|
||||
import { GRID_CONFIG } from "@/config";
|
||||
import {
|
||||
IDENTITY_TRANSFORM,
|
||||
ParsedShape,
|
||||
|
|
@ -8,15 +8,15 @@ import {
|
|||
transformShape,
|
||||
type GameItem,
|
||||
} from "boardgame-core/samples/slay-the-spire-like";
|
||||
import { computed, signal, Signal } from "@preact/signals-core";
|
||||
import { DisposableBag } from "../../../framework/dist";
|
||||
import { InventoryItemState } from "@/state/InventoryItemState";
|
||||
|
||||
export interface InventoryItemContainerCallbacks {
|
||||
onMoveItem: (itemId: string, newX: number, newY: number) => boolean;
|
||||
}
|
||||
|
||||
export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||
private item: Signal<GameItem | undefined>;
|
||||
private itemState: InventoryItemState;
|
||||
|
||||
constructor(
|
||||
scene: Phaser.Scene,
|
||||
|
|
@ -36,43 +36,34 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
|||
);
|
||||
this.add([graphics, label]);
|
||||
|
||||
this.item = signal();
|
||||
this.itemState = new InventoryItemState();
|
||||
|
||||
const disposables = new DisposableBag(this);
|
||||
|
||||
const itemName = computed(() => {
|
||||
const item = this.item.value;
|
||||
return item?.meta?.itemData.name ?? item?.id ?? "";
|
||||
});
|
||||
disposables.addEffect(() => {
|
||||
label.setText(itemName.value);
|
||||
label.setText(this.itemState.name.value);
|
||||
});
|
||||
|
||||
const shape = computed(() => {
|
||||
return this.item.value?.shape;
|
||||
});
|
||||
const color = computed(() => {
|
||||
return this.getItemColor(this.item.value?.id ?? "");
|
||||
});
|
||||
disposables.addEffect(() => {
|
||||
graphics.clear();
|
||||
if (!shape.value) return;
|
||||
const cellRects = this.renderGraphics(graphics, shape.value, color.value);
|
||||
if (!this.itemState.shape.value) return;
|
||||
const cellRects = this.renderGraphics(
|
||||
graphics,
|
||||
this.itemState.shape.value,
|
||||
this.itemState.color.value,
|
||||
);
|
||||
|
||||
this.setupInteractive(cellRects);
|
||||
});
|
||||
|
||||
const transform = computed(() => {
|
||||
return this.item.value?.transform;
|
||||
});
|
||||
disposables.addEffect(() => {
|
||||
if (!transform.value) return;
|
||||
this.snapBack(transform.value);
|
||||
if (!this.itemState.transform.value) return;
|
||||
this.snapBack(this.itemState.transform.value);
|
||||
});
|
||||
}
|
||||
|
||||
setItem(item: GameItem) {
|
||||
this.item.value = item;
|
||||
setItem(item: GameItem): void {
|
||||
this.itemState.setItem(item);
|
||||
}
|
||||
|
||||
renderGraphics(
|
||||
|
|
@ -101,11 +92,6 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
|||
return cellRects;
|
||||
}
|
||||
|
||||
private getItemColor(itemId: string): number {
|
||||
const hash = itemId.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0);
|
||||
return ITEM_COLORS[hash % ITEM_COLORS.length];
|
||||
}
|
||||
|
||||
private setupInteractive(cellRects: { x: number; y: number }[]): void {
|
||||
this.setScrollFactor(0);
|
||||
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||
|
|
@ -115,15 +101,14 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
|||
hitArea: { x: number; y: number }[],
|
||||
x: number,
|
||||
y: number,
|
||||
) => {
|
||||
return hitArea.some(
|
||||
) =>
|
||||
hitArea.some(
|
||||
(cell) =>
|
||||
x >= cell.x &&
|
||||
x < cell.x + cellSize &&
|
||||
y >= cell.y &&
|
||||
y < cell.y + cellSize,
|
||||
);
|
||||
},
|
||||
),
|
||||
useHandCursor: true,
|
||||
} as Phaser.Types.Input.InputConfiguration);
|
||||
|
||||
|
|
@ -140,7 +125,7 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
|||
} else if (event.type === DragDropEventType.UP) {
|
||||
this.setAlpha(1);
|
||||
if (!this.handleDragEnd()) {
|
||||
const t = this.item.peek()?.transform;
|
||||
const t = this.itemState.transform.peek();
|
||||
t && this.snapBack(t);
|
||||
}
|
||||
startX = startY = 0;
|
||||
|
|
@ -149,9 +134,8 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
private handleDragEnd(): boolean {
|
||||
const item = this.item.value;
|
||||
const item = this.itemState.item;
|
||||
if (!item) return false;
|
||||
const itemId = item.id;
|
||||
|
||||
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||
const shapeWidth = item.shape?.width ?? 1;
|
||||
|
|
@ -169,7 +153,7 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
|||
clampedX !== item.transform.offset.x ||
|
||||
clampedY !== item.transform.offset.y
|
||||
) {
|
||||
return this.callbacks.onMoveItem(itemId, clampedX, clampedY);
|
||||
return this.callbacks.onMoveItem(item.id, clampedX, clampedY);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { computed, signal, Signal } from "@preact/signals-core"
|
||||
import { ITEM_COLORS } from "@/config"
|
||||
import {
|
||||
type GameItem,
|
||||
type ParsedShape,
|
||||
type Transform2D,
|
||||
} from "boardgame-core/samples/slay-the-spire-like"
|
||||
|
||||
export class InventoryItemState {
|
||||
private readonly _item: Signal<GameItem | undefined>
|
||||
|
||||
readonly name: Signal<string>
|
||||
readonly shape: Signal<ParsedShape | undefined>
|
||||
readonly color: Signal<number>
|
||||
readonly transform: Signal<Transform2D | undefined>
|
||||
|
||||
constructor(initialItem?: GameItem) {
|
||||
this._item = signal(initialItem)
|
||||
|
||||
this.name = computed(() => {
|
||||
const item = this._item.value
|
||||
return item?.meta?.itemData.name ?? item?.id ?? ""
|
||||
})
|
||||
|
||||
this.shape = computed(() => this._item.value?.shape)
|
||||
|
||||
this.color = computed(() => this.computeColor(this._item.value?.id ?? ""))
|
||||
|
||||
this.transform = computed(() => this._item.value?.transform)
|
||||
}
|
||||
|
||||
get item(): GameItem | undefined {
|
||||
return this._item.value
|
||||
}
|
||||
|
||||
setItem(item: GameItem): void {
|
||||
this._item.value = item
|
||||
}
|
||||
|
||||
private computeColor(itemId: string): number {
|
||||
const hash = itemId.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0)
|
||||
return ITEM_COLORS[hash % ITEM_COLORS.length]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue