Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9KMfjthRUnujCGSzZ
119 lines
8.0 KiB
JavaScript
119 lines
8.0 KiB
JavaScript
import { CARTONE, BENCH, CAN, BAR, BOTTLE, BALLOON, VOLO, PELLETS, STILL, BREATH, GASP, rings, reticle, landing, balloon, voloAt, canThrow, breathless } from './condor.js';
|
|
import { text, panel } from './bocce-render.js';
|
|
|
|
function rect(ctx, x, y, w, h, color) { ctx.fillStyle = color; ctx.fillRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h)); }
|
|
// Il cartone inchiodato al tronco: tre cerchi col pennarello, i chiodini in alto e i buchi dei pallini.
|
|
function cartone(ctx, c) {
|
|
const { x, y, w, h } = CARTONE;
|
|
rect(ctx, x - w / 2 + 2, y - h / 2 + 2, w, h, 'rgba(30,20,10,0.35)');
|
|
rect(ctx, x - w / 2, y - h / 2, w, h, '#c9a46a');
|
|
rect(ctx, x - w / 2, y - h / 2, w, 2, '#e0bf86'); rect(ctx, x - w / 2, y + h / 2 - 2, w, 2, '#a8844e');
|
|
rect(ctx, x - w / 2 + 3, y - h / 2 + 2, 2, 2, '#5a5a5a'); rect(ctx, x + w / 2 - 5, y - h / 2 + 2, 2, 2, '#5a5a5a');
|
|
ctx.strokeStyle = '#2a1f1a'; ctx.lineWidth = 1.2;
|
|
for (const [r] of rings(c)) { ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.stroke(); }
|
|
ctx.fillStyle = '#2a1f1a'; ctx.beginPath(); ctx.arc(x, y, 2, 0, Math.PI * 2); ctx.fill();
|
|
for (const p of c.holes) { rect(ctx, p.x - 1, p.y - 1, 2, 2, '#1a120c'); rect(ctx, p.x, p.y - 1, 1, 1, '#f2d24a'); }
|
|
}
|
|
// Una lattina (sulla panchina o in aria), rossa con la fascia chiara.
|
|
function can(ctx, x, bottom, angle = 0, w = CAN.w, h = CAN.h, color = '#c8321e') {
|
|
ctx.save(); ctx.translate(Math.round(x), Math.round(bottom)); ctx.rotate(angle);
|
|
ctx.fillStyle = color; ctx.fillRect(-w / 2, -h, w, h);
|
|
ctx.fillStyle = '#f2e6c8'; ctx.fillRect(-w / 2, -h + Math.round(h * 0.35), w, 3);
|
|
ctx.fillStyle = '#c9cdd2'; ctx.fillRect(-w / 2, -h, w, 2); ctx.fillRect(-w / 2, -2, w, 2);
|
|
ctx.fillStyle = 'rgba(255,255,255,0.35)'; ctx.fillRect(-w / 2 + 2, -h + 2, 1, h - 4);
|
|
ctx.restore();
|
|
}
|
|
// Una bottiglietta d'acqua, vuota: plastica azzurrina e il tappo blu.
|
|
function bottle(ctx, x, bottom, angle = 0) {
|
|
const { w, h } = BOTTLE;
|
|
ctx.save(); ctx.translate(Math.round(x), Math.round(bottom)); ctx.rotate(angle);
|
|
ctx.fillStyle = 'rgba(190,228,240,0.85)'; ctx.fillRect(-w / 2, -h + 4, w, h - 4); ctx.fillRect(-w / 2 + 1, -h + 2, w - 2, 2);
|
|
ctx.fillStyle = '#2f6fb0'; ctx.fillRect(-1, -h, 3, 2);
|
|
ctx.fillStyle = '#ffffff'; ctx.fillRect(-w / 2 + 1, -h + 5, 1, h - 7);
|
|
ctx.restore();
|
|
}
|
|
// Il pallino giallo.
|
|
function pellet(ctx, x, y, r) {
|
|
ctx.fillStyle = '#7a5a10'; ctx.beginPath(); ctx.arc(x, y, r + 0.7, 0, Math.PI * 2); ctx.fill();
|
|
ctx.fillStyle = '#f5d02a'; ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.fill();
|
|
if (r > 1.5) rect(ctx, x - r / 2, y - r / 2, 1, 1, '#fff6b0');
|
|
}
|
|
// La Condor in basso, nera, puntata verso il mirino: il calcio di legno dritto in giù (la mano lo tiene così), la canna
|
|
// che gira verso il mirino, il mirino in punta.
|
|
function pistol(ctx, to) {
|
|
const from = { x: 430, y: 330 }, a = Math.atan2(to.y - from.y, to.x - from.x);
|
|
rect(ctx, from.x - 13, from.y + 2, 26, 34, '#1a120c'); rect(ctx, from.x - 12, from.y + 2, 24, 32, '#5a3a22');
|
|
rect(ctx, from.x - 9, from.y + 5, 4, 26, '#7a5232'); rect(ctx, from.x - 2, from.y + 8, 2, 20, '#3e2816');
|
|
ctx.save(); ctx.translate(from.x, from.y); ctx.rotate(a);
|
|
ctx.fillStyle = '#0b0b0e'; ctx.fillRect(-16, -11, 84, 20);
|
|
ctx.fillStyle = '#26252c'; ctx.fillRect(-14, -9, 80, 5);
|
|
ctx.fillStyle = '#4a4955'; ctx.fillRect(-14, -9, 80, 1);
|
|
ctx.fillStyle = '#0b0b0e'; ctx.fillRect(62, -14, 5, 4); ctx.fillRect(66, -5, 6, 10);
|
|
ctx.restore();
|
|
}
|
|
|
|
export function drawCondor(ctx, c, def, bg) {
|
|
ctx.save(); ctx.imageSmoothingEnabled = false;
|
|
if (bg) ctx.drawImage(bg, 0, 0, 640, 360); else { ctx.fillStyle = '#7fb24a'; ctx.fillRect(0, 0, 640, 360); }
|
|
cartone(ctx, c);
|
|
for (const k of c.bottles) if (k.fall < 1) bottle(ctx, k.x + k.fall * 6, BAR + k.fall * 10, k.fall * 1.6);
|
|
for (const k of c.cans) if (k.fall < 1) can(ctx, k.x, BENCH + k.fall * 8, -k.fall * 1.4);
|
|
// Il palloncino rosso legato alla sbarra dell'altalena; scoppiato, resta lo spago e qualche brandello.
|
|
const b = balloon(c);
|
|
ctx.strokeStyle = '#e8e2d0'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(BALLOON.x, BALLOON.knot);
|
|
ctx.quadraticCurveTo(BALLOON.x + (b.x - BALLOON.x) * 0.3 + 3, (BALLOON.knot + b.y) / 2, b.x, c.balloon.popped ? b.y + 14 : b.y + BALLOON.r); ctx.stroke();
|
|
if (!c.balloon.popped) {
|
|
ctx.fillStyle = '#d8283a'; ctx.beginPath(); ctx.ellipse(b.x, b.y, BALLOON.r, BALLOON.r * 1.2, 0, 0, Math.PI * 2); ctx.fill();
|
|
ctx.fillStyle = '#ff8a8a'; ctx.fillRect(Math.round(b.x - 4), Math.round(b.y - 6), 2, 3);
|
|
rect(ctx, b.x - 1, b.y + BALLOON.r * 1.2 - 1, 3, 2, '#a01828');
|
|
} else if (c.balloon.pop < 1) {
|
|
ctx.fillStyle = '#d8283a';
|
|
for (let i = 0; i < 6; i++) { const a = i / 6 * Math.PI * 2, d = 4 + c.balloon.pop * 14; ctx.fillRect(Math.round(b.x + Math.cos(a) * d), Math.round(b.y + Math.sin(a) * d), 2, 2); }
|
|
}
|
|
// La lattina tirata in aria dal Taglia (o che ricade, colpita).
|
|
const v = voloAt(c);
|
|
if (c.volo?.fall) can(ctx, c.volo.fall.x, c.volo.fall.y + VOLO.h / 2, c.visual.time * 14, VOLO.w, VOLO.h, '#2f6fb0');
|
|
else if (v) can(ctx, v.x, v.y + VOLO.h / 2, v.a, VOLO.w, VOLO.h, '#2f6fb0');
|
|
// Il pallino in volo: grande vicino alla pistola, piccolo verso il bersaglio.
|
|
if (c.shot) {
|
|
const k = Math.min(1, c.shot.t / c.shot.time), { from, to } = c.shot;
|
|
pellet(ctx, from.x + (to.x - from.x) * k, from.y + (to.y - from.y) * k, 4 - 2.8 * k);
|
|
}
|
|
const r = reticle(c);
|
|
if (canThrow(c)) {
|
|
// Alla bona: dove arriva il pallino, un puntino giallo sotto il mirino.
|
|
if (c.skill.guide) { const l = landing(c); rect(ctx, l.x - 1, l.y - 1, 3, 3, '#f5d02a'); }
|
|
const out = breathless(c);
|
|
ctx.strokeStyle = out ? '#ff6a4a' : c.holding && c.holdTime >= STILL ? '#b9e5b3' : '#fff8dc'; ctx.lineWidth = 1;
|
|
ctx.beginPath(); ctx.arc(r.x, r.y, 7, 0, Math.PI * 2); ctx.stroke();
|
|
ctx.beginPath(); ctx.moveTo(r.x - 11, r.y); ctx.lineTo(r.x - 3, r.y); ctx.moveTo(r.x + 3, r.y); ctx.lineTo(r.x + 11, r.y);
|
|
ctx.moveTo(r.x, r.y - 11); ctx.lineTo(r.x, r.y - 3); ctx.moveTo(r.x, r.y + 3); ctx.lineTo(r.x, r.y + 11); ctx.stroke();
|
|
}
|
|
pistol(ctx, r);
|
|
// Il fiato trattenuto: la barra si riempie fino a «fermo» (verde), poi si svuota in rosso finché il colpo parte.
|
|
if (canThrow(c) && c.holding) {
|
|
const x = 16, y = 300, h = 50, still = Math.min(1, c.holdTime / STILL), left = c.holdTime > BREATH ? 1 - (c.holdTime - BREATH) / GASP : 1;
|
|
rect(ctx, x - 3, y - 3, 18, h + 6, '#17160fd0');
|
|
const f = Math.round(h * (c.holdTime > BREATH ? left : still));
|
|
rect(ctx, x, y + h - f, 12, f, c.holdTime > BREATH ? '#ff6a4a' : still >= 1 ? '#9be07c' : '#ffd23c');
|
|
text(ctx, 'FIATO', x + 6, y - 9, 6, '#efd49e', 'center');
|
|
}
|
|
// In alto: i punti, quelli del Taglia, i pallini rimasti e cosa è successo.
|
|
rect(ctx, 0, 0, 640, 22, '#17160fe8');
|
|
text(ctx, 'LA CONDOR', 8, 7, 8, '#efd49e');
|
|
text(ctx, { bona: 'alla bona', seria: 'seria', male: 'a fassi male' }[c.level], 8, 16, 7, '#d9ccba');
|
|
text(ctx, `Punti ${c.points}`, 200, 11, 9, c.points > c.taglia ? '#b9e5b3' : '#fff0c8', 'center');
|
|
text(ctx, `Il Taglia ${c.taglia}`, 310, 11, 9, '#9fd0ff', 'center');
|
|
for (let i = 0; i < PELLETS; i++) pellet(ctx, 430 + i * 15, 11, i < c.left ? 3 : 1.2);
|
|
const line = breathless(c) ? 'Il fiato finisce: spara!' : c.line;
|
|
if (line) { rect(ctx, 170, 24, 300, 14, '#17160fd0'); text(ctx, line, 320, 31, 8, breathless(c) || /Niente|fuori|Senza/.test(line) ? '#ffbf9f' : '#9fd0ff', 'center'); }
|
|
if (c.step === 'result') {
|
|
const w = c.result.winner;
|
|
panel(ctx, 170, 140, 300, 76);
|
|
text(ctx, { player: 'BATTUTO IL TAGLIA!', draw: 'PARI COL TAGLIA', cpu: 'HA VINTO IL TAGLIA' }[w], 320, 160, 12, w === 'player' ? '#b9e5b3' : '#efd49e', 'center');
|
|
text(ctx, `Tu ${c.result.points}, il Taglia ${c.result.taglia}`, 320, 182, 8, '#efd49e', 'center');
|
|
text(ctx, 'Torna al campone per uscire.', 320, 200, 8, '#d9ccba', 'center');
|
|
}
|
|
ctx.restore();
|
|
}
|