refactor: move InventoryItemSpawner and update grid drawing
- Move `InventoryItemSpawner` to `@/gameobjects` - Wrap grid drawing in `addEffect` in `InventoryTestScene` to ensure reactivity - Fix return type in `onMoveItem` callback
This commit is contained in:
parent
7039938a72
commit
f03e6c5a7b
|
|
@ -0,0 +1,173 @@
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { dragDropEventEffect, DragDropEventType } from "boardgame-phaser";
|
||||||
|
import { GRID_CONFIG, ITEM_COLORS } from "@/config";
|
||||||
|
import {
|
||||||
|
IDENTITY_TRANSFORM,
|
||||||
|
ParsedShape,
|
||||||
|
Transform2D,
|
||||||
|
transformShape,
|
||||||
|
type GameItem,
|
||||||
|
type GameItemMeta,
|
||||||
|
type InventoryItem,
|
||||||
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
|
import { computed, signal, Signal } from "@preact/signals-core";
|
||||||
|
import { DisposableBag } from "../../../framework/dist";
|
||||||
|
|
||||||
|
export interface InventoryItemContainerCallbacks {
|
||||||
|
onMoveItem: (itemId: string, newX: number, newY: number) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InventoryItemContainer extends Phaser.GameObjects.Container {
|
||||||
|
private item: Signal<GameItem | undefined>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
private gridOffsetX: number,
|
||||||
|
private gridOffsetY: number,
|
||||||
|
private callbacks: InventoryItemContainerCallbacks,
|
||||||
|
) {
|
||||||
|
super(scene, gridOffsetX, gridOffsetY);
|
||||||
|
scene.add.existing(this);
|
||||||
|
this.setupInteractive();
|
||||||
|
|
||||||
|
const graphics = this.scene.add.graphics();
|
||||||
|
const label = this.scene.add.text(
|
||||||
|
GRID_CONFIG.VIEWER_CELL_SIZE / 2,
|
||||||
|
GRID_CONFIG.VIEWER_CELL_SIZE / 2,
|
||||||
|
"",
|
||||||
|
GRID_CONFIG.ITEM_NAME_STYLE,
|
||||||
|
);
|
||||||
|
this.add([graphics, label]);
|
||||||
|
|
||||||
|
this.item = signal();
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
this.renderGraphics(graphics, shape.value, color.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const transform = computed(() => {
|
||||||
|
return this.item.value?.transform;
|
||||||
|
});
|
||||||
|
disposables.addEffect(() => {
|
||||||
|
if (!transform.value) return;
|
||||||
|
this.snapBack(transform.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(item: GameItem) {
|
||||||
|
this.item.value = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGraphics(
|
||||||
|
graphics: Phaser.GameObjects.Graphics,
|
||||||
|
shape: ParsedShape,
|
||||||
|
itemColor: number,
|
||||||
|
): void {
|
||||||
|
const cells = transformShape(shape, IDENTITY_TRANSFORM);
|
||||||
|
for (const cell of cells) {
|
||||||
|
const localX = (cell.x - cells[0].x) * GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
const localY = (cell.y - cells[0].y) * GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
|
||||||
|
graphics.fillStyle(itemColor);
|
||||||
|
graphics.fillRect(
|
||||||
|
localX + 2,
|
||||||
|
localY + 2,
|
||||||
|
GRID_CONFIG.VIEWER_CELL_SIZE - 4,
|
||||||
|
GRID_CONFIG.VIEWER_CELL_SIZE - 4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setSize(
|
||||||
|
GRID_CONFIG.VIEWER_CELL_SIZE * shape.width,
|
||||||
|
GRID_CONFIG.VIEWER_CELL_SIZE * shape.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(): void {
|
||||||
|
this.setScrollFactor(0);
|
||||||
|
this.setInteractive({ useHandCursor: true });
|
||||||
|
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
dragDropEventEffect(this, (event) => {
|
||||||
|
if (event.type === DragDropEventType.DOWN) {
|
||||||
|
startX = this.x;
|
||||||
|
startY = this.y;
|
||||||
|
this.setAlpha(0.7);
|
||||||
|
} else if (event.type === DragDropEventType.MOVE) {
|
||||||
|
this.x = startX + event.deltaX;
|
||||||
|
this.y = startY + event.deltaY;
|
||||||
|
} else if (event.type === DragDropEventType.UP) {
|
||||||
|
this.setAlpha(1);
|
||||||
|
if (!this.handleDragEnd()) {
|
||||||
|
this.x = startX;
|
||||||
|
this.y = startY;
|
||||||
|
}
|
||||||
|
startX = startY = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleDragEnd(): boolean {
|
||||||
|
const item = this.item.value;
|
||||||
|
if (!item) return false;
|
||||||
|
const itemId = item.id;
|
||||||
|
|
||||||
|
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
const shapeWidth = item.shape?.width ?? 1;
|
||||||
|
const shapeHeight = item.shape?.height ?? 1;
|
||||||
|
|
||||||
|
const x = this.x - this.gridOffsetX;
|
||||||
|
const y = this.y - this.gridOffsetY;
|
||||||
|
const targetX = Math.round((x - cellSize / 2) / cellSize);
|
||||||
|
const targetY = Math.round((y - cellSize / 2) / cellSize);
|
||||||
|
|
||||||
|
const clampedX = Math.max(0, Math.min(targetX, 10 - shapeWidth));
|
||||||
|
const clampedY = Math.max(0, Math.min(targetY, 10 - shapeHeight));
|
||||||
|
|
||||||
|
if (
|
||||||
|
clampedX !== item.transform.offset.x ||
|
||||||
|
clampedY !== item.transform.offset.y
|
||||||
|
) {
|
||||||
|
return this.callbacks.onMoveItem(itemId, clampedX, clampedY);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private snapBack(transform: Transform2D): void {
|
||||||
|
const { x, y } = transform.offset;
|
||||||
|
const targetX = this.gridOffsetX + x * GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
const targetY = this.gridOffsetY + y * GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: this,
|
||||||
|
x: targetX,
|
||||||
|
y: targetY,
|
||||||
|
duration: 150,
|
||||||
|
ease: "Power2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import type { Spawner } from "boardgame-phaser";
|
||||||
|
import type {
|
||||||
|
GameItemMeta,
|
||||||
|
InventoryItem,
|
||||||
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
|
import type { InventorySignal } from "@/state/inventory";
|
||||||
|
import { spawnEffect } from "boardgame-phaser";
|
||||||
|
import { InventoryItemContainer } from "./InventoryItemContainer";
|
||||||
|
import type { InventoryItemContainerCallbacks } from "./InventoryItemContainer";
|
||||||
|
|
||||||
|
export interface InventoryItemSpawnerCallbacks extends InventoryItemContainerCallbacks {}
|
||||||
|
|
||||||
|
export class InventoryItemSpawner implements Spawner<
|
||||||
|
[string, InventoryItem<GameItemMeta>],
|
||||||
|
InventoryItemContainer
|
||||||
|
> {
|
||||||
|
constructor(
|
||||||
|
private scene: Phaser.Scene,
|
||||||
|
private inventorySignal: InventorySignal,
|
||||||
|
private gridOffsetX: number,
|
||||||
|
private gridOffsetY: number,
|
||||||
|
private callbacks: InventoryItemSpawnerCallbacks,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
*getData(): Iterable<[string, InventoryItem<GameItemMeta>]> {
|
||||||
|
const inventory = this.inventorySignal.value;
|
||||||
|
yield* inventory.items.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey(entry: [string, InventoryItem<GameItemMeta>]): string {
|
||||||
|
return entry[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
onSpawn(
|
||||||
|
entry: [string, InventoryItem<GameItemMeta>],
|
||||||
|
): InventoryItemContainer | null {
|
||||||
|
const [itemId, item] = entry;
|
||||||
|
|
||||||
|
const container = new InventoryItemContainer(
|
||||||
|
this.scene,
|
||||||
|
this.gridOffsetX,
|
||||||
|
this.gridOffsetY,
|
||||||
|
{
|
||||||
|
onMoveItem: (id, newX, newY) => {
|
||||||
|
return this.callbacks.onMoveItem(id, newX, newY);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
container.setItem(item);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate(
|
||||||
|
entry: [string, InventoryItem<GameItemMeta>],
|
||||||
|
container: InventoryItemContainer,
|
||||||
|
): void {
|
||||||
|
const [itemId, item] = entry;
|
||||||
|
|
||||||
|
container.setItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDespawn(container: InventoryItemContainer): void {
|
||||||
|
// TODO: add tween
|
||||||
|
container.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInventoryItemSpawner(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
inventorySignal: InventorySignal,
|
||||||
|
gridOffsetX: number,
|
||||||
|
gridOffsetY: number,
|
||||||
|
callbacks: InventoryItemSpawnerCallbacks,
|
||||||
|
) {
|
||||||
|
return spawnEffect(
|
||||||
|
new InventoryItemSpawner(
|
||||||
|
scene,
|
||||||
|
inventorySignal,
|
||||||
|
gridOffsetX,
|
||||||
|
gridOffsetY,
|
||||||
|
callbacks,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,270 +0,0 @@
|
||||||
import Phaser from "phaser";
|
|
||||||
import {
|
|
||||||
spawnEffect,
|
|
||||||
dragDropEventEffect,
|
|
||||||
DragDropEventType,
|
|
||||||
type Spawner,
|
|
||||||
} from "boardgame-phaser";
|
|
||||||
import { GRID_CONFIG, ITEM_COLORS } from "@/config";
|
|
||||||
import type {
|
|
||||||
GameItemMeta,
|
|
||||||
InventoryItem,
|
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
|
||||||
import type { InventorySignal } from "@/state/inventory";
|
|
||||||
|
|
||||||
export interface InventoryItemSpawnerCallbacks {
|
|
||||||
onMoveItem: (itemId: string, newX: number, newY: number) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InventoryItemSpawner implements Spawner<
|
|
||||||
[string, InventoryItem<GameItemMeta>],
|
|
||||||
Phaser.GameObjects.Container
|
|
||||||
> {
|
|
||||||
private dragState: {
|
|
||||||
itemId: string;
|
|
||||||
startX: number;
|
|
||||||
startY: number;
|
|
||||||
container: Phaser.GameObjects.Container;
|
|
||||||
} | null = null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private scene: Phaser.Scene,
|
|
||||||
private inventorySignal: InventorySignal,
|
|
||||||
private gridOffsetX: number,
|
|
||||||
private gridOffsetY: number,
|
|
||||||
private callbacks: InventoryItemSpawnerCallbacks,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
*getData(): Iterable<[string, InventoryItem<GameItemMeta>]> {
|
|
||||||
const inventory = this.inventorySignal.value;
|
|
||||||
yield* inventory.items.entries();
|
|
||||||
}
|
|
||||||
|
|
||||||
getKey(entry: [string, InventoryItem<GameItemMeta>]): string {
|
|
||||||
return entry[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCells(
|
|
||||||
item: InventoryItem<GameItemMeta>,
|
|
||||||
): { x: number; y: number }[] {
|
|
||||||
const cells: { x: number; y: number }[] = [];
|
|
||||||
const shape = item.shape;
|
|
||||||
const transform = item.transform;
|
|
||||||
|
|
||||||
for (let y = 0; y < shape.height; y++) {
|
|
||||||
for (let x = 0; x < shape.width; x++) {
|
|
||||||
if (shape.grid[y]?.[x]) {
|
|
||||||
const finalX = x + transform.offset.x;
|
|
||||||
const finalY = y + transform.offset.y;
|
|
||||||
cells.push({ x: finalX, y: finalY });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getItemColor(itemId: string): number {
|
|
||||||
const hash = itemId.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0);
|
|
||||||
return ITEM_COLORS[hash % ITEM_COLORS.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
onSpawn(
|
|
||||||
entry: [string, InventoryItem<GameItemMeta>],
|
|
||||||
): Phaser.GameObjects.Container | null {
|
|
||||||
const [itemId, item] = entry;
|
|
||||||
const container = this.scene.add.container(0, 0);
|
|
||||||
const cells = this.getCells(item);
|
|
||||||
|
|
||||||
const itemColor = this.getItemColor(itemId);
|
|
||||||
const graphics = this.scene.add.graphics();
|
|
||||||
|
|
||||||
// Draw graphics in container-local coordinates (container is at firstCell position)
|
|
||||||
for (const cell of cells) {
|
|
||||||
// Local coordinates relative to container position
|
|
||||||
const localX = (cell.x - cells[0].x) * GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
const localY = (cell.y - cells[0].y) * GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
|
|
||||||
graphics.fillStyle(itemColor);
|
|
||||||
graphics.fillRect(
|
|
||||||
localX + 2,
|
|
||||||
localY + 2,
|
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE - 4,
|
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE - 4,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cells.length > 0) {
|
|
||||||
const itemName = item.meta?.itemData.name ?? itemId;
|
|
||||||
|
|
||||||
// Text is centered in the first cell, relative to container
|
|
||||||
const textX = GRID_CONFIG.VIEWER_CELL_SIZE / 2;
|
|
||||||
const textY = GRID_CONFIG.VIEWER_CELL_SIZE / 2;
|
|
||||||
|
|
||||||
const text = this.scene.add.text(
|
|
||||||
textX,
|
|
||||||
textY,
|
|
||||||
itemName,
|
|
||||||
GRID_CONFIG.ITEM_NAME_STYLE,
|
|
||||||
);
|
|
||||||
text.setOrigin(0.5);
|
|
||||||
|
|
||||||
container.add([graphics, text]);
|
|
||||||
|
|
||||||
// Position container at the first cell's world position
|
|
||||||
const worldX =
|
|
||||||
this.gridOffsetX + cells[0].x * GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
const worldY =
|
|
||||||
this.gridOffsetY + cells[0].y * GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
container.setPosition(worldX, worldY);
|
|
||||||
} else {
|
|
||||||
container.add(graphics);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make container interactive for drag-and-drop
|
|
||||||
container.setInteractive(
|
|
||||||
new Phaser.Geom.Rectangle(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE,
|
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE,
|
|
||||||
),
|
|
||||||
Phaser.Geom.Rectangle.Contains,
|
|
||||||
);
|
|
||||||
container.setScrollFactor(0);
|
|
||||||
container.setSize(
|
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE * (item.shape?.width ?? 1),
|
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE * (item.shape?.height ?? 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Setup drag handling
|
|
||||||
dragDropEventEffect(container, (event) => {
|
|
||||||
if (event.type === DragDropEventType.DOWN) {
|
|
||||||
// Start drag
|
|
||||||
this.dragState = {
|
|
||||||
itemId,
|
|
||||||
startX: container.x,
|
|
||||||
startY: container.y,
|
|
||||||
container,
|
|
||||||
};
|
|
||||||
container.setAlpha(0.7);
|
|
||||||
} else if (event.type === DragDropEventType.MOVE) {
|
|
||||||
// Update drag position
|
|
||||||
if (this.dragState?.itemId === itemId) {
|
|
||||||
container.x = this.dragState.startX + event.deltaX;
|
|
||||||
container.y = this.dragState.startY + event.deltaY;
|
|
||||||
}
|
|
||||||
} else if (event.type === DragDropEventType.UP) {
|
|
||||||
// End drag
|
|
||||||
if (this.dragState?.itemId === itemId) {
|
|
||||||
container.setAlpha(1);
|
|
||||||
this.handleDragEnd(itemId, container);
|
|
||||||
this.dragState = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleDragEnd(
|
|
||||||
itemId: string,
|
|
||||||
container: Phaser.GameObjects.Container,
|
|
||||||
): void {
|
|
||||||
const inventory = this.inventorySignal.value;
|
|
||||||
const item = inventory.items.get(itemId);
|
|
||||||
if (!item) return;
|
|
||||||
|
|
||||||
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
const shapeWidth = item.shape?.width ?? 1;
|
|
||||||
const shapeHeight = item.shape?.height ?? 1;
|
|
||||||
|
|
||||||
// Calculate target grid position based on container center
|
|
||||||
const targetX = Math.round((container.x - cellSize / 2) / cellSize);
|
|
||||||
const targetY = Math.round((container.y - cellSize / 2) / cellSize);
|
|
||||||
|
|
||||||
// Clamp to inventory bounds
|
|
||||||
const clampedX = Math.max(
|
|
||||||
0,
|
|
||||||
Math.min(targetX, inventory.width - shapeWidth),
|
|
||||||
);
|
|
||||||
const clampedY = Math.max(
|
|
||||||
0,
|
|
||||||
Math.min(targetY, inventory.height - shapeHeight),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If position changed, notify callback
|
|
||||||
if (
|
|
||||||
clampedX !== item.transform.offset.x ||
|
|
||||||
clampedY !== item.transform.offset.y
|
|
||||||
) {
|
|
||||||
this.callbacks.onMoveItem(itemId, clampedX, clampedY);
|
|
||||||
} else {
|
|
||||||
// Snap back to original position
|
|
||||||
const originalX = this.gridOffsetX + item.transform.offset.x * cellSize;
|
|
||||||
const originalY = this.gridOffsetY + item.transform.offset.y * cellSize;
|
|
||||||
|
|
||||||
this.scene.tweens.add({
|
|
||||||
targets: container,
|
|
||||||
x: originalX,
|
|
||||||
y: originalY,
|
|
||||||
duration: 150,
|
|
||||||
ease: "Power2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdate(
|
|
||||||
entry: [string, InventoryItem<GameItemMeta>],
|
|
||||||
obj: Phaser.GameObjects.Container,
|
|
||||||
): void {
|
|
||||||
const [, item] = entry;
|
|
||||||
const cells = this.getCells(item);
|
|
||||||
|
|
||||||
if (cells.length > 0) {
|
|
||||||
// Don't animate if currently dragging this item
|
|
||||||
if (this.dragState?.itemId !== entry[0]) {
|
|
||||||
const worldX =
|
|
||||||
this.gridOffsetX + cells[0].x * GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
const worldY =
|
|
||||||
this.gridOffsetY + cells[0].y * GRID_CONFIG.VIEWER_CELL_SIZE;
|
|
||||||
|
|
||||||
this.scene.tweens.add({
|
|
||||||
targets: obj,
|
|
||||||
x: worldX,
|
|
||||||
y: worldY,
|
|
||||||
duration: 200,
|
|
||||||
ease: "Power2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDespawn(obj: Phaser.GameObjects.Container): void {
|
|
||||||
this.scene.tweens.add({
|
|
||||||
targets: obj,
|
|
||||||
alpha: 0,
|
|
||||||
scale: 0.5,
|
|
||||||
duration: 200,
|
|
||||||
ease: "Back.easeIn",
|
|
||||||
onComplete: () => obj.destroy(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createInventoryItemSpawner(
|
|
||||||
scene: Phaser.Scene,
|
|
||||||
inventorySignal: InventorySignal,
|
|
||||||
gridOffsetX: number,
|
|
||||||
gridOffsetY: number,
|
|
||||||
callbacks: InventoryItemSpawnerCallbacks,
|
|
||||||
) {
|
|
||||||
return spawnEffect(
|
|
||||||
new InventoryItemSpawner(
|
|
||||||
scene,
|
|
||||||
inventorySignal,
|
|
||||||
gridOffsetX,
|
|
||||||
gridOffsetY,
|
|
||||||
callbacks,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { createButton } from "@/utils/createButton";
|
||||||
import { GRID_CONFIG } from "@/config";
|
import { GRID_CONFIG } from "@/config";
|
||||||
import { createInventorySignal, moveItem } from "@/state/inventory";
|
import { createInventorySignal, moveItem } from "@/state/inventory";
|
||||||
import { createItemIn, data } from "boardgame-core/samples/slay-the-spire-like";
|
import { createItemIn, data } from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { createInventoryItemSpawner } from "./InventoryItemSpawner";
|
import { createInventoryItemSpawner } from "@/gameobjects/InventoryItemSpawner";
|
||||||
import { SceneKey } from "./types";
|
import { SceneKey } from "./types";
|
||||||
|
|
||||||
export class InventoryTestScene extends ReactiveScene {
|
export class InventoryTestScene extends ReactiveScene {
|
||||||
|
|
@ -54,34 +54,36 @@ export class InventoryTestScene extends ReactiveScene {
|
||||||
private drawGrid(invWidth: number, invHeight: number): void {
|
private drawGrid(invWidth: number, invHeight: number): void {
|
||||||
const graphics = this.add.graphics();
|
const graphics = this.add.graphics();
|
||||||
|
|
||||||
for (let y = 0; y < invHeight; y++) {
|
this.addEffect(() => {
|
||||||
for (let x = 0; x < invWidth; x++) {
|
for (let y = 0; y < invHeight; y++) {
|
||||||
const px = this.gridOffsetX + x * GRID_CONFIG.VIEWER_CELL_SIZE;
|
for (let x = 0; x < invWidth; x++) {
|
||||||
const py = this.gridOffsetY + y * GRID_CONFIG.VIEWER_CELL_SIZE;
|
const px = this.gridOffsetX + x * GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
const py = this.gridOffsetY + y * GRID_CONFIG.VIEWER_CELL_SIZE;
|
||||||
|
|
||||||
const isOccupied = this.inventorySignal.value.occupiedCells.has(
|
const isOccupied = this.inventorySignal.value.occupiedCells.has(
|
||||||
`${x},${y}`,
|
`${x},${y}`,
|
||||||
);
|
);
|
||||||
graphics.fillStyle(
|
graphics.fillStyle(
|
||||||
isOccupied
|
isOccupied
|
||||||
? GRID_CONFIG.CELL_OCCUPIED_COLOR
|
? GRID_CONFIG.CELL_OCCUPIED_COLOR
|
||||||
: GRID_CONFIG.CELL_EMPTY_COLOR,
|
: GRID_CONFIG.CELL_EMPTY_COLOR,
|
||||||
);
|
);
|
||||||
graphics.fillRect(
|
graphics.fillRect(
|
||||||
px + 1,
|
px + 1,
|
||||||
py + 1,
|
py + 1,
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE - 2,
|
GRID_CONFIG.VIEWER_CELL_SIZE - 2,
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE - 2,
|
GRID_CONFIG.VIEWER_CELL_SIZE - 2,
|
||||||
);
|
);
|
||||||
graphics.lineStyle(1, GRID_CONFIG.GRID_LINE_COLOR);
|
graphics.lineStyle(1, GRID_CONFIG.GRID_LINE_COLOR);
|
||||||
graphics.strokeRect(
|
graphics.strokeRect(
|
||||||
px,
|
px,
|
||||||
py,
|
py,
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE,
|
GRID_CONFIG.VIEWER_CELL_SIZE,
|
||||||
GRID_CONFIG.VIEWER_CELL_SIZE,
|
GRID_CONFIG.VIEWER_CELL_SIZE,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupItemSpawner(): void {
|
private setupItemSpawner(): void {
|
||||||
|
|
@ -92,7 +94,7 @@ export class InventoryTestScene extends ReactiveScene {
|
||||||
this.gridOffsetY,
|
this.gridOffsetY,
|
||||||
{
|
{
|
||||||
onMoveItem: (itemId: string, newX: number, newY: number) => {
|
onMoveItem: (itemId: string, newX: number, newY: number) => {
|
||||||
moveItem(this.inventorySignal, itemId, newX, newY);
|
return moveItem(this.inventorySignal, itemId, newX, newY);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue