From 19dfafeb308581d964044390756bea10bd206ac4 Mon Sep 17 00:00:00 2001 From: hypercross Date: Mon, 20 Apr 2026 19:45:01 +0800 Subject: [PATCH] refactor: implement cell-based hit area for inventory items Update `InventoryItemContainer` to use a custom hit area callback based on individual cell rectangles instead of a single bounding box. This allows for more precise interaction with non-rectangular item shapes. --- .../src/gameobjects/InventoryItemContainer.ts | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/sts-like-viewer/src/gameobjects/InventoryItemContainer.ts b/packages/sts-like-viewer/src/gameobjects/InventoryItemContainer.ts index 687ea18..b800e72 100644 --- a/packages/sts-like-viewer/src/gameobjects/InventoryItemContainer.ts +++ b/packages/sts-like-viewer/src/gameobjects/InventoryItemContainer.ts @@ -7,8 +7,6 @@ import { 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"; @@ -56,15 +54,12 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container { const color = computed(() => { return this.getItemColor(this.item.value?.id ?? ""); }); - let hasSize = false; disposables.addEffect(() => { graphics.clear(); if (!shape.value) return; - this.renderGraphics(graphics, shape.value, color.value); + const cellRects = this.renderGraphics(graphics, shape.value, color.value); - if (hasSize) return; - hasSize = true; - this.setupInteractive(); + this.setupInteractive(cellRects); }); const transform = computed(() => { @@ -84,8 +79,10 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container { graphics: Phaser.GameObjects.Graphics, shape: ParsedShape, itemColor: number, - ): void { + ): { x: number; y: number }[] { const cells = transformShape(shape, IDENTITY_TRANSFORM); + const cellRects: { x: number; y: number }[] = []; + 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; @@ -97,12 +94,11 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container { GRID_CONFIG.VIEWER_CELL_SIZE - 4, GRID_CONFIG.VIEWER_CELL_SIZE - 4, ); + + cellRects.push({ x: localX, y: localY }); } - this.setSize( - GRID_CONFIG.VIEWER_CELL_SIZE * shape.width, - GRID_CONFIG.VIEWER_CELL_SIZE * shape.height, - ); + return cellRects; } private getItemColor(itemId: string): number { @@ -110,9 +106,26 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container { return ITEM_COLORS[hash % ITEM_COLORS.length]; } - private setupInteractive(): void { + private setupInteractive(cellRects: { x: number; y: number }[]): void { this.setScrollFactor(0); - this.setInteractive({ useHandCursor: true }); + const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE; + this.setInteractive({ + hitArea: cellRects, + hitAreaCallback: ( + hitArea: { x: number; y: number }[], + x: number, + y: number, + ) => { + return hitArea.some( + (cell) => + x >= cell.x && + x < cell.x + cellSize && + y >= cell.y && + y < cell.y + cellSize, + ); + }, + useHandCursor: true, + } as Phaser.Types.Input.InputConfiguration); let startX = 0; let startY = 0;