import { LEN, WID, R, VIEW, WINDUP } from './biliardo.js'; import { table, sphere, pin, cue } from './biliardo-render.js'; import { guide, canThrow, shooter } from './rinquarto.js'; import { text, panel } from './bocce-render.js'; const sx = x => VIEW.x + x * VIEW.k, sy = y => VIEW.y + y * VIEW.k; const NAME = { andrea: 'Andrea', bertovo: 'Trappizzio Berti' }; // La traiettoria del tiro: resta sul tavolo finché non si va avanti, con le sponde numerate e il contatto che decide. function trail(ctx, c) { if (c.trail.length < 2) return; const good = c.attempt.point, done = c.step === 'review'; ctx.strokeStyle = done ? (good ? '#b9e5b3' : '#ffbf9f') : '#fff8dccc'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(sx(c.trail[0][0]), sy(c.trail[0][1])); for (const [x, y] of c.trail.slice(1)) ctx.lineTo(sx(x), sy(y)); ctx.stroke(); c.attempt.marks.forEach((m, i) => { ctx.fillStyle = '#17160f'; ctx.beginPath(); ctx.arc(sx(m.x), sy(m.y), 5, 0, Math.PI * 2); ctx.fill(); text(ctx, String(i + 1), sx(m.x), sy(m.y) + 0.5, 7, '#ffe28a', 'center'); }); if (done && c.attempt.hit) { const { x, y } = c.attempt.hit; ctx.strokeStyle = good ? '#b9e5b3' : '#ff6a4a'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(sx(x), sy(y), R * VIEW.k + 4, 0, Math.PI * 2); ctx.stroke(); } } export function drawRinquarto(ctx, c, def, bg) { ctx.save(); ctx.imageSmoothingEnabled = false; table(ctx, bg); ctx.save(); ctx.beginPath(); ctx.rect(VIEW.x - 15, VIEW.y - 15, LEN * VIEW.k + 30, WID * VIEW.k + 30); ctx.clip(); if (canThrow(c)) { const points = guide(c); if (points.length) { ctx.strokeStyle = '#fff8dc'; ctx.lineWidth = 1; ctx.setLineDash([4, 3]); ctx.beginPath(); ctx.moveTo(sx(points[0][0]), sy(points[0][1])); for (const [x, y] of points.slice(1)) ctx.lineTo(sx(x), sy(y)); ctx.stroke(); ctx.setLineDash([]); } const b = c.balls.player; cue(ctx, b.x, b.y, c.angle, R * VIEW.k + 6 + (c.charging ? 26 : 10)); } else if (c.visual.shot && c.windup > 0) { const s = c.visual.shot; cue(ctx, s.x, s.y, s.angle, R * VIEW.k + 32 * c.windup / WINDUP); } trail(ctx, c); for (const p of c.pins) pin(ctx, p, c.visual.time); // Il bersaglio ha un anello che pulsa; i disturbi una croce grossa, che si vede anche sul telefono. const tgt = c.balls.cpu, pulse = 1 + Math.sin(c.visual.time * 4) * 0.5; ctx.strokeStyle = '#ffe28a'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.arc(sx(tgt.x), sy(tgt.y), R * VIEW.k + 3 + pulse, 0, Math.PI * 2); ctx.stroke(); for (const b of Object.values(c.balls)) { sphere(ctx, b.role, sx(b.x), sy(b.y), R * VIEW.k); if (!['player', 'cpu'].includes(b.role)) { const k = R * VIEW.k; ctx.strokeStyle = '#1b0d29'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(sx(b.x) - k, sy(b.y) - k); ctx.lineTo(sx(b.x) + k, sy(b.y) + k); ctx.moveTo(sx(b.x) + k, sy(b.y) - k); ctx.lineTo(sx(b.x) - k, sy(b.y) + k); ctx.stroke(); } } ctx.restore(); // In alto: chi tira, il punteggio, le sponde; l'esito sta qui, così non copre il tavolo. ctx.fillStyle = '#17160ff0'; ctx.fillRect(54, 4, 532, 64); text(ctx, c.skill.training ? 'I RINQUARTI DI TRAPPIZZIO BERTI · ALLENAMENTO' : 'I RINQUARTI DI TRAPPIZZIO BERTI', 320, 13, 9, '#efd49e', 'center'); const score = c.skill.training ? 'senza punti' : `Andrea ${c.score.andrea} Trappizzio Berti ${c.score.bertovo}`; text(ctx, `Posizione ${c.position}/${c.skill.positions} ${score} Sponde ${c.attempt.cushions}/${c.skill.cushions}`, 320, 29, 9, '#fff0c8', 'center'); const who = NAME[shooter(c)]; const line = c.step === 'review' ? c.line : c.step === 'think' ? 'Tira Trappizzio Berti…' : c.step === 'rolling' ? `Tira ${who}…` : `Tocca ad ${who}: «Oh tirami questo rinquarto!?»`; text(ctx, line, 320, 45, 8, c.step === 'review' ? (c.attempt.point ? '#b9e5b3' : '#ffbf9f') : '#9fd0ff', 'center'); if (c.step === 'review') text(ctx, c.last ? 'Tocca per il risultato' : 'Tocca o premi Prossima', 320, 59, 7, '#d9ccba', 'center'); if (c.step === 'result') { const r = c.result, title = r.training ? 'ALLENAMENTO FINITO' : r.winner === 'player' ? 'HAI BATTUTO TRAPPIZZIO BERTI!' : r.winner === 'cpu' ? 'HA VINTO TRAPPIZZIO BERTI' : 'PARI'; panel(ctx, 155, 145, 330, 80); text(ctx, title, 320, 166, 12, r.winner === 'player' ? '#b9e5b3' : '#efd49e', 'center'); if (!r.training) text(ctx, `Andrea ${r.points.andrea} · Trappizzio Berti ${r.points.bertovo}, su ${c.skill.positions} posizioni`, 320, 190, 8, '#efd49e', 'center'); text(ctx, 'Torna da Trappizzio Berti per uscire.', 320, 210, 8, '#d9ccba', 'center'); } ctx.restore(); }