Briscola all'oratorio: alle tavolate il giocatore di carte (segnaposto, sprite di Codex) sfida Andrea a briscola in due. Si sceglie il mazzo, toscane (cuori, mattoni, fiori, picche; gobbo, donna e regio) o napoletane, e il livello: «Alla bona», «Seria», «A fassi male» (conta le carte uscite, finale perfetto a mazzo finito, mai la mano altrui). Regole fedeli, conteggio delle prese a fine partita, rivincita, suoni di carte, comandi con mouse, tocco e tastiera. Carte e dorso col Regolo serpentello disegnati da Codex; motore e prove nel browser rivisti da Claude
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
import { canPlay, cardName, counted, countRows } from './briscola.js';
|
||||
import { text, panel } from './bocce-render.js';
|
||||
|
||||
// Coordinate condivise da disegno e hit test: bersagli larghi anche sul telefono.
|
||||
export const HAND = { x: 176, y: 230, gap: 102, w: 84, h: 120 };
|
||||
export function cardAt(c, [x, y]) {
|
||||
if (!canPlay(c)) return -1;
|
||||
return c.hands.player.findIndex((_, i) => x >= HAND.x + i * HAND.gap && x < HAND.x + i * HAND.gap + HAND.w && y >= HAND.y && y < HAND.y + HAND.h);
|
||||
}
|
||||
const ink = '#38271f', paper = '#fff0cd', gold = '#dba32b', red = '#aa4034', blue = '#386a89', green = '#487348';
|
||||
function rect(ctx, x, y, w, h, color) { ctx.fillStyle = color; ctx.fillRect(x, y, w, h); }
|
||||
function poly(ctx, points, color) {
|
||||
ctx.fillStyle = color; ctx.beginPath(); points.forEach(([x, y], i) => i ? ctx.lineTo(x, y) : ctx.moveTo(x, y)); ctx.closePath(); ctx.fill();
|
||||
}
|
||||
// Simboli originali: moneta incisa, calice, lama ricurva e bastone coi nodi.
|
||||
function suit(ctx, name, x, y) {
|
||||
ctx.save(); ctx.translate(x, y);
|
||||
if (name === 'denari') {
|
||||
rect(ctx, -4, -3, 8, 6, ink); rect(ctx, -3, -4, 6, 8, ink);
|
||||
rect(ctx, -3, -2, 6, 4, gold); rect(ctx, -2, -3, 4, 6, gold);
|
||||
rect(ctx, -2, -2, 1, 4, '#ffe58c'); rect(ctx, 0, -1, 2, 2, '#9e6b25');
|
||||
} else if (name === 'coppe') {
|
||||
rect(ctx, -4, -4, 8, 2, ink); rect(ctx, -3, -2, 6, 3, gold);
|
||||
rect(ctx, -2, 0, 4, 2, red); rect(ctx, -1, 2, 2, 2, ink);
|
||||
rect(ctx, -3, 4, 6, 1, gold); rect(ctx, -3, -3, 6, 1, '#ffdd70');
|
||||
} else if (name === 'spade') {
|
||||
poly(ctx, [[-4,5],[-3,1],[0,-1],[2,-5],[4,-6],[3,-1],[-1,3],[-2,6]], ink);
|
||||
rect(ctx, 1, -3, 2, 3, '#9eb3ba'); rect(ctx, -1, 0, 2, 2, '#c9d8d6');
|
||||
rect(ctx, -5, 3, 5, 1, gold); rect(ctx, -4, 5, 2, 2, red);
|
||||
} else {
|
||||
poly(ctx, [[-3,6],[-4,4],[-1,-1],[0,-6],[3,-5],[3,-2],[0,3],[-1,6]], ink);
|
||||
rect(ctx, 0, -4, 2, 5, '#94632e'); rect(ctx, -2, 1, 2, 4, '#ae7a39');
|
||||
rect(ctx, 2, -3, 2, 2, gold); rect(ctx, -3, 2, 2, 2, gold); rect(ctx, 3, -5, 2, 1, green);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
function regolo(ctx) {
|
||||
// Cornice simmetrica e un piccolo re sorridente, avvolto nelle proprie spire.
|
||||
rect(ctx, 3, 3, 36, 54, blue); rect(ctx, 5, 5, 32, 50, gold); rect(ctx, 6, 6, 30, 48, '#203e44');
|
||||
for (let y = 9; y < 54; y += 6) for (const x of [8, 32]) rect(ctx, x, y, 2, 2, '#63867c');
|
||||
poly(ctx, [[15,32],[28,32],[32,36],[32,43],[28,47],[14,47],[10,43],[10,37]], ink);
|
||||
poly(ctx, [[15,34],[27,34],[30,37],[30,42],[27,45],[15,45],[12,42],[12,38]], green);
|
||||
poly(ctx, [[17,37],[26,37],[28,39],[27,41],[16,41],[15,39]], '#203e44');
|
||||
rect(ctx, 14, 42, 13, 2, '#a3bc72'); rect(ctx, 16, 36, 11, 1, '#a3bc72');
|
||||
poly(ctx, [[19,25],[25,25],[25,33],[23,36],[18,36],[17,33],[20,32]], green);
|
||||
rect(ctx, 20, 28, 2, 6, '#d0cd83');
|
||||
poly(ctx, [[15,16],[27,16],[30,19],[30,25],[27,29],[15,29],[12,25],[12,20]], ink);
|
||||
poly(ctx, [[16,17],[26,17],[29,20],[29,24],[26,27],[16,27],[13,24],[13,21]], '#94b365');
|
||||
rect(ctx, 15, 19, 4, 4, paper); rect(ctx, 23, 19, 4, 4, paper);
|
||||
rect(ctx, 17, 20, 2, 3, ink); rect(ctx, 23, 20, 2, 3, ink);
|
||||
rect(ctx, 18, 25, 6, 1, ink); rect(ctx, 16, 24, 2, 1, ink); rect(ctx, 24, 24, 2, 1, ink);
|
||||
rect(ctx, 17, 12, 9, 4, gold); for (const x of [17, 21, 25]) rect(ctx, x, 9, 1, 4, '#ffe396');
|
||||
rect(ctx, 21, 13, 1, 2, red);
|
||||
for (const x of [15, 25]) { rect(ctx, x, 50, 2, 2, gold); rect(ctx, x + 1, 49, 1, 4, gold); }
|
||||
}
|
||||
function figure(ctx, rank, name) {
|
||||
// Figure intere: fante in tunica, cavaliere a cavallo, re con corona e manto.
|
||||
const king = rank === 'Re', horse = rank === 'Cavallo', y = horse ? 15 : 17;
|
||||
if (horse) {
|
||||
poly(ctx, [[8,36],[12,29],[27,30],[30,23],[35,23],[37,29],[32,33],[31,41],[12,41]], ink);
|
||||
rect(ctx, 11, 33, 20, 7, '#b78044'); rect(ctx, 30, 26, 4, 8, '#c79c60');
|
||||
rect(ctx, 12, 40, 3, 10, ink); rect(ctx, 27, 39, 3, 11, ink);
|
||||
rect(ctx, 15, 40, 2, 8, '#a36b3c'); rect(ctx, 32, 27, 1, 1, paper);
|
||||
}
|
||||
if (king) poly(ctx, [[15,24],[29,24],[33,48],[10,48]], red);
|
||||
poly(ctx, [[19,y],[25,y],[27,y+2],[28,y+4],[26,y+5],[26,y+8],[20,y+9],[17,y+6],[17,y+2]], ink);
|
||||
poly(ctx, [[20,y+1],[25,y+1],[25,y+3],[27,y+4],[25,y+5],[25,y+7],[21,y+8],[19,y+5]], '#eab578');
|
||||
rect(ctx, 18, y + 1, 2, 4, '#86502d'); rect(ctx, 20, y + 1, 4, 1, '#ffe0a2');
|
||||
rect(ctx, 24, y + 2, 1, 2, ink); rect(ctx, 24, y + 6, 3, 1, ink);
|
||||
rect(ctx, 17, y - 2, 10, 3, king ? gold : blue);
|
||||
if (king) for (const x of [17, 21, 25]) rect(ctx, x, y - 5, 2, 4, gold);
|
||||
else rect(ctx, 18, y - 5, 2, 4, red);
|
||||
poly(ctx, [[18,y+9],[26,y+9],[29,y+12],[27,y+19],[29,y+24],[15,y+24],[17,y+19],[15,y+12]], ink);
|
||||
poly(ctx, [[19,y+10],[25,y+10],[27,y+12],[25,y+19],[27,y+22],[17,y+22],[19,y+18],[17,y+12]], horse ? red : blue);
|
||||
rect(ctx, 18, y + 10, 3, 10, king ? gold : '#6291a5'); rect(ctx, 17, y + 19, 11, 2, gold);
|
||||
rect(ctx, 23, y + 11, 1, 6, paper); rect(ctx, 23, y + 19, 2, 2, ink);
|
||||
rect(ctx, 12, y + 11, 4, 3, '#eab578'); rect(ctx, 28, y + 10, 4, 4, '#eab578');
|
||||
if (!horse) {
|
||||
poly(ctx, [[17,y+24],[21,y+24],[20,y+32],[16,y+32]], ink);
|
||||
poly(ctx, [[23,y+24],[27,y+24],[29,y+32],[25,y+32]], ink);
|
||||
rect(ctx, 18, y + 24, 2, 5, '#b9884f'); rect(ctx, 24, y + 24, 2, 4, '#b9884f');
|
||||
rect(ctx, 15, y + 32, 6, 2, red); rect(ctx, 24, y + 32, 6, 2, red);
|
||||
}
|
||||
suit(ctx, name, 10, 24);
|
||||
if (king) { rect(ctx, 32, 22, 1, 24, gold); rect(ctx, 31, 20, 3, 3, gold); }
|
||||
}
|
||||
const PIPS = {
|
||||
Asso: [[21,32]], 2: [[21,22],[21,42]], 3: [[21,20],[21,32],[21,44]],
|
||||
4: [[13,23],[29,23],[13,41],[29,41]],
|
||||
5: [[13,21],[29,21],[21,32],[13,43],[29,43]],
|
||||
6: [[13,20],[29,20],[13,32],[29,32],[13,44],[29,44]],
|
||||
7: [[13,19],[29,19],[13,32],[29,32],[13,45],[29,45],[21,32]],
|
||||
};
|
||||
// Le toscane hanno semi francesi, nessun indice sulle numerali e figure intere.
|
||||
const TUSCAN_COLORS = {
|
||||
cuori: { cloth: '#326447', light: '#65936a', cloak: '#ab393b', ground: '#bc7760' },
|
||||
mattoni: { cloth: '#a93832', light: '#d56a46', cloak: '#315f7e', ground: '#b3a05b' },
|
||||
fiori: { cloth: '#315e85', light: '#6995ac', cloak: '#bc842e', ground: '#829968' },
|
||||
picche: { cloth: '#755079', light: '#ac7d9a', cloak: '#376b66', ground: '#9e8271' },
|
||||
};
|
||||
const TUSCAN_PIPS = {
|
||||
Asso: [[21,30]], 2: [[21,17],[21,43]], 3: [[21,15],[21,30],[21,45]],
|
||||
4: [[12,16],[30,16],[12,44],[30,44]],
|
||||
5: [[12,16],[30,16],[21,30],[12,44],[30,44]],
|
||||
6: [[12,15],[30,15],[12,30],[30,30],[12,45],[30,45]],
|
||||
7: [[10,14],[32,14],[21,22],[10,30],[32,30],[10,46],[32,46]],
|
||||
};
|
||||
function tuscanSuit(ctx, name, x, y, inverted = false) {
|
||||
ctx.save(); ctx.translate(x, y);
|
||||
if (inverted) ctx.scale(1, -1);
|
||||
const color = name === 'cuori' || name === 'mattoni' ? '#c12f35' : '#202326';
|
||||
// Sagome a gradini sulla griglia: nessuna sfumatura nei semi.
|
||||
if (name === 'cuori') {
|
||||
poly(ctx, [[-4,-4],[-1,-4],[0,-3],[1,-4],[4,-4],[5,-3],[5,0],[4,1],[4,2],[3,2],[3,3],[2,3],[2,4],[1,4],[1,5],[0,6],[-1,5],[-1,4],[-2,4],[-2,3],[-3,3],[-3,2],[-4,2],[-4,1],[-5,0],[-5,-3]], color);
|
||||
} else if (name === 'mattoni') {
|
||||
poly(ctx, [[0,-6],[1,-4],[2,-3],[3,-2],[5,0],[3,2],[2,3],[1,4],[0,6],[-1,4],[-2,3],[-3,2],[-5,0],[-3,-2],[-2,-3],[-1,-4]], color);
|
||||
} else if (name === 'fiori') {
|
||||
poly(ctx, [[-2,-5],[2,-5],[3,-4],[3,-2],[2,-1],[4,-2],[6,0],[6,2],[4,4],[2,4],[1,3],[1,4],[3,6],[-3,6],[-1,4],[-1,3],[-2,4],[-4,4],[-6,2],[-6,0],[-4,-2],[-2,-1],[-3,-2],[-3,-4]], color);
|
||||
} else {
|
||||
poly(ctx, [[0,-6],[1,-5],[1,-4],[3,-2],[5,0],[5,3],[3,4],[1,3],[1,4],[3,6],[-3,6],[-1,4],[-1,3],[-3,4],[-5,3],[-5,0],[-3,-2],[-1,-4],[-1,-5]], color);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
function tuscanFigure(ctx, rank, name) {
|
||||
const p = TUSCAN_COLORS[name], skin = '#e9b785', shade = '#bf805d', trim = '#e0b957';
|
||||
const queen = rank === 'Donna', king = rank === 'Regio';
|
||||
// Cornice sottile, terreno e silhouette originali, disegnati su una griglia 42×60.
|
||||
rect(ctx, 3, 3, 36, 54, '#b6a48a'); rect(ctx, 3.5, 3.5, 35, 53, paper);
|
||||
poly(ctx, [[4,51],[14,49],[26,50],[38,48],[38,56],[4,56]], p.ground);
|
||||
if (queen) {
|
||||
// Dama di profilo: diadema, manica a sbuffo, gonna a ventaglio e fazzoletto.
|
||||
poly(ctx, [[21,11],[27,11],[29,14],[28,22],[31,26],[27,29],[19,24],[18,18]], ink);
|
||||
poly(ctx, [[22,12],[26,12],[27,15],[25,19],[22,21],[20,18],[19,17],[21,16]], skin);
|
||||
rect(ctx, 23, 12, 4, 2, '#70452c'); rect(ctx, 26, 14, 2, 7, '#70452c');
|
||||
rect(ctx, 21, 15, 1, 1, ink); rect(ctx, 20, 18, 2, 1, shade);
|
||||
poly(ctx, [[21,12],[21,10],[23,11],[24,9],[25,11],[27,10],[27,13]], trim);
|
||||
rect(ctx, 24, 10, 1, 1, '#c12f35');
|
||||
poly(ctx, [[21,21],[26,20],[30,24],[28,31],[31,39],[37,54],[12,54],[15,43],[18,33],[17,26]], ink);
|
||||
poly(ctx, [[22,22],[26,21],[28,24],[26,31],[29,39],[35,53],[14,53],[17,42],[20,32],[19,26]], p.cloth);
|
||||
poly(ctx, [[21,32],[25,32],[25,40],[31,52],[23,51],[19,46]], p.light);
|
||||
poly(ctx, [[26,34],[29,41],[33,51],[30,48],[27,43]], trim);
|
||||
poly(ctx, [[19,35],[18,45],[16,52],[20,52],[21,49],[19,47]], p.cloak);
|
||||
poly(ctx, [[18,23],[22,25],[20,29],[16,31],[12,29],[13,26]], '#d8dfd7');
|
||||
rect(ctx, 17, 25, 4, 2, '#94a7ad');
|
||||
poly(ctx, [[14,28],[16,30],[12,33],[9,32],[10,30]], skin);
|
||||
poly(ctx, [[9,31],[12,32],[10,35],[8,35],[6,40],[6,35]], '#fcf7df');
|
||||
rect(ctx, 8, 34, 1, 3, '#94a7ad');
|
||||
poly(ctx, [[20,29],[26,29],[27,31],[20,33],[18,32]], trim);
|
||||
rect(ctx, 22, 23, 1, 3, trim); rect(ctx, 16, 54, 7, 1, ink);
|
||||
} else if (king) {
|
||||
// Regio anziano: corona sempre visibile, barba bianca e bordi d'ermellino.
|
||||
poly(ctx, [[15,23],[27,22],[32,27],[36,54],[9,54],[11,31]], ink);
|
||||
poly(ctx, [[15,24],[27,23],[30,28],[34,53],[11,53],[13,31]], p.cloak);
|
||||
poly(ctx, [[18,27],[25,27],[27,52],[16,52]], p.cloth);
|
||||
rect(ctx, 20, 33, 3, 18, p.light); rect(ctx, 16, 48, 11, 2, trim);
|
||||
poly(ctx, [[15,24],[18,25],[16,34],[15,45],[12,52],[11,51],[13,33]], '#f6edcf');
|
||||
poly(ctx, [[26,24],[28,25],[29,35],[32,51],[29,52],[26,35],[24,27]], '#f6edcf');
|
||||
for (const [x,y] of [[14,29],[13,37],[12,46],[27,29],[28,38],[30,47]]) rect(ctx, x, y, 1, 2, ink);
|
||||
poly(ctx, [[17,13],[24,12],[27,16],[26,23],[23,28],[18,26],[15,21],[15,16]], '#b9bfbb');
|
||||
poly(ctx, [[18,14],[24,14],[25,17],[27,19],[24,20],[24,22],[19,22],[17,18]], skin);
|
||||
rect(ctx, 23, 16, 1, 2, ink); rect(ctx, 18, 16, 1, 2, shade);
|
||||
poly(ctx, [[16,18],[19,21],[22,20],[25,21],[24,25],[21,28],[18,25],[16,23]], '#f7f0d7');
|
||||
rect(ctx, 21, 22, 3, 1, '#9ba8a3'); rect(ctx, 18, 22, 1, 3, '#c6cdc0');
|
||||
poly(ctx, [[16,14],[15,8],[17,8],[18,11],[20,10],[20,7],[22,7],[22,10],[24,11],[25,8],[27,8],[26,14]], ink);
|
||||
poly(ctx, [[17,13],[16,9],[17,9],[18,12],[21,11],[21,8],[22,12],[25,12],[26,9],[25,13]], trim);
|
||||
rect(ctx, 17, 13, 9, 2, trim); rect(ctx, 21, 13, 2, 1, '#c12f35');
|
||||
rect(ctx, 33, 23, 1, 30, '#9b6a29'); rect(ctx, 33, 24, 1, 27, trim);
|
||||
poly(ctx, [[33,19],[35,21],[35,23],[33,25],[31,23],[31,21]], trim);
|
||||
rect(ctx, 33, 21, 1, 2, '#fff1ae'); rect(ctx, 31, 32, 4, 3, skin);
|
||||
rect(ctx, 10, 33, 4, 3, skin); rect(ctx, 15, 53, 6, 2, ink); rect(ctx, 24, 53, 6, 2, ink);
|
||||
} else {
|
||||
// Gobbo giovane, senza corona: berretto morbido, mantello corto e mazza.
|
||||
poly(ctx, [[15,23],[26,22],[31,27],[33,39],[27,43],[11,40],[10,28]], ink);
|
||||
poly(ctx, [[15,24],[25,23],[29,27],[31,38],[27,41],[12,39],[12,28]], p.cloak);
|
||||
poly(ctx, [[18,37],[23,38],[21,48],[20,53],[15,53],[17,46]], ink);
|
||||
poly(ctx, [[24,37],[28,37],[28,46],[31,53],[26,53],[23,46]], ink);
|
||||
rect(ctx, 18, 41, 2, 9, '#d3ad60'); rect(ctx, 25, 41, 2, 6, '#d3ad60');
|
||||
rect(ctx, 16, 50, 4, 4, '#744531'); rect(ctx, 27, 50, 3, 4, '#744531');
|
||||
rect(ctx, 13, 54, 7, 1, ink); rect(ctx, 26, 54, 7, 1, ink);
|
||||
poly(ctx, [[17,12],[24,12],[27,16],[26,23],[22,25],[17,22],[15,18]], '#70452c');
|
||||
poly(ctx, [[19,14],[24,14],[24,16],[26,18],[24,19],[24,21],[21,23],[18,20]], skin);
|
||||
rect(ctx, 23, 16, 1, 2, ink); rect(ctx, 23, 20, 2, 1, shade);
|
||||
rect(ctx, 17, 15, 2, 7, '#70452c'); rect(ctx, 16, 19, 2, 5, '#70452c');
|
||||
poly(ctx, [[16,14],[17,11],[21,10],[25,11],[26,14],[28,15],[16,15]], p.cloth);
|
||||
rect(ctx, 18, 13, 8, 1, p.light);
|
||||
poly(ctx, [[18,24],[24,23],[27,27],[25,33],[28,40],[15,40],[17,33],[15,27]], ink);
|
||||
poly(ctx, [[19,25],[23,24],[25,27],[23,33],[26,39],[17,39],[19,33],[17,27]], p.cloth);
|
||||
rect(ctx, 20, 26, 2, 7, p.light); rect(ctx, 16, 34, 10, 2, trim);
|
||||
poly(ctx, [[18,23],[21,25],[20,29],[17,25]], '#f6edcf');
|
||||
rect(ctx, 21, 34, 2, 2, ink);
|
||||
poly(ctx, [[10,27],[13,29],[12,33],[9,34],[8,32]], skin);
|
||||
poly(ctx, [[27,28],[30,30],[28,34],[25,35],[24,33],[27,32]], skin);
|
||||
poly(ctx, [[7,30],[9,30],[9,46],[11,50],[11,53],[6,53],[6,49],[7,46]], ink);
|
||||
rect(ctx, 8, 33, 1, 17, '#ae783a'); rect(ctx, 7, 49, 3, 3, trim);
|
||||
rect(ctx, 7, 31, 3, 2, skin);
|
||||
}
|
||||
ctx.save(); ctx.translate(9, 8); ctx.scale(0.65, 0.65); tuscanSuit(ctx, name, 0, 0); ctx.restore();
|
||||
text(ctx, queen ? 'D' : king ? 'R' : 'G', 7, 16, 4, ink);
|
||||
}
|
||||
function tuscanCard(ctx, card) {
|
||||
// Angoli arrotondati a gradini e un solo bordo sottile; nessuna scritta al piede.
|
||||
const outline = [[2,0],[40,0],[42,2],[42,58],[40,60],[2,60],[0,58],[0,2]];
|
||||
ctx.save(); ctx.translate(1, 2); poly(ctx, outline, '#15231d'); ctx.restore();
|
||||
poly(ctx, outline, '#89765f');
|
||||
poly(ctx, [[2,1],[40,1],[41,2],[41,58],[40,59],[2,59],[1,58],[1,2]], paper);
|
||||
if (TUSCAN_PIPS[card.rank]) {
|
||||
for (const [x, y] of TUSCAN_PIPS[card.rank]) tuscanSuit(ctx, card.suit, x, y, y > 30);
|
||||
} else tuscanFigure(ctx, card.rank, card.suit);
|
||||
}
|
||||
export function drawCard(ctx, card, x, y, w = 84, h = 120, back = false) {
|
||||
ctx.save(); ctx.translate(Math.round(x), Math.round(y)); ctx.scale(w / 42, h / 60);
|
||||
if (!back && TUSCAN_COLORS[card.suit]) { tuscanCard(ctx, card); ctx.restore(); return; }
|
||||
rect(ctx, 1, 2, 42, 60, '#15231d'); rect(ctx, 0, 0, 42, 60, ink);
|
||||
rect(ctx, 1, 1, 40, 58, '#c5a576'); rect(ctx, 2, 2, 38, 56, paper);
|
||||
if (back) regolo(ctx);
|
||||
else {
|
||||
const rank = { Asso: 'A', Fante: 'F', Cavallo: 'C', Re: 'R', Gobbo: 'G', Donna: 'D', Regio: 'R' }[card.rank] ?? card.rank;
|
||||
text(ctx, rank, 4, 7, 6, ink); suit(ctx, card.suit, 34, 8);
|
||||
if (PIPS[card.rank]) {
|
||||
for (const [px, py] of PIPS[card.rank]) {
|
||||
// Una lama per simbolo: le coppie si allungano e s'incrociano al centro.
|
||||
if (card.suit === 'spade' && (px === 13 || px === 29)) {
|
||||
ctx.save(); ctx.translate(px === 13 ? 18 : 24, py); ctx.scale(px === 13 ? 1.8 : -1.8, 1);
|
||||
suit(ctx, card.suit, 0, 0); ctx.restore();
|
||||
} else suit(ctx, card.suit, px, py);
|
||||
}
|
||||
} else figure(ctx, card.rank, card.suit);
|
||||
text(ctx, card.suit.toUpperCase(), 21, 55, 3.7, ink, 'center');
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
function countTable(ctx, c, names) {
|
||||
panel(ctx, 12, 32, 616, 318);
|
||||
const rows = countRows(c), shown = rows.slice(0, c.count), totals = counted(c);
|
||||
for (const [side, who] of ['player', 'cpu'].entries()) {
|
||||
const x = 25 + side * 310;
|
||||
text(ctx, names[who], x, 48, 7);
|
||||
const cards = shown.filter(p => p.who === who);
|
||||
cards.forEach(({ card }, i) => {
|
||||
const cx = x + (i % 8) * 36, cy = 64 + Math.floor(i / 8) * 49;
|
||||
drawCard(ctx, card, cx, cy, 30, 40);
|
||||
text(ctx, '+' + card.points, cx + 15, cy + 44, 5, '#ffe09b', 'center');
|
||||
});
|
||||
text(ctx, `${totals[who]} punti`, x, 321, 10, '#ffe09b');
|
||||
}
|
||||
const last = shown.at(-1);
|
||||
if (c.step === 'count' && last) {
|
||||
panel(ctx, 270, 115, 100, 140); drawCard(ctx, last.card, 278, 123);
|
||||
}
|
||||
text(ctx, c.step === 'result' ? c.result.winner ? `Vince ${names[c.result.winner]}!` : '60 a 60. Patta!' : last ? `${cardName(last.card)}: ${last.card.points} punti` : 'Si contano tutte le carte…', 320, 339, 6, paper, 'center');
|
||||
}
|
||||
export function drawBriscola(ctx, c, def) {
|
||||
ctx.save(); ctx.imageSmoothingEnabled = false;
|
||||
rect(ctx, 0, 0, 640, 360, '#573720');
|
||||
for (let y = 0; y < 360; y += 24) {
|
||||
rect(ctx, 0, y, 640, 2, '#38271e');
|
||||
for (let x = 0; x < 640; x += 53) rect(ctx, x + y % 17, y + 6, 29, 1, '#72462a');
|
||||
}
|
||||
rect(ctx, 17, 27, 606, 329, '#b28949'); rect(ctx, 20, 30, 600, 323, '#234c40');
|
||||
for (let i = 0; i < 600; i++) rect(ctx, 23 + (i * 73) % 590, 33 + (i * 41) % 315, 1, 1, '#2a5143');
|
||||
text(ctx, 'BRISCOLA ALL’ORATORIO', 320, 15, 9, '#ffe0a0', 'center');
|
||||
if (c.step === 'count' || c.step === 'result') { countTable(ctx, c, def.names); ctx.restore(); return; }
|
||||
text(ctx, def.names.cpu, 320, 38, 6, paper, 'center');
|
||||
c.hands.cpu.forEach((_, i) => drawCard(ctx, null, 229 + i * 62, 49, 52, 74, true));
|
||||
// La briscola sporge sotto il mazzo, girata di traverso: più di metà resta scoperta, anche l'asso che ha
|
||||
// un seme solo nel mezzo (le toscane non hanno numeri negli angoli).
|
||||
if (c.stock.length) {
|
||||
ctx.save(); ctx.translate(154, 150); ctx.rotate(Math.PI / 2);
|
||||
drawCard(ctx, c.trumpCard, 0, 0, 64, 92); ctx.restore();
|
||||
if (c.stock.length > 1) {
|
||||
for (let i = Math.min(5, Math.ceil(c.stock.length / 7)); i >= 0; i--)
|
||||
drawCard(ctx, null, 38 + i, 116 - i, 58, 83, true);
|
||||
}
|
||||
}
|
||||
text(ctx, `Mazzo: ${c.stock.length}`, 34, 243, 7);
|
||||
text(ctx, 'Briscola:', 34, 263, 7); // a 6 punti la B sembra una D
|
||||
text(ctx, c.trump.toUpperCase(), 34, 278, 7, '#ffe09b');
|
||||
for (const [i, who] of ['cpu', 'player'].entries()) {
|
||||
const y = 74 + i * 183;
|
||||
if (c.taken[who].length) for (let n = 0; n < Math.min(4, c.taken[who].length / 2); n++) drawCard(ctx, null, 526 + n * 2, y - n * 2, 42, 60, true);
|
||||
text(ctx, 'Prese', 550, y + 72, 6, paper, 'center');
|
||||
}
|
||||
c.table.forEach((p, i) => {
|
||||
// Il brevissimo scorrimento precede il fermo della presa.
|
||||
const slide = i === c.table.length - 1 ? Math.max(0, 1 - c.t / 0.18) : 0;
|
||||
drawCard(ctx, p.card, 242 + i * 86, 130 + slide * (p.who === 'player' ? 60 : -40), 64, 92);
|
||||
});
|
||||
c.hands.player.forEach((card, i) => {
|
||||
const x = HAND.x + i * HAND.gap;
|
||||
if (canPlay(c) && c.selected === i) rect(ctx, x - 3, HAND.y - 3, HAND.w + 6, HAND.h + 6, '#ffdb76');
|
||||
drawCard(ctx, card, x, HAND.y, HAND.w, HAND.h);
|
||||
text(ctx, String(i + 1), x + HAND.w / 2, 221, 7, '#fff0cd', 'center');
|
||||
});
|
||||
ctx.restore();
|
||||
}
|
||||
Reference in New Issue
Block a user