<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Il Sogno di Dami</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Fredoka+One&display=swap');
body {
margin: 0; padding: 0;
background-color: #000; color: #fff;
font-family: 'Fredoka One', cursive;
overflow: hidden; touch-action: none;
transition: background-color 1.5s ease;
}
#gameCanvas { display: block; margin: 0 auto; }
.overlay-msg {
display: none; position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.95); z-index: 100;
flex-direction: column; align-items: center;
justify-content: center; text-align: center;
padding: 30px; cursor: pointer;
}
.progress-container {
position: absolute; bottom: 30px; left: 50%;
transform: translateX(-50%); width: 70%; height: 8px;
background: rgba(255,255,255,0.1); border-radius: 20px;
}
#progressBar {
width: 0%; height: 100%;
background: #ff00ff; border-radius: 20px;
transition: width 0.3s ease;
}
#pauseBtn {
position: absolute; top: 20px; right: 20px;
width: 50px; height: 50px;
background: rgba(255, 255, 255, 0.2);
border: 2px solid white; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 24px; cursor: pointer; z-index: 50;
transition: transform 0.2s;
}
#pauseBtn:active { transform: scale(0.9); }
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<!-- Pulsante Pausa -->
<div id="pauseBtn">II</div>
<div id="messageOverlay" class="overlay-msg">
<h2 id="dynamicText" class="text-2xl md:text-3xl mb-6 leading-relaxed text-pink-400"></h2>
<div id="subText" class="text-lg text-white/50 mb-10 italic"></div>
<p id="continueHint" class="text-xl animate-pulse">TOCCA PER CONTINUARE</p>
</div>
<div class="absolute inset-0 flex items-center justify-center" id="menuOverlay">
<div class="text-center bg-black/80 p-10 rounded-3xl border-4 border-pink-500/30 backdrop-blur-xl max-w-lg shadow-2xl">
<h1 id="mainTitle" class="text-4xl font-bold mb-6 text-pink-500">IL SOGNO DI DAMI</h1>
<p id="missionText" class="mb-8 text-white/80 text-lg">
Raccogli 250 oggetti per scoprire la verità.<br>
Usa il pulsante in alto per mettere in pausa.
</p>
<button id="startBtn" class="bg-pink-600 hover:bg-pink-500 text-white font-bold py-4 px-12 rounded-full text-2xl transition-all shadow-lg">
INIZIA
</button>
</div>
</div>
<div class="absolute top-6 left-6 pointer-events-none">
<div id="hudText" class="text-2xl">PUNTI: <span id="scoreVal">0</span>/250</div>
</div>
<div class="progress-container">
<div id="progressBar"></div>
</div>
<script type="module">
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreVal = document.getElementById('scoreVal');
const messageOverlay = document.getElementById('messageOverlay');
const dynamicText = document.getElementById('dynamicText');
const subText = document.getElementById('subText');
const hudText = document.getElementById('hudText');
const startBtn = document.getElementById('startBtn');
const progressBar = document.getElementById('progressBar');
const menuOverlay = document.getElementById('menuOverlay');
const pauseBtn = document.getElementById('pauseBtn');
let gameActive = false, paused = false, score = 0, isEnding = false;
let themeColor = '#ff00ff', obstacleColor = '#ff0000', bgColor = '#050505';
let nextMessageScore = 5;
let player = { x: 0, y: 0, targetX: 0, form: 'elephant', scale: 0.55, hitboxRadius: 10 };
let objects = [];
// Audio System
let audioCtx;
const notes = [261.63, 293.66, 329.63, 392.00, 440.00, 523.25];
let noteIndex = 0, nextNoteTime = 0;
function playNote(freqMultiplier = 1) {
if (!audioCtx || (!gameActive && !isEnding) || paused) return;
const now = audioCtx.currentTime;
if (now > nextNoteTime) {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = player.form === 'sunflower' ? 'sine' : 'triangle';
const baseNote = notes[noteIndex % notes.length];
osc.frequency.setValueAtTime(baseNote * (score > 100 ? 2 : 1) * freqMultiplier, now);
gain.gain.setValueAtTime(0.06, now);
gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.5);
osc.connect(gain); gain.connect(audioCtx.destination);
osc.start(); osc.stop(now + 0.5);
noteIndex++;
nextNoteTime = now + 0.35;
}
}
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if(!isEnding) player.y = canvas.height - 120;
}
window.addEventListener('resize', resize);
resize();
const handleMove = (e) => {
if(isEnding) return;
const x = e.touches ? e.touches[0].clientX : e.clientX;
player.targetX = x;
};
window.addEventListener('mousemove', handleMove);
window.addEventListener('touchstart', handleMove);
window.addEventListener('touchmove', handleMove);
startBtn.onclick = () => {
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
score = 0; scoreVal.innerText = 0;
nextMessageScore = 5;
progressBar.style.width = '0%';
objects = []; gameActive = true; paused = false; isEnding = false;
player.form = 'elephant';
player.x = canvas.width / 2; player.targetX = player.x;
resetColors();
menuOverlay.style.display = 'none';
};
pauseBtn.onclick = (e) => {
e.stopPropagation();
if(!gameActive || isEnding) return;
paused = !paused;
if(paused) {
showPauseMessage("PAUSA", "Tocca per riprendere");
pauseBtn.innerText = "▶";
} else {
messageOverlay.style.display = 'none';
pauseBtn.innerText = "II";
}
};
messageOverlay.onclick = () => {
if (score >= 250 && !isEnding) {
startEndingSequence();
} else if (!isEnding) {
paused = false;
pauseBtn.innerText = "II";
messageOverlay.style.display = 'none';
}
};
function resetColors() {
themeColor = '#ff00ff'; obstacleColor = '#ff0000', bgColor = '#050505';
applyColors();
}
function applyColors() {
document.body.style.backgroundColor = bgColor;
hudText.style.color = themeColor;
progressBar.style.background = themeColor;
}
function randomizeColors() {
const hue = Math.floor(Math.random() * 360);
themeColor = `hsl(${hue}, 100%, 65%)`;
obstacleColor = `hsl(${(hue + 160) % 360}, 100%, 50%)`;
bgColor = `hsl(${hue}, 40%, 4%)`;
applyColors();
}
function checkMilestones() {
progressBar.style.width = `${(score / 250) * 100}%`;
if (score % 10 === 0 && score > 0) randomizeColors();
if (score === nextMessageScore && score <= 20) {
showPauseMessage("DAMI VAI A PRENDERE LE PILLOLE", "Un passo alla volta.");
nextMessageScore += 5;
} else if (score === 30) {
showPauseMessage("quante pillole stai prendendoo, ma quelle vere le hai prese?", "Senti la musica? Sta crescendo con te.");
} else if (score === 100) {
player.form = 'cat';
showPauseMessage("Trasformazione!", "L'elefantino è diventato un gattino. Le pillole ora sono noccioline.");
} else if (score === 150) {
showPauseMessage("è stato difficile prendere 150 pillole?", "penso che 3 al giorno siano più semplici");
} else if (score === 200) {
player.form = 'sunflower';
showPauseMessage("Sei un Girasole!", "Il sole è la tua meta. Manca pochissimo.");
} else if (score === 250) {
gameActive = false;
document.getElementById('continueHint').innerText = "GUARDA IL FINALE";
showPauseMessage(
"bravissima, l’elefantino ha preso le sue pillole schivando gli ostacoli ed il gattino le sue noccioline, ed il girasole il suo amato sole, allo stesso modo farai tu, arriverà il momento in cui brillerai con quel vestito, mi manchi un pochino",
"❤️"
);
}
}
function showPauseMessage(txt, sub) {
paused = true;
dynamicText.innerText = txt;
subText.innerText = sub || "";
dynamicText.style.color = themeColor;
messageOverlay.style.display = 'flex';
}
function drawElephant(x, y, s = 0.55, col = themeColor) {
ctx.save(); ctx.translate(x, y); ctx.scale(s, s); ctx.fillStyle = col;
ctx.beginPath(); ctx.ellipse(0, 0, 30, 22, 0, 0, Math.PI * 2); ctx.fill();
ctx.fillRect(-18, 12, 8, 12); ctx.fillRect(8, 12, 8, 12);
ctx.beginPath(); ctx.arc(30, -5, 18, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = "rgba(0,0,0,0.1)"; ctx.beginPath(); ctx.ellipse(22, -5, 10, 16, 0.2, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = col; ctx.lineWidth = 1.5; ctx.stroke();
ctx.fillStyle = col; ctx.beginPath(); ctx.moveTo(42, -5); ctx.quadraticCurveTo(62, -5, 62, 20); ctx.lineTo(54, 20); ctx.quadraticCurveTo(54, 5, 42, 5); ctx.fill();
ctx.fillStyle = "black"; ctx.beginPath(); ctx.arc(38, -8, 2, 0, Math.PI * 2); ctx.fill();
ctx.restore();
}
function drawCat(x, y, s = 0.55) {
ctx.save(); ctx.translate(x, y); ctx.scale(s, s); ctx.fillStyle = '#fdbb2d';
ctx.beginPath(); ctx.arc(0, 0, 25, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.moveTo(-18, -10); ctx.lineTo(-26, -35); ctx.lineTo(-8, -20); ctx.fill();
ctx.beginPath(); ctx.moveTo(18, -10); ctx.lineTo(26, -35); ctx.lineTo(8, -20); ctx.fill();
ctx.fillStyle = 'black'; ctx.beginPath(); ctx.arc(-8, -5, 3, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(8, -5, 3, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = 'pink'; ctx.beginPath(); ctx.arc(0, 2, 2, 0, Math.PI * 2); ctx.fill();
ctx.restore();
}
function drawSunflower(x, y, s = 0.55) {
ctx.save(); ctx.translate(x, y); ctx.scale(s, s); ctx.fillStyle = '#FFD700';
for(let i=0; i<10; i++) { ctx.rotate(Math.PI/5); ctx.beginPath(); ctx.ellipse(22, 0, 14, 7, 0, 0, Math.PI*2); ctx.fill(); }
ctx.fillStyle = '#4B2C20'; ctx.beginPath(); ctx.arc(0, 0, 13, 0, Math.PI*2); ctx.fill();
ctx.restore();
}
let endTimer = 0;
function startEndingSequence() {
messageOverlay.style.display = 'none';
pauseBtn.style.display = 'none';
isEnding = true;
endTimer = 0;
document.body.style.backgroundColor = "#000";
}
function drawEnding() {
endTimer += 1.5;
const midX = canvas.width / 2;
const midY = canvas.height / 2;
if (endTimer < 300) {
ctx.strokeStyle = "rgba(255,255,255,0.1)"; ctx.lineWidth = 40;
ctx.beginPath(); ctx.moveTo(midX - 100, canvas.height); ctx.lineTo(midX + 100, -150); ctx.stroke();
const progress = endTimer / 300;
const charY = canvas.height - (canvas.height + 300) * progress;
drawElephant(midX - 50, charY, 0.4, "#fff");
drawCat(midX, charY - 50, 0.4);
drawSunflower(midX + 50, charY + 20, 0.4);
ctx.fillStyle = "#FFD700"; ctx.fillRect(midX - 35, -50, 70, 110);
}
else if (endTimer < 1000) {
const fade = Math.min(1, (endTimer - 300) / 100);
ctx.globalAlpha = fade;
let zoom = 1 - (endTimer - 300) * 0.0005;
if(zoom < 0.6) zoom = 0.6;
ctx.save();
ctx.translate(midX, midY);
ctx.scale(zoom, zoom);
let girlX = -60, girlY = 25;
let boyX = 25, boyY = 20;
let swingEmpty = false;
if (endTimer > 600) {
swingEmpty = true;
let walkProgress = Math.min(1, (endTimer - 600) / 100);
girlX = -60 + (boyX - -60 - 40) * walkProgress;
girlY = 25 + (35 - 25) * walkProgress;
}
let walkAwayX = 0;
if (endTimer > 750) {
walkAwayX = (endTimer - 750) * 1.5;
girlX += walkAwayX;
boyX += walkAwayX;
}
ctx.strokeStyle = "rgba(255,255,255,0.5)"; ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(-120, -200); ctx.lineTo(-120, 80); ctx.moveTo(40, -200); ctx.lineTo(40, 80); ctx.stroke();
ctx.beginPath(); ctx.moveTo(-140, -200); ctx.lineTo(60, -200); ctx.stroke();
ctx.strokeStyle = "white";
ctx.beginPath(); ctx.moveTo(-90, -200); ctx.lineTo(-90, 50); ctx.moveTo(-30, -200); ctx.lineTo(-30, 50); ctx.stroke();
if (!swingEmpty) {
ctx.strokeRect(-100, 50, 80, 6);
} else {
ctx.save(); ctx.translate(-60, 50); ctx.rotate(Math.sin(endTimer * 0.05) * 0.1);
ctx.strokeRect(-40, 0, 80, 6); ctx.restore();
}
ctx.save(); ctx.translate(girlX, girlY);
ctx.fillStyle = "#FFC0CB";
ctx.beginPath(); ctx.arc(0, 0, 14, 0, Math.PI*2); ctx.fill();
ctx.fillRect(-8, 15, 16, 25);
ctx.restore();
ctx.save(); ctx.translate(boyX, boyY);
ctx.fillStyle = "#87CEEB";
ctx.beginPath(); ctx.arc(0, 0, 14, 0, Math.PI*2); ctx.fill();
ctx.fillRect(-8, 15, 16, 40);
ctx.fillStyle = "#D2691E"; ctx.fillRect(8, 30, 18, 14);
ctx.restore();
ctx.restore();
ctx.globalAlpha = 1;
if (endTimer > 900) {
const blackFade = Math.min(1, (endTimer - 900) / 100);
ctx.fillStyle = `rgba(0,0,0,${blackFade})`;
ctx.fillRect(0,0,canvas.width, canvas.height);
}
} else {
ctx.fillStyle = "black";
ctx.fillRect(0,0,canvas.width, canvas.height);
ctx.fillStyle = "#333";
ctx.textAlign = "center";
ctx.font = "16px Fredoka One";
ctx.fillText("Fine.", midX, midY);
}
}
function update() {
if (isEnding) return;
if (!gameActive || paused) return;
player.x += (player.targetX - player.x) * 0.12;
playNote();
let currentSpeed = 3.5 + (score / 75);
if (Math.random() < 0.025) {
objects.push({ x: Math.random() * (canvas.width - 30), y: -50, type: 'collectible', speed: currentSpeed * 0.85 });
}
if (Math.random() < 0.02 + (score/2500)) {
objects.push({ x: Math.random() * (canvas.width - 40), y: -50, type: 'enemy', size: 30, speed: currentSpeed });
}
for (let i = objects.length - 1; i >= 0; i--) {
let o = objects[i]; o.y += o.speed;
const dist = Math.sqrt((player.x - (o.x+15))**2 + (player.y - (o.y+15))**2);
if (o.type === 'collectible') {
if (dist < 35) {
score++; scoreVal.innerText = score; objects.splice(i, 1);
checkMilestones();
}
} else {
if (dist < player.hitboxRadius + 12) {
gameActive = false;
menuOverlay.style.display = 'flex';
mainTitle.innerText = "OPS!";
missionText.innerText = `Dami, sei arrivata a ${score} punti. Riprova!`;
}
}
if (o.y > canvas.height + 50) objects.splice(i, 1);
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if(isEnding) { drawEnding(); return; }
objects.forEach(o => {
if (o.type === 'collectible') {
if(player.form === 'elephant') {
ctx.save(); ctx.translate(o.x+15, o.y+15); ctx.rotate(Math.PI/4);
ctx.fillStyle = 'white'; ctx.beginPath(); ctx.roundRect(-8,-4,8,8,6); ctx.fill();
ctx.fillStyle = themeColor; ctx.beginPath(); ctx.roundRect(0,-4,8,8,6); ctx.fill(); ctx.restore();
} else if(player.form === 'cat') {
ctx.fillStyle = '#d2b48c'; ctx.beginPath(); ctx.ellipse(o.x+15, o.y+15, 7, 10, Math.PI/4, 0, Math.PI*2); ctx.fill();
} else {
ctx.fillStyle = '#FFEB3B'; ctx.beginPath(); ctx.arc(o.x+15, o.y+15, 9, 0, Math.PI*2); ctx.fill();
}
} else {
ctx.fillStyle = obstacleColor; ctx.fillRect(o.x, o.y, 30, 30);
}
});
if(player.form === 'elephant') drawElephant(player.x, player.y);
else if(player.form === 'cat') drawCat(player.x, player.y);
else drawSunflower(player.x, player.y);
}
function loop() { update(); draw(); requestAnimationFrame(loop); }
loop();
</script>
</body>
</html>