import type { CellKey, GridInventory, InventoryItem } from '../grid-inventory/types'; import type { GameItemMeta } from '../progress/types'; import { createRegion, createRegionAxis } from '@/core/region'; import type { GameCard, GameCardMeta, PlayerDeck, DeckRegions } from './types'; /** * Generates a unique card ID for a cell within an item. */ function generateCardId(itemId: string, cellIndex: number): string { return `card-${itemId}-${cellIndex}`; } /** * Collects all cell keys from an item's shape in a deterministic order. * Iterates the shape grid row by row, left to right, top to bottom. */ function getItemCells(item: InventoryItem): CellKey[] { const cells: CellKey[] = []; const { shape, transform } = item; const { grid } = shape; const { offset, rotation, flipX, flipY } = transform; // Track local dimensions (may swap on rotation) let localWidth = grid[0]?.length || 1; let localHeight = grid.length; for (let gy = 0; gy < grid.length; gy++) { for (let gx = 0; gx < grid[gy].length; gx++) { if (!grid[gy][gx]) continue; // Start from grid coordinates let x = gx; let y = gy; // Apply rotation (90 degree increments, clockwise) const rotTimes = ((rotation % 4) + 4) % 4; for (let r = 0; r < rotTimes; r++) { const newX = localHeight - 1 - y; const newY = x; x = newX; y = newY; // Swap dimensions for next iteration const tmp = localWidth; localWidth = localHeight; localHeight = tmp; } // Reset local dimensions for fresh computation per cell localWidth = grid[0]?.length || 1; localHeight = grid.length; // Apply flips if (flipX) { x = -x; } if (flipY) { y = -y; } // Apply offset const finalX = x + offset.x; const finalY = y + offset.y; cells.push(`${finalX},${finalY}`); } } return cells; } /** * Creates a single card from an inventory item. * * @param itemId - The inventory item instance ID * @param itemData - The CSV item data * @param cellKey - The cell key ("x,y") this card represents * @param cellIndex - Index of the cell for unique ID generation */ function createItemCard( itemId: string, itemData: GameItemMeta['itemData'], cellKey: CellKey, cellIndex: number ): GameCard { const cardId = generateCardId(itemId, cellIndex); return { id: cardId, regionId: '', position: [], sourceItemId: itemId, itemData, cellKey, displayName: itemData.name, description: itemData.desc, }; } /** * Creates a status card that does not correspond to any inventory item. * Status cards represent temporary effects like wounds, stuns, etc. * * @param id - Unique identifier for the card instance * @param displayName - Display name (e.g. "伤口", "眩晕") * @param description - Card description / ability text */ function createStatusCard( id: string, displayName: string, description: string ): GameCard { return { id, regionId: '', position: [], sourceItemId: null, itemData: null, cellKey: null, displayName, description, }; } /** * Generates a complete player deck from the current inventory state. * * For each item in the inventory, N cards are generated where N is the * number of cells the item occupies (shape.count). * * All generated cards are placed in the draw pile initially. * * @param inventory - The player's grid inventory * @returns A PlayerDeck with all cards in the draw pile */ function generateDeckFromInventory(inventory: GridInventory): PlayerDeck { const cards: Record = {}; const drawPile: string[] = []; for (const item of inventory.items.values()) { const itemData = item.meta?.itemData; if (!itemData) continue; // Generate one card per occupied cell in the item's shape const cellCount = item.shape.count; const cells = getItemCells(item); for (let i = 0; i < cellCount; i++) { const cellKey = cells[i] ?? `${i},0`; const card = createItemCard(item.id, itemData, cellKey, i); cards[card.id] = card; drawPile.push(card.id); } } return { cards, drawPile, hand: [], discardPile: [], exhaustPile: [], }; } /** * Creates region definitions for deck management. * Returns regions for draw pile, hand, discard pile, and exhaust pile. */ function createDeckRegions(): DeckRegions { return { drawPile: createRegion('drawPile', [ createRegionAxis('index', 0, 0), // unbounded ]), hand: createRegion('hand', [ createRegionAxis('index', 0, 0), ]), discardPile: createRegion('discardPile', [ createRegionAxis('index', 0, 0), ]), exhaustPile: createRegion('exhaustPile', [ createRegionAxis('index', 0, 0), ]), }; } /** * Creates an empty player deck structure. */ function createPlayerDeck(): PlayerDeck { return { cards: {}, drawPile: [], hand: [], discardPile: [], exhaustPile: [], }; } export { generateDeckFromInventory, createStatusCard, createDeckRegions, createPlayerDeck, generateCardId, };