feat: inventory preview?
This commit is contained in:
parent
3ca2a16e29
commit
5342f1c09d
|
|
@ -6,12 +6,14 @@ import {
|
|||
type RunState,
|
||||
type EncounterResult,
|
||||
type MapNodeType,
|
||||
type InventoryItem,
|
||||
type GameItemMeta,
|
||||
} from 'boardgame-core/samples/slay-the-spire-like';
|
||||
|
||||
/**
|
||||
* 占位符遭遇场景
|
||||
*
|
||||
* 当前实现:从全局 gameState 读取当前遭遇信息并展示
|
||||
* 当前实现:从全局 gameState 读取当前遭遇信息并展示,左侧显示背包网格
|
||||
*
|
||||
* 后续扩展:根据 encounter.type 路由到不同的专用遭遇场景
|
||||
* - MapNodeType.Minion / Elite → CombatEncounterScene
|
||||
|
|
@ -24,6 +26,12 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
|||
/** 全局游戏状态(由 App.tsx 注入) */
|
||||
private gameState: MutableSignal<RunState>;
|
||||
|
||||
// Grid constants
|
||||
private readonly CELL_SIZE = 80;
|
||||
private readonly GRID_PADDING = 40;
|
||||
private gridOffsetX = 0;
|
||||
private gridOffsetY = 0;
|
||||
|
||||
constructor(gameState: MutableSignal<RunState>) {
|
||||
super('PlaceholderEncounterScene');
|
||||
this.gameState = gameState;
|
||||
|
|
@ -32,14 +40,20 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
|||
create(): void {
|
||||
super.create();
|
||||
const { width, height } = this.scale;
|
||||
const centerX = width / 2;
|
||||
const centerY = height / 2;
|
||||
const state = this.gameState.value;
|
||||
|
||||
// Calculate grid position (left side, vertically centered)
|
||||
this.gridOffsetX = this.GRID_PADDING;
|
||||
const gridHeight = 4 * this.CELL_SIZE;
|
||||
this.gridOffsetY = (height - gridHeight) / 2;
|
||||
|
||||
// Draw inventory grid
|
||||
this.drawInventoryGrid();
|
||||
|
||||
// Read encounter data from global state
|
||||
const state = this.gameState.value;
|
||||
const node = state.map.nodes.get(state.currentNodeId);
|
||||
if (!node || !node.encounter) {
|
||||
this.add.text(centerX, centerY, '没有遭遇数据', {
|
||||
this.add.text(width / 2, height / 2, '没有遭遇数据', {
|
||||
fontSize: '24px',
|
||||
color: '#ff4444',
|
||||
}).setOrigin(0.5);
|
||||
|
|
@ -53,6 +67,11 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
|||
};
|
||||
const nodeId = node.id;
|
||||
|
||||
// Right side panel for encounter info
|
||||
const rightPanelX = this.gridOffsetX + state.inventory.width * this.CELL_SIZE + 60;
|
||||
const centerX = rightPanelX + 300;
|
||||
const centerY = height / 2;
|
||||
|
||||
// Title
|
||||
this.add.text(centerX, centerY - 150, '遭遇', {
|
||||
fontSize: '32px',
|
||||
|
|
@ -62,7 +81,7 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
|||
|
||||
// Encounter type badge
|
||||
const typeLabel = this.getEncounterTypeLabel(encounter.type);
|
||||
const typeBg = this.add.rectangle(centerX, centerY - 80, 120, 36, this.getEncounterTypeColor(encounter.type));
|
||||
this.add.rectangle(centerX, centerY - 80, 120, 36, this.getEncounterTypeColor(encounter.type));
|
||||
this.add.text(centerX, centerY - 80, typeLabel, {
|
||||
fontSize: '16px',
|
||||
color: '#ffffff',
|
||||
|
|
@ -79,7 +98,7 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
|||
this.add.text(centerX, centerY + 20, encounter.description || '(暂无描述)', {
|
||||
fontSize: '16px',
|
||||
color: '#aaaaaa',
|
||||
wordWrap: { width: 600 },
|
||||
wordWrap: { width: 500 },
|
||||
align: 'center',
|
||||
}).setOrigin(0.5);
|
||||
|
||||
|
|
@ -107,6 +126,98 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
|||
});
|
||||
}
|
||||
|
||||
private drawInventoryGrid(): void {
|
||||
const state = this.gameState.value;
|
||||
const { width, height } = state.inventory;
|
||||
|
||||
// Background panel for the grid area
|
||||
const panelWidth = width * this.CELL_SIZE + 20;
|
||||
const panelHeight = height * this.CELL_SIZE + 60;
|
||||
const panelX = this.gridOffsetX - 10;
|
||||
const panelY = this.gridOffsetY - 40;
|
||||
|
||||
this.add.rectangle(panelX + panelWidth / 2, panelY + panelHeight / 2, panelWidth, panelHeight, 0x1a1a2e)
|
||||
.setStrokeStyle(2, 0x333355);
|
||||
|
||||
// Title
|
||||
this.add.text(this.gridOffsetX + (width * this.CELL_SIZE) / 2, panelY + 10, '背包', {
|
||||
fontSize: '18px',
|
||||
color: '#ffffff',
|
||||
fontStyle: 'bold',
|
||||
}).setOrigin(0.5);
|
||||
|
||||
const gridY = this.gridOffsetY + 15;
|
||||
|
||||
// Draw empty cells
|
||||
const graphics = this.add.graphics();
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const px = this.gridOffsetX + x * this.CELL_SIZE;
|
||||
const py = gridY + y * this.CELL_SIZE;
|
||||
|
||||
graphics.lineStyle(1, 0x444466);
|
||||
graphics.strokeRect(px, py, this.CELL_SIZE, this.CELL_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw items
|
||||
const itemColors = [0x4488cc, 0xcc8844, 0x44cc88, 0xcc4488, 0x8844cc, 0x44cccc, 0xcccc44, 0x8888cc];
|
||||
let colorIndex = 0;
|
||||
const itemColorMap = new Map<string, number>();
|
||||
|
||||
for (const [itemId, item] of state.inventory.items) {
|
||||
// Assign a color to this item
|
||||
if (!itemColorMap.has(itemId)) {
|
||||
itemColorMap.set(itemId, itemColors[colorIndex % itemColors.length]);
|
||||
colorIndex++;
|
||||
}
|
||||
const itemColor = itemColorMap.get(itemId)!;
|
||||
|
||||
// Get occupied cells for this item
|
||||
const cells = this.getItemCells(item);
|
||||
|
||||
// Draw filled cells
|
||||
for (const cell of cells) {
|
||||
const px = this.gridOffsetX + cell.x * this.CELL_SIZE;
|
||||
const py = gridY + cell.y * this.CELL_SIZE;
|
||||
|
||||
graphics.fillStyle(itemColor);
|
||||
graphics.fillRect(px + 2, py + 2, this.CELL_SIZE - 4, this.CELL_SIZE - 4);
|
||||
graphics.lineStyle(2, 0xffffff);
|
||||
graphics.strokeRect(px, py, this.CELL_SIZE, this.CELL_SIZE);
|
||||
}
|
||||
|
||||
// Item name label (at first cell)
|
||||
if (cells.length > 0) {
|
||||
const firstCell = cells[0];
|
||||
const px = this.gridOffsetX + firstCell.x * this.CELL_SIZE;
|
||||
const py = gridY + firstCell.y * this.CELL_SIZE;
|
||||
const itemName = item.meta?.itemData?.name ?? item.id;
|
||||
|
||||
this.add.text(px + this.CELL_SIZE / 2, py + this.CELL_SIZE / 2, itemName, {
|
||||
fontSize: '12px',
|
||||
color: '#ffffff',
|
||||
fontStyle: 'bold',
|
||||
}).setOrigin(0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getItemCells(item: InventoryItem<GameItemMeta>): { x: number; y: number }[] {
|
||||
const cells: { x: number; y: number }[] = [];
|
||||
const shape = item.shape;
|
||||
const { offset } = item.transform;
|
||||
|
||||
for (let y = 0; y < shape.height; y++) {
|
||||
for (let x = 0; x < shape.width; x++) {
|
||||
if (shape.grid[y]?.[x]) {
|
||||
cells.push({ x: x + offset.x, y: y + offset.y });
|
||||
}
|
||||
}
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
private async completeEncounter(): Promise<void> {
|
||||
const state = this.gameState.value;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue