Bunch o Changes
This commit is contained in:
parent
10df4781ef
commit
dff196e7ed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -24,7 +24,7 @@ export default class CatanGame extends Phaser.Scene {
|
|||
this.opponents = data.opponents ?? [];
|
||||
this.playfield = data.playfield ?? null;
|
||||
this.cardBack = data.cardBack ?? null;
|
||||
this.tilePlacement = data.tilePlacement ?? 'random';
|
||||
this.tilePlacement = data.tilePlacement ?? 'standard';
|
||||
this.gs = null;
|
||||
this.busy = false;
|
||||
this.highlights = [];
|
||||
|
|
@ -142,7 +142,7 @@ export default class CatanGame extends Phaser.Scene {
|
|||
|
||||
// Layer 1: resource swatch fill (outer colored border ring)
|
||||
const swatch = hex.resource === 'desert' ? DESERT_COLOR : RESOURCE_INFO[hex.resource].swatch;
|
||||
g.fillStyle(swatch, 1);
|
||||
g.fillStyle(swatch, 0.55);
|
||||
g.fillPoints(pts, true);
|
||||
|
||||
// Layer 2: dark fill inset (inner black border ring)
|
||||
|
|
@ -169,7 +169,7 @@ export default class CatanGame extends Phaser.Scene {
|
|||
}
|
||||
|
||||
// Thin outer crisp stroke
|
||||
this.hexBorderGfx.lineStyle(2, 0x4a3210, 0.8);
|
||||
this.hexBorderGfx.lineStyle(2, 0x4a3210, 0.35);
|
||||
this.hexBorderGfx.strokePoints(pts, true);
|
||||
|
||||
// Resource label
|
||||
|
|
@ -392,6 +392,100 @@ export default class CatanGame extends Phaser.Scene {
|
|||
mk('endTurn', 'End Turn', () => this.onEndTurn());
|
||||
|
||||
new Button(this, 90, 60, 'Leave', () => this.scene.start('GameMenu'), { variant: 'ghost', width: 120, height: 42, fontSize: 18 }).setDepth(D.hud);
|
||||
|
||||
this.buildDevCardTooltip();
|
||||
this._buildTurnTriangle();
|
||||
}
|
||||
|
||||
_buildTurnTriangle() {
|
||||
const g = this.add.graphics().setDepth(D.hud + 8);
|
||||
g.fillStyle(0xffdd00, 1);
|
||||
g.fillTriangle(-10, -13, -10, 13, 10, 0);
|
||||
g.setPosition(-9999, -9999);
|
||||
this.turnIndicator = g;
|
||||
this._turnSeat = null;
|
||||
this.tweens.add({
|
||||
targets: g,
|
||||
scaleX: 1.4, scaleY: 1.4,
|
||||
duration: 700,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.InOut',
|
||||
});
|
||||
}
|
||||
|
||||
_seatPortraitPos(seat) {
|
||||
if (seat === 0) return { x: 90, y: 980, r: 64 };
|
||||
const panel = this.oppPanels?.find(p => p.seat === seat);
|
||||
return panel ? { x: panel.x, y: panel.y, r: 56 } : null;
|
||||
}
|
||||
|
||||
_updateTurnIndicator() {
|
||||
const seat = this.gs?.currentPlayer;
|
||||
if (seat == null) return;
|
||||
const pos = this._seatPortraitPos(seat);
|
||||
if (!pos) return;
|
||||
const tx = pos.x - pos.r - 20;
|
||||
const ty = pos.y;
|
||||
if (this._turnSeat === seat) return;
|
||||
this._turnSeat = seat;
|
||||
if (this.turnIndicator.x < 0) {
|
||||
this.turnIndicator.setPosition(tx, ty);
|
||||
} else {
|
||||
this.tweens.add({
|
||||
targets: this.turnIndicator,
|
||||
x: tx, y: ty,
|
||||
duration: 600,
|
||||
ease: 'Cubic.Out',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildDevCardTooltip() {
|
||||
const popW = 320, popH = 160, popR = 10;
|
||||
const g = this.add.graphics();
|
||||
const titleTxt = this.add.text(0, -48, '', {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '22px', color: '#f2ead8',
|
||||
}).setOrigin(0.5);
|
||||
const descTxt = this.add.text(0, 12, '', {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex,
|
||||
align: 'center', wordWrap: { width: popW - 32 },
|
||||
}).setOrigin(0.5);
|
||||
this._devTooltip = this.add.container(-9999, -9999, [g, titleTxt, descTxt])
|
||||
.setDepth(D.panel + 5);
|
||||
this._devTooltip.gfx = g;
|
||||
this._devTooltip.titleTxt = titleTxt;
|
||||
this._devTooltip.descTxt = descTxt;
|
||||
this._devTooltip.popW = popW;
|
||||
this._devTooltip.popH = popH;
|
||||
this._devTooltip.popR = popR;
|
||||
}
|
||||
|
||||
showDevCardTooltip(cardX, cardTopY, type, isNew, borderColor) {
|
||||
const DEV_DESC = {
|
||||
knight: 'Move the Robber to any tile and steal a resource from a player there.',
|
||||
roadBuilding: 'Place 2 roads anywhere you could legally build them, for free.',
|
||||
vp: 'Worth 1 Victory Point. Kept hidden until you reach 10 VP and win.',
|
||||
monopoly: 'Name a resource. Every other player gives you all of that resource.',
|
||||
yearOfPlenty: 'Take any 2 resources of your choice directly from the bank.',
|
||||
};
|
||||
const tt = this._devTooltip;
|
||||
const { gfx: g, titleTxt, descTxt, popW, popH, popR } = tt;
|
||||
g.clear();
|
||||
g.fillStyle(0x0d1117, 0.96);
|
||||
g.fillRoundedRect(-popW / 2, -popH / 2, popW, popH, popR);
|
||||
g.lineStyle(2, borderColor, 0.9);
|
||||
g.strokeRoundedRect(-popW / 2, -popH / 2, popW, popH, popR);
|
||||
titleTxt.setText(DEV_INFO[type]?.label ?? type);
|
||||
let d = DEV_DESC[type] ?? '';
|
||||
if (isNew) d += '\n(Not playable until next turn)';
|
||||
descTxt.setText(d);
|
||||
const ttX = Phaser.Math.Clamp(cardX, popW / 2 + 10, GAME_WIDTH - popW / 2 - 10);
|
||||
tt.setPosition(ttX, cardTopY - popH / 2 - 10);
|
||||
}
|
||||
|
||||
hideDevCardTooltip() {
|
||||
this._devTooltip?.setPosition(-9999, -9999);
|
||||
}
|
||||
|
||||
buildCostLegend() {
|
||||
|
|
@ -667,40 +761,51 @@ export default class CatanGame extends Phaser.Scene {
|
|||
badgeContainer.add([bg, vpText]);
|
||||
panel.vpBadge = badgeContainer;
|
||||
|
||||
// Rebuild face-down card fan to the right of the portrait
|
||||
// Rebuild face-down card rows to the right of the portrait
|
||||
if (!panel.cardFan) panel.cardFan = [];
|
||||
panel.cardFan.forEach(o => o.destroy());
|
||||
panel.cardFan = [];
|
||||
|
||||
const cardW = 30, cardH = 43, cardStep = cardW + 3;
|
||||
const rowStartX = panel.x + 88;
|
||||
const maxShow = 8;
|
||||
|
||||
// ── Resource cards (side by side) ──────────────────────────────────────
|
||||
if (cards > 0) {
|
||||
const frameIdx = this.cardBack?.spriteIndex ?? 0;
|
||||
const cardW = 32, cardH = 45;
|
||||
const maxShow = 7;
|
||||
const show = Math.min(cards, maxShow);
|
||||
const step = 16;
|
||||
const fanStartX = panel.x + 74;
|
||||
|
||||
for (let i = 0; i < show; i++) {
|
||||
const img = this.add.image(fanStartX + i * step, panel.y, 'cardbacks', frameIdx)
|
||||
const img = this.add.image(rowStartX + i * cardStep, panel.y, 'cardbacks', frameIdx)
|
||||
.setDisplaySize(cardW, cardH)
|
||||
.setDepth(D.hud + i + 1);
|
||||
.setDepth(D.hud + 1);
|
||||
panel.cardFan.push(img);
|
||||
}
|
||||
|
||||
if (cards > maxShow) {
|
||||
const countX = fanStartX + (maxShow - 1) * step + cardW / 2 + 6;
|
||||
const badge = this.add.text(countX, panel.y, `×${cards}`, {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '16px', color: '#ffd700',
|
||||
const bx = rowStartX + maxShow * cardStep;
|
||||
panel.cardFan.push(this.add.text(bx, panel.y, `+${cards - maxShow}`, {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '14px', color: '#ffd700',
|
||||
stroke: '#000000', strokeThickness: 3,
|
||||
}).setOrigin(0, 0.5).setDepth(D.hud + 20);
|
||||
panel.cardFan.push(badge);
|
||||
} else {
|
||||
const countX = fanStartX + (show - 1) * step + cardW / 2 + 6;
|
||||
const badge = this.add.text(countX, panel.y, `${cards}`, {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex,
|
||||
stroke: '#000000', strokeThickness: 2,
|
||||
}).setOrigin(0, 0.5).setDepth(D.hud + 20);
|
||||
panel.cardFan.push(badge);
|
||||
}).setOrigin(0, 0.5).setDepth(D.hud + 20));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dev cards (face-down, using dev card back) ─────────────────────────
|
||||
const devTotal = p.devCards.length + p.newDevCards.length + (p.vpCards ?? 0);
|
||||
if (devTotal > 0) {
|
||||
const devY = panel.y + cardH + 5;
|
||||
const devShow = Math.min(devTotal, maxShow);
|
||||
for (let i = 0; i < devShow; i++) {
|
||||
const img = this.add.image(rowStartX + i * cardStep, devY, 'catan-cards', 8)
|
||||
.setDisplaySize(cardW, cardH)
|
||||
.setDepth(D.hud + 1);
|
||||
panel.cardFan.push(img);
|
||||
}
|
||||
if (devTotal > maxShow) {
|
||||
const bx = rowStartX + maxShow * cardStep;
|
||||
panel.cardFan.push(this.add.text(bx, devY, `+${devTotal - maxShow}`, {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '14px', color: '#ffd700',
|
||||
stroke: '#000000', strokeThickness: 3,
|
||||
}).setOrigin(0, 0.5).setDepth(D.hud + 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -816,6 +921,155 @@ export default class CatanGame extends Phaser.Scene {
|
|||
});
|
||||
}
|
||||
|
||||
async animateCostPayment(seat, type) {
|
||||
const cost = COSTS[type];
|
||||
if (!cost) return;
|
||||
const resources = [];
|
||||
for (const [r, n] of Object.entries(cost)) for (let i = 0; i < n; i++) resources.push(r);
|
||||
const isHuman = seat === 0;
|
||||
const usedHandIdxs = new Set();
|
||||
let oppCardOffset = 0;
|
||||
for (const resource of resources) {
|
||||
const frameIdx = RESOURCE_TYPES.indexOf(resource);
|
||||
const bankPos = this.bankCardPos?.[resource] ?? { x: 1562, y: 400 };
|
||||
let srcX, srcY, startFaceUp;
|
||||
if (isHuman) {
|
||||
startFaceUp = true;
|
||||
let ci = -1;
|
||||
for (let i = 0; i < this.handDisplay.length; i++) {
|
||||
if (!usedHandIdxs.has(i) && this.handDisplay[i] === resource) { ci = i; usedHandIdxs.add(i); break; }
|
||||
}
|
||||
srcX = (ci >= 0 && this.handCardObjs[ci]) ? this.handCardObjs[ci].x : 90;
|
||||
srcY = (ci >= 0 && this.handCardObjs[ci]) ? this.handCardObjs[ci].y : 950;
|
||||
} else {
|
||||
startFaceUp = false;
|
||||
const panel = this.oppPanels.find(p => p.seat === seat);
|
||||
if (panel?.cardFan[oppCardOffset]) {
|
||||
srcX = panel.cardFan[oppCardOffset].x;
|
||||
srcY = panel.cardFan[oppCardOffset].y;
|
||||
} else {
|
||||
const pos = this._seatPortraitPos(seat);
|
||||
srcX = (pos?.x ?? 130) + 88 + oppCardOffset * 33;
|
||||
srcY = pos?.y ?? 300;
|
||||
}
|
||||
oppCardOffset++;
|
||||
}
|
||||
await this._flyCardToBank(srcX, srcY, frameIdx, bankPos, startFaceUp);
|
||||
}
|
||||
}
|
||||
|
||||
_flyCardToBank(srcX, srcY, frameIdx, bankPos, startFaceUp) {
|
||||
return new Promise(resolve => {
|
||||
const cardW = 60, cardH = 84;
|
||||
const bigW = 90, bigH = 126;
|
||||
|
||||
if (startFaceUp) {
|
||||
// Human card: already face-up, just fly to bank.
|
||||
const img = this.add.image(srcX, srcY, 'catan-cards', frameIdx)
|
||||
.setDisplaySize(cardW, cardH).setDepth(D.banner);
|
||||
this.tweens.add({
|
||||
targets: img, x: bankPos.x, y: bankPos.y, duration: 600, ease: 'Quad.InOut',
|
||||
onComplete: () => this.tweens.add({
|
||||
targets: img, alpha: 0, duration: 200,
|
||||
onComplete: () => { img.destroy(); resolve(); },
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Opponent card: show face-down → flip → resize → fly.
|
||||
const img = this.add.image(srcX, srcY, 'cardbacks', this.cardBack?.spriteIndex ?? 0)
|
||||
.setDisplaySize(cardW, cardH).setDepth(D.banner);
|
||||
|
||||
// Brief beat so the player can register the face-down card.
|
||||
this.time.delayedCall(120, () => {
|
||||
// Flip first half: collapse width to 0.
|
||||
this.tweens.add({
|
||||
targets: img, scaleX: 0, duration: 110, ease: 'Linear',
|
||||
onComplete: () => {
|
||||
// Swap to face-up artwork (still at small size for now).
|
||||
img.setTexture('catan-cards', frameIdx).setDisplaySize(cardW, cardH);
|
||||
const sx = img.scaleX, sy = img.scaleY;
|
||||
img.scaleX = 0;
|
||||
// Flip second half: restore width.
|
||||
this.tweens.add({
|
||||
targets: img, scaleX: sx, duration: 110, ease: 'Linear',
|
||||
onComplete: () => {
|
||||
// Scale up to bank card size with a little bounce.
|
||||
this.tweens.add({
|
||||
targets: img, scaleX: sx * (bigW / cardW), scaleY: sy * (bigH / cardH),
|
||||
duration: 230, ease: 'Back.Out',
|
||||
onComplete: () => {
|
||||
// Now fly to the bank.
|
||||
this.tweens.add({
|
||||
targets: img, x: bankPos.x, y: bankPos.y, duration: 720, ease: 'Quad.InOut',
|
||||
onComplete: () => this.tweens.add({
|
||||
targets: img, alpha: 0, duration: 180,
|
||||
onComplete: () => { img.destroy(); resolve(); },
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
animatePiecePlacement(seat, type, destX, destY) {
|
||||
return new Promise(resolve => {
|
||||
const srcPos = this._seatPortraitPos(seat);
|
||||
if (!srcPos) { resolve(); return; }
|
||||
const col = PLAYER_COLORS[this.gs.players[seat].colorIndex];
|
||||
const g = this.add.graphics();
|
||||
const container = this.add.container(srcPos.x, srcPos.y, [g]).setDepth(D.banner - 1);
|
||||
if (type === 'road') {
|
||||
g.lineStyle(10, 0xffffff, 0.9); g.lineBetween(-13, 0, 13, 0);
|
||||
g.lineStyle(7, col.hexDark, 1); g.lineBetween(-13, 0, 13, 0);
|
||||
g.lineStyle(4, col.hex, 1); g.lineBetween(-13, 0, 13, 0);
|
||||
} else if (type === 'settlement') {
|
||||
g.fillStyle(0xffffff, 1);
|
||||
g.fillRect(-11, -3, 22, 15); g.fillTriangle(-14, -3, 14, -3, 0, -17);
|
||||
g.fillStyle(col.hex, 1);
|
||||
g.fillRect(-9, -3, 18, 12); g.fillTriangle(-11, -3, 11, -3, 0, -14);
|
||||
g.lineStyle(2, col.hexDark, 1); g.strokeRect(-9, -3, 18, 12);
|
||||
} else if (type === 'city') {
|
||||
g.fillStyle(0xffffff, 1);
|
||||
g.fillRect(-17, 0, 18, 15); g.fillRect(-6, -9, 21, 24); g.fillTriangle(-9, -9, 17, -9, 6, -22);
|
||||
g.fillStyle(col.hex, 1);
|
||||
g.fillRect(-15, 0, 15, 12); g.fillRect(-4, -8, 17, 20); g.fillTriangle(-7, -8, 15, -8, 6, -19);
|
||||
g.lineStyle(2, col.hexDark, 1); g.strokeRect(-15, 0, 15, 12); g.strokeRect(-4, -8, 17, 20);
|
||||
}
|
||||
const emitter = this.add.particles(0, 0, 'catanParticle', {
|
||||
follow: container,
|
||||
speed: { min: 25, max: 70 },
|
||||
lifespan: 450,
|
||||
scale: { start: 0.9, end: 0 },
|
||||
alpha: { start: 0.85, end: 0 },
|
||||
quantity: 2,
|
||||
frequency: 28,
|
||||
tint: [col.hex, 0xffffff, col.hexDark],
|
||||
angle: { min: 0, max: 360 },
|
||||
}).setDepth(D.banner - 2);
|
||||
const duration = 2000;
|
||||
const arcHeight = Math.max(180, Math.abs(destY - srcPos.y) * 0.7 + 120);
|
||||
const peakY = Math.min(srcPos.y, destY) - arcHeight;
|
||||
const half = duration / 2;
|
||||
this.tweens.add({ targets: container, x: destX, duration, ease: 'Quad.InOut' });
|
||||
this.tweens.chain({ targets: container, tweens: [
|
||||
{ y: peakY, duration: half, ease: 'Quad.Out' },
|
||||
{ y: destY, duration: half, ease: 'Quad.In', onComplete: () => {
|
||||
emitter.stop();
|
||||
container.destroy();
|
||||
this.time.delayedCall(500, () => emitter.destroy());
|
||||
resolve();
|
||||
}},
|
||||
]});
|
||||
});
|
||||
}
|
||||
|
||||
updateHand() {
|
||||
const p = this.gs.players[0];
|
||||
|
||||
|
|
@ -943,23 +1197,52 @@ export default class CatanGame extends Phaser.Scene {
|
|||
const p = this.gs.players[0];
|
||||
const cards = [...p.devCards, ...p.newDevCards.map((c) => c + '*')];
|
||||
if (p.vpCards) for (let i = 0; i < p.vpCards; i++) cards.push('vp');
|
||||
|
||||
if (!cards.length) {
|
||||
this.devHandContainer.add(this.add.text(740, 980, '—', {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex,
|
||||
}).setOrigin(0, 0.5));
|
||||
return;
|
||||
}
|
||||
|
||||
const DEV_VISUAL = {
|
||||
knight: { frame: 5, border: 0xb03030 },
|
||||
roadBuilding: { frame: 6, border: 0x8b5a2b },
|
||||
vp: { frame: 7, border: 0xdaa520 },
|
||||
monopoly: { frame: 9, border: 0x7b2d8b },
|
||||
yearOfPlenty: { frame: 10, border: 0x2d8b57 },
|
||||
};
|
||||
|
||||
const cardW = 60, cardH = 84, cardR = 6, borderW = 3;
|
||||
const step = 66;
|
||||
const y = 980;
|
||||
let x = 740;
|
||||
const y = 970;
|
||||
|
||||
cards.forEach((card) => {
|
||||
const isNew = card.endsWith('*');
|
||||
const type = isNew ? card.slice(0, -1) : card;
|
||||
const g = this.add.graphics();
|
||||
g.fillStyle(isNew ? 0x6a5a2a : 0x3a2f6b, 1); g.fillRoundedRect(x - 28, y - 36, 56, 72, 6);
|
||||
g.lineStyle(2, COLORS.accent, 0.8); g.strokeRoundedRect(x - 28, y - 36, 56, 72, 6);
|
||||
this.devHandContainer.add(g);
|
||||
this.devHandContainer.add(this.add.text(x, y, DEV_INFO[type]?.short ?? type, {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '12px', color: '#f2ead8', align: 'center', wordWrap: { width: 52 },
|
||||
}).setOrigin(0.5));
|
||||
x += 64;
|
||||
const visual = DEV_VISUAL[type] ?? { frame: 7, border: COLORS.accent };
|
||||
|
||||
const bg = this.add.graphics();
|
||||
bg.fillStyle(0x111111, 0.85);
|
||||
bg.fillRoundedRect(x - cardW / 2, y - cardH / 2, cardW, cardH, cardR);
|
||||
|
||||
const img = this.add.image(x, y, 'catan-cards', visual.frame)
|
||||
.setDisplaySize(54, 78)
|
||||
.setAlpha(isNew ? 0.5 : 1);
|
||||
|
||||
const border = this.add.graphics();
|
||||
border.lineStyle(borderW, visual.border, isNew ? 0.45 : 1);
|
||||
border.strokeRoundedRect(x - cardW / 2, y - cardH / 2, cardW, cardH, cardR);
|
||||
|
||||
const hit = this.add.rectangle(x, y, cardW, cardH, 0x000000, 0).setInteractive();
|
||||
const cx = x, topY = y - cardH / 2;
|
||||
hit.on('pointerover', () => this.showDevCardTooltip(cx, topY, type, isNew, visual.border));
|
||||
hit.on('pointerout', () => this.hideDevCardTooltip());
|
||||
|
||||
this.devHandContainer.add([bg, img, border, hit]);
|
||||
x += step;
|
||||
});
|
||||
if (!cards.length) {
|
||||
this.devHandContainer.add(this.add.text(740, 970, '—', { fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex }).setOrigin(0, 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus() {
|
||||
|
|
@ -979,6 +1262,7 @@ export default class CatanGame extends Phaser.Scene {
|
|||
}
|
||||
this.statusText.setText(msg);
|
||||
this.logText.setText(s.log[s.log.length - 1] ?? '');
|
||||
this._updateTurnIndicator();
|
||||
}
|
||||
|
||||
updateButtons() {
|
||||
|
|
@ -1146,6 +1430,13 @@ export default class CatanGame extends Phaser.Scene {
|
|||
const a = AI.chooseAction(this.gs, seat);
|
||||
if (a.type === 'endTurn') { this.gs = L.endTurn(this.gs); break; }
|
||||
const before = JSON.stringify(this.gs.players[seat]) + this.gs.phase;
|
||||
if (a.type === 'buildRoad' || a.type === 'buildSettlement' || a.type === 'buildCity') {
|
||||
const pieceType = { buildCity: 'city', buildSettlement: 'settlement', buildRoad: 'road' }[a.type];
|
||||
const dest = a.type === 'buildRoad' ? this.edgePos(a.edgeId) : this.nodePos(a.nodeId);
|
||||
enqueueSpeech(`catan-purchase-${pieceType}`);
|
||||
await this.animateCostPayment(seat, pieceType);
|
||||
await this.animatePiecePlacement(seat, pieceType, dest.x, dest.y);
|
||||
}
|
||||
this.gs = this.applyAction(seat, a);
|
||||
if (this.gs.phase === 'moveRobber') {
|
||||
const m = AI.chooseRobberMove(this.gs, seat);
|
||||
|
|
@ -1166,10 +1457,10 @@ export default class CatanGame extends Phaser.Scene {
|
|||
|
||||
applyAction(seat, a) {
|
||||
switch (a.type) {
|
||||
case 'buildCity': return L.buildCity(this.gs, seat, a.nodeId);
|
||||
case 'buildCity': return L.buildCity(this.gs, seat, a.nodeId);
|
||||
case 'buildSettlement': return L.buildSettlement(this.gs, seat, a.nodeId);
|
||||
case 'buildRoad': return L.buildRoad(this.gs, seat, a.edgeId);
|
||||
case 'buyDev': return L.buyDevCard(this.gs, seat);
|
||||
case 'buildRoad': return L.buildRoad(this.gs, seat, a.edgeId);
|
||||
case 'buyDev': enqueueSpeech('catan-purchase-development-card'); return L.buyDevCard(this.gs, seat);
|
||||
case 'bankTrade': return L.tradeWithBank(this.gs, seat, a.give, a.get);
|
||||
case 'playDev':
|
||||
if (a.card === 'knight') return L.playKnight(this.gs, seat);
|
||||
|
|
@ -1273,13 +1564,19 @@ export default class CatanGame extends Phaser.Scene {
|
|||
this.statusText.setText(`Choose where to build a ${type} (or pick another action)`);
|
||||
}
|
||||
|
||||
doBuild(type, id) {
|
||||
async doBuild(type, id) {
|
||||
this.busy = true;
|
||||
this.clearHighlights();
|
||||
this.placeMode = null;
|
||||
if (type === 'road') this.gs = L.buildRoad(this.gs, 0, id);
|
||||
const dest = type === 'road' ? this.edgePos(id) : this.nodePos(id);
|
||||
enqueueSpeech(`catan-purchase-${type}`);
|
||||
await this.animateCostPayment(0, type);
|
||||
await this.animatePiecePlacement(0, type, dest.x, dest.y);
|
||||
if (type === 'road') this.gs = L.buildRoad(this.gs, 0, id);
|
||||
if (type === 'settlement') this.gs = L.buildSettlement(this.gs, 0, id);
|
||||
if (type === 'city') this.gs = L.buildCity(this.gs, 0, id);
|
||||
if (type === 'city') this.gs = L.buildCity(this.gs, 0, id);
|
||||
playSound(this, SFX.PIECE_CLICK);
|
||||
this.busy = false;
|
||||
this.advance();
|
||||
}
|
||||
|
||||
|
|
@ -1287,6 +1584,7 @@ export default class CatanGame extends Phaser.Scene {
|
|||
if (this.busy || this.gs.phase !== 'action') return;
|
||||
this.clearHighlights(); this.placeMode = null;
|
||||
this.gs = L.buyDevCard(this.gs, 0);
|
||||
enqueueSpeech('catan-purchase-development-card');
|
||||
playSound(this, SFX.CARD_DEAL);
|
||||
this.advance();
|
||||
}
|
||||
|
|
@ -1366,10 +1664,11 @@ export default class CatanGame extends Phaser.Scene {
|
|||
|
||||
const stepper = (col, r, i, side) => {
|
||||
const x = col, y = 350 + i * 50;
|
||||
const hitBox = new Phaser.Geom.Rectangle(-22, -22, 44, 44);
|
||||
const lbl = this.add.text(x - 150, y, RESOURCE_INFO[r].label, { fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.textHex }).setOrigin(0, 0.5).setDepth(D.panel + 1);
|
||||
const minus = this.add.text(x - 10, y, '−', { fontFamily: 'Righteous', fontSize: '28px', color: COLORS.dangerHex }).setOrigin(0.5).setInteractive({ useHandCursor: true }).setDepth(D.panel + 1);
|
||||
const minus = this.add.text(x - 16, y, '−', { fontFamily: 'Righteous', fontSize: '36px', color: COLORS.dangerHex }).setOrigin(0.5).setInteractive(hitBox, Phaser.Geom.Rectangle.Contains, true).setDepth(D.panel + 1);
|
||||
const val = this.add.text(x + 30, y, '0', { fontFamily: 'Righteous', fontSize: '22px', color: COLORS.textHex }).setOrigin(0.5).setDepth(D.panel + 1);
|
||||
const plus = this.add.text(x + 70, y, '+', { fontFamily: 'Righteous', fontSize: '26px', color: COLORS.goldHex }).setOrigin(0.5).setInteractive({ useHandCursor: true }).setDepth(D.panel + 1);
|
||||
const plus = this.add.text(x + 76, y, '+', { fontFamily: 'Righteous', fontSize: '36px', color: COLORS.goldHex }).setOrigin(0.5).setInteractive(hitBox, Phaser.Geom.Rectangle.Contains, true).setDepth(D.panel + 1);
|
||||
const bag = side === 'give' ? give : get;
|
||||
valTexts[side + r] = val;
|
||||
minus.on('pointerdown', () => { if (bag[r] > 0) { bag[r]--; val.setText(String(bag[r])); } });
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default class GameRoomScene extends Phaser.Scene {
|
|||
this.opponents = data.opponents ?? [];
|
||||
this.playfield = data.playfield ?? null;
|
||||
this.cardBack = data.cardBack ?? null;
|
||||
this.tilePlacement = data.tilePlacement ?? null;
|
||||
this.tilePlacement = data.tilePlacement ?? 'standard';
|
||||
}
|
||||
|
||||
create() {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default class OpponentSelectScene extends Phaser.Scene {
|
|||
this.playfieldTiles = [];
|
||||
this.selectedCardBack = null;
|
||||
this.cardBackTiles = [];
|
||||
this.selectedTilePlacement = 'random';
|
||||
this.selectedTilePlacement = 'standard';
|
||||
this._initializing = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue