feat: inventory preview?
This commit is contained in:
parent
3ca2a16e29
commit
5342f1c09d
|
|
@ -6,12 +6,14 @@ import {
|
||||||
type RunState,
|
type RunState,
|
||||||
type EncounterResult,
|
type EncounterResult,
|
||||||
type MapNodeType,
|
type MapNodeType,
|
||||||
|
type InventoryItem,
|
||||||
|
type GameItemMeta,
|
||||||
} from 'boardgame-core/samples/slay-the-spire-like';
|
} from 'boardgame-core/samples/slay-the-spire-like';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 占位符遭遇场景
|
* 占位符遭遇场景
|
||||||
*
|
*
|
||||||
* 当前实现:从全局 gameState 读取当前遭遇信息并展示
|
* 当前实现:从全局 gameState 读取当前遭遇信息并展示,左侧显示背包网格
|
||||||
*
|
*
|
||||||
* 后续扩展:根据 encounter.type 路由到不同的专用遭遇场景
|
* 后续扩展:根据 encounter.type 路由到不同的专用遭遇场景
|
||||||
* - MapNodeType.Minion / Elite → CombatEncounterScene
|
* - MapNodeType.Minion / Elite → CombatEncounterScene
|
||||||
|
|
@ -24,6 +26,12 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
||||||
/** 全局游戏状态(由 App.tsx 注入) */
|
/** 全局游戏状态(由 App.tsx 注入) */
|
||||||
private gameState: MutableSignal<RunState>;
|
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>) {
|
constructor(gameState: MutableSignal<RunState>) {
|
||||||
super('PlaceholderEncounterScene');
|
super('PlaceholderEncounterScene');
|
||||||
this.gameState = gameState;
|
this.gameState = gameState;
|
||||||
|
|
@ -32,14 +40,20 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
||||||
create(): void {
|
create(): void {
|
||||||
super.create();
|
super.create();
|
||||||
const { width, height } = this.scale;
|
const { width, height } = this.scale;
|
||||||
const centerX = width / 2;
|
const state = this.gameState.value;
|
||||||
const centerY = height / 2;
|
|
||||||
|
// 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
|
// Read encounter data from global state
|
||||||
const state = this.gameState.value;
|
|
||||||
const node = state.map.nodes.get(state.currentNodeId);
|
const node = state.map.nodes.get(state.currentNodeId);
|
||||||
if (!node || !node.encounter) {
|
if (!node || !node.encounter) {
|
||||||
this.add.text(centerX, centerY, '没有遭遇数据', {
|
this.add.text(width / 2, height / 2, '没有遭遇数据', {
|
||||||
fontSize: '24px',
|
fontSize: '24px',
|
||||||
color: '#ff4444',
|
color: '#ff4444',
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
|
|
@ -53,6 +67,11 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
||||||
};
|
};
|
||||||
const nodeId = node.id;
|
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
|
// Title
|
||||||
this.add.text(centerX, centerY - 150, '遭遇', {
|
this.add.text(centerX, centerY - 150, '遭遇', {
|
||||||
fontSize: '32px',
|
fontSize: '32px',
|
||||||
|
|
@ -62,7 +81,7 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
||||||
|
|
||||||
// Encounter type badge
|
// Encounter type badge
|
||||||
const typeLabel = this.getEncounterTypeLabel(encounter.type);
|
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, {
|
this.add.text(centerX, centerY - 80, typeLabel, {
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
|
|
@ -79,7 +98,7 @@ export class PlaceholderEncounterScene extends ReactiveScene {
|
||||||
this.add.text(centerX, centerY + 20, encounter.description || '(暂无描述)', {
|
this.add.text(centerX, centerY + 20, encounter.description || '(暂无描述)', {
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
color: '#aaaaaa',
|
color: '#aaaaaa',
|
||||||
wordWrap: { width: 600 },
|
wordWrap: { width: 500 },
|
||||||
align: 'center',
|
align: 'center',
|
||||||
}).setOrigin(0.5);
|
}).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> {
|
private async completeEncounter(): Promise<void> {
|
||||||
const state = this.gameState.value;
|
const state = this.gameState.value;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue