fix: refine card back rendering and update SkipBo layout coordinates
- Clip diagonal stripes in Phase10 and SkipBo card backs to prevent overflow. - Adjust slot coordinates for stock, discards, hand, and labels in SkipBo. - Add background rectangle to SkipBo stock count badge. - Apply consistent code formatting (spacing) across SkipBoGame.
This commit is contained in:
parent
639b37c354
commit
a397302942
|
|
@ -476,9 +476,12 @@ export default class Phase10Game extends Phaser.Scene {
|
||||||
g.strokeRoundedRect(x, y, CARD_W, CARD_H, CARD_R);
|
g.strokeRoundedRect(x, y, CARD_W, CARD_H, CARD_R);
|
||||||
g.lineStyle(2, WILD_STRIPE, 0.5);
|
g.lineStyle(2, WILD_STRIPE, 0.5);
|
||||||
for (let s = -CARD_H; s < CARD_W; s += 14) {
|
for (let s = -CARD_H; s < CARD_W; s += 14) {
|
||||||
|
const t0 = Math.max(0, -s / CARD_H);
|
||||||
|
const t1 = Math.min(1, (CARD_W - s) / CARD_H);
|
||||||
|
if (t0 >= t1) continue;
|
||||||
g.beginPath();
|
g.beginPath();
|
||||||
g.moveTo(x + s, y + CARD_H);
|
g.moveTo(x + s + t0 * CARD_H, y + CARD_H * (1 - t0));
|
||||||
g.lineTo(x + s + CARD_H, y);
|
g.lineTo(x + s + t1 * CARD_H, y + CARD_H * (1 - t1));
|
||||||
g.strokePath();
|
g.strokePath();
|
||||||
}
|
}
|
||||||
container.add(g);
|
container.add(g);
|
||||||
|
|
|
||||||
|
|
@ -67,52 +67,52 @@ function slotLayout(slot) {
|
||||||
case 'bottom':
|
case 'bottom':
|
||||||
return {
|
return {
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
stock: { x: 460, y: 950 },
|
stock: { x: 460, y: 950 },
|
||||||
discards: [{x:630,y:950},{x:750,y:950},{x:870,y:950},{x:990,y:950}],
|
discards: [{ x: 630, y: 950 }, { x: 750, y: 950 }, { x: 870, y: 950 }, { x: 990, y: 950 }],
|
||||||
handStart:{ x: 1110, y: 950 },
|
handStart: { x: 1110, y: 975 },
|
||||||
handAxis: 'x',
|
handAxis: 'x',
|
||||||
portrait: { x: 320, y: 950, r: 60 },
|
portrait: { x: 320, y: 950, r: 60 },
|
||||||
nameLabel:{ x: 320, y: 1030 },
|
nameLabel: { x: 320, y: 1030 },
|
||||||
stockCntPos: { x: 460, y: 880 },
|
stockCntPos: { x: 460, y: 855 },
|
||||||
rotateCards: 0,
|
rotateCards: 0,
|
||||||
outlineRotation: 0,
|
outlineRotation: 0,
|
||||||
};
|
};
|
||||||
case 'top':
|
case 'top':
|
||||||
return {
|
return {
|
||||||
rotation: 180,
|
rotation: 180,
|
||||||
stock: { x: 560, y: 130 },
|
stock: { x: 560, y: 130 },
|
||||||
discards: [{x:730,y:130},{x:850,y:130},{x:970,y:130},{x:1090,y:130}],
|
discards: [{ x: 730, y: 130 }, { x: 850, y: 130 }, { x: 970, y: 130 }, { x: 1090, y: 130 }],
|
||||||
handStart:{ x: 1000, y: 80 },
|
handStart: { x: 1000, y: 80 },
|
||||||
handAxis: 'x',
|
handAxis: 'x',
|
||||||
portrait: { x: 420, y: 130, r: 60 },
|
portrait: { x: 420, y: 130, r: 60 },
|
||||||
nameLabel:{ x: 420, y: 210 },
|
nameLabel: { x: 420, y: 210 },
|
||||||
stockCntPos: { x: 560, y: 60 },
|
stockCntPos: { x: 560, y: 225 },
|
||||||
rotateCards: 180,
|
rotateCards: 180,
|
||||||
outlineRotation: 0,
|
outlineRotation: 0,
|
||||||
};
|
};
|
||||||
case 'left':
|
case 'left':
|
||||||
return {
|
return {
|
||||||
rotation: 90,
|
rotation: 90,
|
||||||
stock: { x: 100, y: 260 },
|
stock: { x: 100, y: 260 },
|
||||||
discards: [{x:100,y:430},{x:100,y:550},{x:100,y:670},{x:100,y:790}],
|
discards: [{ x: 100, y: 430 }, { x: 100, y: 550 }, { x: 100, y: 670 }, { x: 100, y: 790 }],
|
||||||
handStart:{ x: 40, y: 920 },
|
handStart: { x: 40, y: 920 },
|
||||||
handAxis: 'y-up', // hand fans upward toward 920..520
|
handAxis: 'y-up', // hand fans upward toward 920..520
|
||||||
portrait: { x: 100, y: 130, r: 56 },
|
portrait: { x: 100, y: 130, r: 56 },
|
||||||
nameLabel:{ x: 100, y: 50 },
|
nameLabel: { x: 100, y: 50 },
|
||||||
stockCntPos:{ x: 180, y: 260 },
|
stockCntPos: { x: 205, y: 260 },
|
||||||
rotateCards: 90,
|
rotateCards: 90,
|
||||||
outlineRotation: 90,
|
outlineRotation: 90,
|
||||||
};
|
};
|
||||||
case 'right':
|
case 'right':
|
||||||
return {
|
return {
|
||||||
rotation: 270,
|
rotation: 270,
|
||||||
stock: { x: 1820, y: 260 },
|
stock: { x: 1820, y: 260 },
|
||||||
discards: [{x:1820,y:430},{x:1820,y:550},{x:1820,y:670},{x:1820,y:790}],
|
discards: [{ x: 1820, y: 430 }, { x: 1820, y: 550 }, { x: 1820, y: 670 }, { x: 1820, y: 790 }],
|
||||||
handStart:{ x: 1880,y: 920 },
|
handStart: { x: 1880, y: 920 },
|
||||||
handAxis: 'y-up',
|
handAxis: 'y-up',
|
||||||
portrait: { x: 1820, y: 130, r: 56 },
|
portrait: { x: 1820, y: 130, r: 56 },
|
||||||
nameLabel:{ x: 1820, y: 50 },
|
nameLabel: { x: 1820, y: 50 },
|
||||||
stockCntPos:{ x: 1740,y: 260 },
|
stockCntPos: { x: 1715, y: 260 },
|
||||||
rotateCards: 270,
|
rotateCards: 270,
|
||||||
outlineRotation: 90,
|
outlineRotation: 90,
|
||||||
};
|
};
|
||||||
|
|
@ -123,7 +123,7 @@ function slotLayout(slot) {
|
||||||
|
|
||||||
// Center build piles
|
// Center build piles
|
||||||
const BUILD_X0 = CX - (CARD_W + 28) * 1.5;
|
const BUILD_X0 = CX - (CARD_W + 28) * 1.5;
|
||||||
const BUILD_Y = CY + 30;
|
const BUILD_Y = CY + 30;
|
||||||
function buildPilePos(idx) {
|
function buildPilePos(idx) {
|
||||||
return { x: BUILD_X0 + idx * (CARD_W + 28), y: BUILD_Y };
|
return { x: BUILD_X0 + idx * (CARD_W + 28), y: BUILD_Y };
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +137,7 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
this.gameDef = data.game;
|
this.gameDef = data.game;
|
||||||
this.opponents = data.opponents ?? [];
|
this.opponents = data.opponents ?? [];
|
||||||
this.playfield = data.playfield ?? null;
|
this.playfield = data.playfield ?? null;
|
||||||
this.cardBack = data.cardBack ?? null;
|
this.cardBack = data.cardBack ?? null;
|
||||||
|
|
||||||
// Runtime state
|
// Runtime state
|
||||||
this.gs = null;
|
this.gs = null;
|
||||||
|
|
@ -203,9 +203,11 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
const sRect = this.add.rectangle(layout.stock.x, layout.stock.y, CARD_W + 6, CARD_H + 6, 0x000000, 0.35)
|
const sRect = this.add.rectangle(layout.stock.x, layout.stock.y, CARD_W + 6, CARD_H + 6, 0x000000, 0.35)
|
||||||
.setStrokeStyle(2, COLORS.muted).setAngle(layout.outlineRotation).setDepth(D.pile);
|
.setStrokeStyle(2, COLORS.muted).setAngle(layout.outlineRotation).setDepth(D.pile);
|
||||||
// Stock count badge
|
// Stock count badge
|
||||||
|
this.add.rectangle(layout.stockCntPos.x, layout.stockCntPos.y, 52, 34, 0x000000, 0.55)
|
||||||
|
.setDepth(D.ui);
|
||||||
const sCnt = this.add.text(layout.stockCntPos.x, layout.stockCntPos.y, '0', {
|
const sCnt = this.add.text(layout.stockCntPos.x, layout.stockCntPos.y, '0', {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.accentHex, fontStyle: 'bold',
|
fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.accentHex, fontStyle: 'bold',
|
||||||
}).setOrigin(0.5).setDepth(D.ui);
|
}).setOrigin(0.5).setDepth(D.ui + 1);
|
||||||
this.stockPileObjs[seat] = { rect: sRect, count: sCnt, x: layout.stock.x, y: layout.stock.y };
|
this.stockPileObjs[seat] = { rect: sRect, count: sCnt, x: layout.stock.x, y: layout.stock.y };
|
||||||
|
|
||||||
if (seat === 0) {
|
if (seat === 0) {
|
||||||
|
|
@ -249,7 +251,7 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
// Draw pile placeholder
|
// Draw pile placeholder
|
||||||
const drawRect = this.add.rectangle(DRAW_POS.x, DRAW_POS.y, CARD_W + 8, CARD_H + 8, 0x000000, 0.4)
|
const drawRect = this.add.rectangle(DRAW_POS.x, DRAW_POS.y, CARD_W + 8, CARD_H + 8, 0x000000, 0.4)
|
||||||
.setStrokeStyle(2, COLORS.accent).setDepth(D.pile);
|
.setStrokeStyle(2, COLORS.accent).setDepth(D.pile);
|
||||||
this.add.text(DRAW_POS.x, DRAW_POS.y - CARD_H/2 - 18, 'DRAW', {
|
this.add.text(DRAW_POS.x, DRAW_POS.y - CARD_H / 2 - 18, 'DRAW', {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex,
|
fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex,
|
||||||
}).setOrigin(0.5).setDepth(D.ui);
|
}).setOrigin(0.5).setDepth(D.ui);
|
||||||
this.drawPileText = this.add.text(DRAW_POS.x, DRAW_POS.y, '', {
|
this.drawPileText = this.add.text(DRAW_POS.x, DRAW_POS.y, '', {
|
||||||
|
|
@ -264,10 +266,10 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
.setStrokeStyle(2, COLORS.muted).setDepth(D.pile)
|
.setStrokeStyle(2, COLORS.muted).setDepth(D.pile)
|
||||||
.setInteractive({ useHandCursor: true });
|
.setInteractive({ useHandCursor: true });
|
||||||
r.on('pointerdown', () => this.onBuildPileClick(i));
|
r.on('pointerdown', () => this.onBuildPileClick(i));
|
||||||
const label = this.add.text(p.x, p.y - CARD_H/2 - 18, `BUILD ${i + 1}`, {
|
const label = this.add.text(p.x, p.y - CARD_H / 2 - 18, `BUILD ${i + 1}`, {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '14px', color: COLORS.mutedHex,
|
fontFamily: '"Julius Sans One"', fontSize: '14px', color: COLORS.mutedHex,
|
||||||
}).setOrigin(0.5).setDepth(D.ui);
|
}).setOrigin(0.5).setDepth(D.ui);
|
||||||
const needText = this.add.text(p.x, p.y + CARD_H/2 + 18, 'next: 1', {
|
const needText = this.add.text(p.x, p.y + CARD_H / 2 + 18, 'next: 1', {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '14px', color: COLORS.accentHex,
|
fontFamily: '"Julius Sans One"', fontSize: '14px', color: COLORS.accentHex,
|
||||||
}).setOrigin(0.5).setDepth(D.ui);
|
}).setOrigin(0.5).setDepth(D.ui);
|
||||||
this.buildPileObjs[i] = { rect: r, label, needText, x: p.x, y: p.y };
|
this.buildPileObjs[i] = { rect: r, label, needText, x: p.x, y: p.y };
|
||||||
|
|
@ -344,12 +346,15 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
g.fillRoundedRect(x, y, CARD_W, CARD_H, CARD_R);
|
g.fillRoundedRect(x, y, CARD_W, CARD_H, CARD_R);
|
||||||
g.lineStyle(3, WILD_STRIPE, 1);
|
g.lineStyle(3, WILD_STRIPE, 1);
|
||||||
g.strokeRoundedRect(x, y, CARD_W, CARD_H, CARD_R);
|
g.strokeRoundedRect(x, y, CARD_W, CARD_H, CARD_R);
|
||||||
// Diagonal stripes
|
// Diagonal stripes — clipped to card bounds
|
||||||
g.lineStyle(2, WILD_STRIPE, 0.5);
|
g.lineStyle(2, WILD_STRIPE, 0.5);
|
||||||
for (let s = -CARD_H; s < CARD_W; s += 14) {
|
for (let s = -CARD_H; s < CARD_W; s += 14) {
|
||||||
|
const t0 = Math.max(0, -s / CARD_H);
|
||||||
|
const t1 = Math.min(1, (CARD_W - s) / CARD_H);
|
||||||
|
if (t0 >= t1) continue;
|
||||||
g.beginPath();
|
g.beginPath();
|
||||||
g.moveTo(x + s, y + CARD_H);
|
g.moveTo(x + s + t0 * CARD_H, y + CARD_H * (1 - t0));
|
||||||
g.lineTo(x + s + CARD_H, y);
|
g.lineTo(x + s + t1 * CARD_H, y + CARD_H * (1 - t1));
|
||||||
g.strokePath();
|
g.strokePath();
|
||||||
}
|
}
|
||||||
container.add(g);
|
container.add(g);
|
||||||
|
|
@ -462,7 +467,7 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
this.cardObjs.set(`stock-${seat}-${top.id}`, c);
|
this.cardObjs.set(`stock-${seat}-${top.id}`, c);
|
||||||
if (seat === 0) {
|
if (seat === 0) {
|
||||||
c.setInteractive(new Phaser.Geom.Rectangle(-CARD_W/2, -CARD_H/2, CARD_W, CARD_H), Phaser.Geom.Rectangle.Contains);
|
c.setInteractive(new Phaser.Geom.Rectangle(-CARD_W / 2, -CARD_H / 2, CARD_W, CARD_H), Phaser.Geom.Rectangle.Contains);
|
||||||
c.input.cursor = 'pointer';
|
c.input.cursor = 'pointer';
|
||||||
c.on('pointerdown', () => this.onStockClick());
|
c.on('pointerdown', () => this.onStockClick());
|
||||||
}
|
}
|
||||||
|
|
@ -479,13 +484,13 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
this.cardObjs.set(`discard-${seat}-${d}-${t.id}`, c);
|
this.cardObjs.set(`discard-${seat}-${d}-${t.id}`, c);
|
||||||
if (seat === 0) {
|
if (seat === 0) {
|
||||||
c.setInteractive(new Phaser.Geom.Rectangle(-CARD_W/2, -CARD_H/2, CARD_W, CARD_H), Phaser.Geom.Rectangle.Contains);
|
c.setInteractive(new Phaser.Geom.Rectangle(-CARD_W / 2, -CARD_H / 2, CARD_W, CARD_H), Phaser.Geom.Rectangle.Contains);
|
||||||
c.input.cursor = 'pointer';
|
c.input.cursor = 'pointer';
|
||||||
c.on('pointerdown', () => this.onDiscardClick(d));
|
c.on('pointerdown', () => this.onDiscardClick(d));
|
||||||
}
|
}
|
||||||
// Pile-depth chip if more than one card
|
// Pile-depth chip if more than one card
|
||||||
if (pile.length > 1) {
|
if (pile.length > 1) {
|
||||||
const chip = this.add.text(pos.x + CARD_W/2 - 4, pos.y + CARD_H/2 - 4, `×${pile.length}`, {
|
const chip = this.add.text(pos.x + CARD_W / 2 - 4, pos.y + CARD_H / 2 - 4, `×${pile.length}`, {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '14px',
|
fontFamily: '"Julius Sans One"', fontSize: '14px',
|
||||||
color: COLORS.textDarkHex, backgroundColor: COLORS.accentHex,
|
color: COLORS.textDarkHex, backgroundColor: COLORS.accentHex,
|
||||||
padding: { x: 4, y: 2 },
|
padding: { x: 4, y: 2 },
|
||||||
|
|
@ -519,7 +524,7 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
this.cardObjs.set(`hand-${seat}-${card.id}`, c);
|
this.cardObjs.set(`hand-${seat}-${card.id}`, c);
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
c.setInteractive(new Phaser.Geom.Rectangle(-CARD_W/2, -CARD_H/2, CARD_W, CARD_H), Phaser.Geom.Rectangle.Contains);
|
c.setInteractive(new Phaser.Geom.Rectangle(-CARD_W / 2, -CARD_H / 2, CARD_W, CARD_H), Phaser.Geom.Rectangle.Contains);
|
||||||
c.input.cursor = 'pointer';
|
c.input.cursor = 'pointer';
|
||||||
c.on('pointerdown', () => this.onHandClick(i));
|
c.on('pointerdown', () => this.onHandClick(i));
|
||||||
}
|
}
|
||||||
|
|
@ -597,8 +602,8 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
cardForSelection(sel) {
|
cardForSelection(sel) {
|
||||||
if (sel.kind === 'hand') return this.gs.players[0].hand[sel.idx];
|
if (sel.kind === 'hand') return this.gs.players[0].hand[sel.idx];
|
||||||
if (sel.kind === 'stock') return stockTop(this.gs, 0);
|
if (sel.kind === 'stock') return stockTop(this.gs, 0);
|
||||||
if (sel.kind === 'discard') return discardTop(this.gs, 0, sel.idx);
|
if (sel.kind === 'discard') return discardTop(this.gs, 0, sel.idx);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -796,7 +801,7 @@ export default class SkipBoGame extends Phaser.Scene {
|
||||||
const p = this.opponentPortraits[s];
|
const p = this.opponentPortraits[s];
|
||||||
if (!p) continue;
|
if (!p) continue;
|
||||||
if (winnerSeat === s) p.playEmotion?.('win');
|
if (winnerSeat === s) p.playEmotion?.('win');
|
||||||
else p.playEmotion?.('loss');
|
else p.playEmotion?.('loss');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.recordHistory(youWon);
|
this.recordHistory(youWon);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue