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 Phaser from "phaser";
|
||||||
import { dragDropEventEffect, DragDropEventType } from "boardgame-phaser";
|
import { dragDropEventEffect, DragDropEventType } from "boardgame-phaser";
|
||||||
import { GRID_CONFIG, ITEM_COLORS } from "@/config";
|
import { GRID_CONFIG } from "@/config";
|
||||||
import {
|
import {
|
||||||
IDENTITY_TRANSFORM,
|
IDENTITY_TRANSFORM,
|
||||||
ParsedShape,
|
ParsedShape,
|
||||||
|
|
@ -8,15 +8,15 @@ import {
|
||||||
transformShape,
|
transformShape,
|
||||||
type GameItem,
|
type GameItem,
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { computed, signal, Signal } from "@preact/signals-core";
|
|
||||||
import { DisposableBag } from "../../../framework/dist";
|
import { DisposableBag } from "../../../framework/dist";
|
||||||
|
import { InventoryItemState } from "@/state/InventoryItemState";
|
||||||
|
|
||||||
export interface InventoryItemContainerCallbacks {
|
export interface InventoryItemContainerCallbacks {
|
||||||
onMoveItem: (itemId: string, newX: number, newY: number) => boolean;
|
onMoveItem: (itemId: string, newX: number, newY: number) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
private item: Signal<GameItem | undefined>;
|
private itemState: InventoryItemState;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
scene: Phaser.Scene,
|
scene: Phaser.Scene,
|
||||||
|
|
@ -36,43 +36,34 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
);
|
);
|
||||||
this.add([graphics, label]);
|
this.add([graphics, label]);
|
||||||
|
|
||||||
this.item = signal();
|
this.itemState = new InventoryItemState();
|
||||||
|
|
||||||
const disposables = new DisposableBag(this);
|
const disposables = new DisposableBag(this);
|
||||||
|
|
||||||
const itemName = computed(() => {
|
|
||||||
const item = this.item.value;
|
|
||||||
return item?.meta?.itemData.name ?? item?.id ?? "";
|
|
||||||
});
|
|
||||||
disposables.addEffect(() => {
|
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(() => {
|
disposables.addEffect(() => {
|
||||||
graphics.clear();
|
graphics.clear();
|
||||||
if (!shape.value) return;
|
if (!this.itemState.shape.value) return;
|
||||||
const cellRects = this.renderGraphics(graphics, shape.value, color.value);
|
const cellRects = this.renderGraphics(
|
||||||
|
graphics,
|
||||||
|
this.itemState.shape.value,
|
||||||
|
this.itemState.color.value,
|
||||||
|
);
|
||||||
|
|
||||||
this.setupInteractive(cellRects);
|
this.setupInteractive(cellRects);
|
||||||
});
|
});
|
||||||
|
|
||||||
const transform = computed(() => {
|
|
||||||
return this.item.value?.transform;
|
|
||||||
});
|
|
||||||
disposables.addEffect(() => {
|
disposables.addEffect(() => {
|
||||||
if (!transform.value) return;
|
if (!this.itemState.transform.value) return;
|
||||||
this.snapBack(transform.value);
|
this.snapBack(this.itemState.transform.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setItem(item: GameItem) {
|
setItem(item: GameItem): void {
|
||||||
this.item.value = item;
|
this.itemState.setItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGraphics(
|
renderGraphics(
|
||||||
|
|
@ -101,11 +92,6 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
return cellRects;
|
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 {
|
private setupInteractive(cellRects: { x: number; y: number }[]): void {
|
||||||
this.setScrollFactor(0);
|
this.setScrollFactor(0);
|
||||||
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
|
@ -115,15 +101,14 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
hitArea: { x: number; y: number }[],
|
hitArea: { x: number; y: number }[],
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
) => {
|
) =>
|
||||||
return hitArea.some(
|
hitArea.some(
|
||||||
(cell) =>
|
(cell) =>
|
||||||
x >= cell.x &&
|
x >= cell.x &&
|
||||||
x < cell.x + cellSize &&
|
x < cell.x + cellSize &&
|
||||||
y >= cell.y &&
|
y >= cell.y &&
|
||||||
y < cell.y + cellSize,
|
y < cell.y + cellSize,
|
||||||
);
|
),
|
||||||
},
|
|
||||||
useHandCursor: true,
|
useHandCursor: true,
|
||||||
} as Phaser.Types.Input.InputConfiguration);
|
} as Phaser.Types.Input.InputConfiguration);
|
||||||
|
|
||||||
|
|
@ -140,7 +125,7 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
} else if (event.type === DragDropEventType.UP) {
|
} else if (event.type === DragDropEventType.UP) {
|
||||||
this.setAlpha(1);
|
this.setAlpha(1);
|
||||||
if (!this.handleDragEnd()) {
|
if (!this.handleDragEnd()) {
|
||||||
const t = this.item.peek()?.transform;
|
const t = this.itemState.transform.peek();
|
||||||
t && this.snapBack(t);
|
t && this.snapBack(t);
|
||||||
}
|
}
|
||||||
startX = startY = 0;
|
startX = startY = 0;
|
||||||
|
|
@ -149,9 +134,8 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDragEnd(): boolean {
|
private handleDragEnd(): boolean {
|
||||||
const item = this.item.value;
|
const item = this.itemState.item;
|
||||||
if (!item) return false;
|
if (!item) return false;
|
||||||
const itemId = item.id;
|
|
||||||
|
|
||||||
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
const shapeWidth = item.shape?.width ?? 1;
|
const shapeWidth = item.shape?.width ?? 1;
|
||||||
|
|
@ -169,7 +153,7 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
clampedX !== item.transform.offset.x ||
|
clampedX !== item.transform.offset.x ||
|
||||||
clampedY !== item.transform.offset.y
|
clampedY !== item.transform.offset.y
|
||||||
) {
|
) {
|
||||||
return this.callbacks.onMoveItem(itemId, clampedX, clampedY);
|
return this.callbacks.onMoveItem(item.id, clampedX, clampedY);
|
||||||
}
|
}
|
||||||
return false;
|
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