167 lines
4.6 KiB
TypeScript
167 lines
4.6 KiB
TypeScript
import Phaser from 'phaser';
|
|
import { ReactiveScene } from 'boardgame-phaser';
|
|
import { parseShapeString, heroItemFighter1Data, type ParsedShape } from 'boardgame-core/samples/slay-the-spire-like';
|
|
|
|
export class ShapeViewerScene extends ReactiveScene {
|
|
private readonly CELL_SIZE = 40;
|
|
private readonly ITEMS_PER_ROW = 4;
|
|
private currentIndex = 0;
|
|
private currentRotation = 0;
|
|
private currentFlipX = false;
|
|
private currentFlipY = false;
|
|
|
|
constructor() {
|
|
super('ShapeViewerScene');
|
|
}
|
|
|
|
create(): void {
|
|
super.create();
|
|
this.drawShapeViewer();
|
|
this.createControls();
|
|
}
|
|
|
|
private drawShapeViewer(): void {
|
|
this.children.removeAll();
|
|
|
|
const { width, height } = this.scale;
|
|
|
|
// Title
|
|
this.add.text(width / 2, 30, 'Shape Viewer - Item Shapes', {
|
|
fontSize: '24px',
|
|
color: '#ffffff',
|
|
fontStyle: 'bold',
|
|
}).setOrigin(0.5);
|
|
|
|
// Draw all shapes in a grid
|
|
this.drawAllShapes();
|
|
}
|
|
|
|
private drawAllShapes(): void {
|
|
const { width } = this.scale;
|
|
const startY = 80;
|
|
const spacingX = 220;
|
|
const spacingY = 140;
|
|
|
|
// Show first 12 items for clarity
|
|
const itemsToShow = heroItemFighter1Data.slice(0, 12);
|
|
|
|
for (let i = 0; i < itemsToShow.length; i++) {
|
|
const data = itemsToShow[i];
|
|
const shape = parseShapeString(data.shape);
|
|
|
|
const col = i % this.ITEMS_PER_ROW;
|
|
const row = Math.floor(i / this.ITEMS_PER_ROW);
|
|
|
|
const x = 60 + col * spacingX;
|
|
const y = startY + row * spacingY;
|
|
|
|
this.drawSingleShape(x, y, data, shape);
|
|
}
|
|
}
|
|
|
|
private drawSingleShape(startX: number, startY: number, data: any, shape: ParsedShape): void {
|
|
const graphics = this.add.graphics();
|
|
|
|
// Draw shape background
|
|
const shapeWidth = shape.width * this.CELL_SIZE;
|
|
const shapeHeight = shape.height * this.CELL_SIZE;
|
|
|
|
// Title - item name
|
|
this.add.text(startX + shapeWidth / 2, startY - 20, data.name, {
|
|
fontSize: '14px',
|
|
color: '#ffffff',
|
|
fontStyle: 'bold',
|
|
}).setOrigin(0.5);
|
|
|
|
// Draw shape cells
|
|
for (let y = 0; y < shape.height; y++) {
|
|
for (let x = 0; x < shape.width; x++) {
|
|
if (shape.grid[y]?.[x]) {
|
|
const px = startX + x * this.CELL_SIZE;
|
|
const py = startY + y * this.CELL_SIZE;
|
|
|
|
// Determine if this is the origin cell
|
|
const isOrigin = x === shape.originX && y === shape.originY;
|
|
const color = isOrigin ? 0x88cc44 : 0x4488cc;
|
|
|
|
graphics.fillStyle(color);
|
|
graphics.fillRect(px + 1, py + 1, this.CELL_SIZE - 2, this.CELL_SIZE - 2);
|
|
graphics.lineStyle(2, 0xffffff);
|
|
graphics.strokeRect(px, py, this.CELL_SIZE, this.CELL_SIZE);
|
|
|
|
// Mark origin with 'O'
|
|
if (isOrigin) {
|
|
this.add.text(px + this.CELL_SIZE / 2, py + this.CELL_SIZE / 2, 'O', {
|
|
fontSize: '16px',
|
|
color: '#ffffff',
|
|
fontStyle: 'bold',
|
|
}).setOrigin(0.5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Shape string
|
|
this.add.text(startX + shapeWidth / 2, startY + shapeHeight + 10, `形状: ${data.shape}`, {
|
|
fontSize: '11px',
|
|
color: '#aaaaaa',
|
|
}).setOrigin(0.5);
|
|
|
|
// Type and cost
|
|
this.add.text(startX + shapeWidth / 2, startY + shapeHeight + 28,
|
|
`类型: ${data.type} | 费用: ${data.costCount} ${data.costType}`, {
|
|
fontSize: '11px',
|
|
color: '#cccccc',
|
|
}).setOrigin(0.5);
|
|
|
|
// Description
|
|
this.add.text(startX + shapeWidth / 2, startY + shapeHeight + 46, data.desc, {
|
|
fontSize: '10px',
|
|
color: '#888888',
|
|
wordWrap: { width: shapeWidth },
|
|
}).setOrigin(0.5);
|
|
}
|
|
|
|
private createControls(): void {
|
|
const { width, height } = this.scale;
|
|
|
|
// Back button
|
|
this.createButton('返回菜单', 100, height - 40, async () => {
|
|
await this.sceneController.launch('IndexScene');
|
|
});
|
|
|
|
// Info text
|
|
this.add.text(width / 2, height - 40,
|
|
`Showing first 12 items | Green = Origin | Blue = Normal`, {
|
|
fontSize: '14px',
|
|
color: '#aaaaaa',
|
|
}).setOrigin(0.5);
|
|
}
|
|
|
|
private createButton(label: string, x: number, y: number, onClick: () => void): void {
|
|
const buttonWidth = 120;
|
|
const buttonHeight = 36;
|
|
|
|
const bg = this.add.rectangle(x, y, buttonWidth, buttonHeight, 0x444466)
|
|
.setStrokeStyle(2, 0x7777aa)
|
|
.setInteractive({ useHandCursor: true });
|
|
|
|
const text = this.add.text(x, y, label, {
|
|
fontSize: '16px',
|
|
color: '#ffffff',
|
|
}).setOrigin(0.5);
|
|
|
|
bg.on('pointerover', () => {
|
|
bg.setFillStyle(0x555588);
|
|
text.setScale(1.05);
|
|
});
|
|
|
|
bg.on('pointerout', () => {
|
|
bg.setFillStyle(0x444466);
|
|
text.setScale(1);
|
|
});
|
|
|
|
bg.on('pointerdown', onClick);
|
|
}
|
|
}
|