|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Sort 'Em Silly: Critter Chaos</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
<style> |
|
@keyframes pop { |
|
0% { transform: scale(1); } |
|
50% { transform: scale(1.2); } |
|
100% { transform: scale(1); } |
|
} |
|
|
|
@keyframes shake { |
|
0% { transform: translateX(0); } |
|
25% { transform: translateX(-5px); } |
|
50% { transform: translateX(5px); } |
|
75% { transform: translateX(-5px); } |
|
100% { transform: translateX(0); } |
|
} |
|
|
|
@keyframes pulse { |
|
0% { opacity: 1; } |
|
50% { opacity: 0.7; } |
|
100% { opacity: 1; } |
|
} |
|
|
|
@keyframes float { |
|
0% { transform: translateY(0px); } |
|
50% { transform: translateY(-10px); } |
|
100% { transform: translateY(0px); } |
|
} |
|
|
|
@keyframes flash { |
|
0% { background-color: transparent; } |
|
50% { background-color: rgba(255, 255, 255, 0.5); } |
|
100% { background-color: transparent; } |
|
} |
|
|
|
@keyframes wobble { |
|
0%, 100% { transform: rotate(0deg); } |
|
25% { transform: rotate(5deg); } |
|
75% { transform: rotate(-5deg); } |
|
} |
|
|
|
@keyframes rainbow { |
|
0% { filter: hue-rotate(0deg); } |
|
100% { filter: hue-rotate(360deg); } |
|
} |
|
|
|
.pop-animation { |
|
animation: pop 0.3s ease-out; |
|
} |
|
|
|
.shake-animation { |
|
animation: shake 0.3s ease-in-out; |
|
} |
|
|
|
.pulse-animation { |
|
animation: pulse 1s infinite; |
|
} |
|
|
|
.float-animation { |
|
animation: float 2s ease-in-out infinite; |
|
} |
|
|
|
.flash-animation { |
|
animation: flash 0.5s ease-out; |
|
} |
|
|
|
.wobble-animation { |
|
animation: wobble 0.5s ease-in-out infinite; |
|
} |
|
|
|
.rainbow-animation { |
|
animation: rainbow 3s linear infinite; |
|
} |
|
|
|
.critter { |
|
transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); |
|
cursor: grab; |
|
user-select: none; |
|
will-change: transform; |
|
position: absolute; |
|
border-radius: 50%; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 2rem; |
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); |
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); |
|
z-index: 5; |
|
} |
|
|
|
.critter::before { |
|
content: ''; |
|
position: absolute; |
|
width: 100%; |
|
height: 100%; |
|
border-radius: 50%; |
|
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0) 70%); |
|
pointer-events: none; |
|
} |
|
|
|
.critter.dragging { |
|
transform: scale(1.3) rotate(5deg); |
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.4); |
|
cursor: grabbing; |
|
z-index: 10; |
|
filter: brightness(1.1); |
|
} |
|
|
|
.bin { |
|
transition: all 0.3s ease; |
|
border-radius: 20px; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
justify-content: center; |
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.bin::before { |
|
content: ''; |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
right: 0; |
|
height: 10px; |
|
background: rgba(255,255,255,0.4); |
|
} |
|
|
|
.bin.highlight { |
|
transform: scale(1.1); |
|
box-shadow: 0 0 30px rgba(255, 255, 255, 0.6); |
|
filter: brightness(1.2); |
|
} |
|
|
|
.timer-critical { |
|
color: #ef4444; |
|
font-weight: bold; |
|
animation: pulse 0.5s infinite; |
|
} |
|
|
|
.source-area { |
|
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%); |
|
border: 2px dashed rgba(255, 255, 255, 0.2); |
|
border-radius: 20px; |
|
position: relative; |
|
height: 400px; |
|
width: 100%; |
|
overflow: hidden; |
|
} |
|
|
|
.source-area::after { |
|
content: ''; |
|
position: absolute; |
|
inset: 0; |
|
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); |
|
pointer-events: none; |
|
} |
|
|
|
.bins-area { |
|
position: absolute; |
|
bottom: 20px; |
|
left: 0; |
|
right: 0; |
|
display: flex; |
|
justify-content: center; |
|
gap: 30px; |
|
padding: 15px; |
|
} |
|
|
|
.win-title { |
|
text-shadow: 0 0 15px rgba(255, 255, 255, 0.8); |
|
background: linear-gradient(45deg, #f6d365, #fda085, #ff9a9e, #fbc2eb, #a6c1ee, #f6d365); |
|
background-size: 400% 400%; |
|
-webkit-background-clip: text; |
|
background-clip: text; |
|
color: transparent; |
|
animation: gradient 5s ease infinite; |
|
} |
|
|
|
@keyframes gradient { |
|
0% { background-position: 0% 50%; } |
|
50% { background-position: 100% 50%; } |
|
100% { background-position: 0% 50%; } |
|
} |
|
|
|
.lose-title { |
|
text-shadow: 0 0 15px rgba(239, 68, 68, 0.8); |
|
} |
|
|
|
.particle { |
|
position: absolute; |
|
border-radius: 50%; |
|
pointer-events: none; |
|
z-index: 5; |
|
mix-blend-mode: screen; |
|
} |
|
|
|
.glow-text { |
|
text-shadow: 0 0 12px currentColor; |
|
} |
|
|
|
.btn-glow { |
|
box-shadow: 0 0 20px 5px rgba(255, 255, 255, 0.4); |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.btn-glow:hover { |
|
box-shadow: 0 0 25px 8px rgba(255, 255, 255, 0.6); |
|
} |
|
|
|
.game-area { |
|
position: relative; |
|
height: 500px; |
|
width: 100%; |
|
max-width: 900px; |
|
margin: 0 auto; |
|
background: linear-gradient(135deg, rgba(99, 102, 241, 0.3) 0%, rgba(168, 85, 247, 0.3) 100%); |
|
border-radius: 20px; |
|
overflow: hidden; |
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); |
|
} |
|
|
|
.game-area::before { |
|
content: ''; |
|
position: absolute; |
|
inset: 0; |
|
background: url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm-6 60c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm29 22c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-26-22c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3z' fill='%23ffffff' fill-opacity='0.05' fill-rule='evenodd'/%3E%3C/svg%3E"); |
|
pointer-events: none; |
|
} |
|
|
|
.game-container { |
|
width: 100%; |
|
max-width: 1000px; |
|
margin: 0 auto; |
|
padding: 20px; |
|
} |
|
|
|
.start-screen { |
|
background: linear-gradient(135deg, rgba(99, 102, 241, 0.8) 0%, rgba(168, 85, 247, 0.8) 100%); |
|
border-radius: 20px; |
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); |
|
backdrop-filter: blur(10px); |
|
border: 2px solid rgba(255, 255, 255, 0.2); |
|
} |
|
|
|
.game-header { |
|
background: linear-gradient(135deg, rgba(99, 102, 241, 0.7) 0%, rgba(168, 85, 247, 0.7) 100%); |
|
backdrop-filter: blur(5px); |
|
border-radius: 15px; |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); |
|
} |
|
|
|
.score-bubble { |
|
background: rgba(0, 0, 0, 0.3); |
|
border-radius: 20px; |
|
padding: 5px 15px; |
|
backdrop-filter: blur(5px); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
.timer-bubble { |
|
background: rgba(0, 0, 0, 0.3); |
|
border-radius: 20px; |
|
padding: 5px 15px; |
|
backdrop-filter: blur(5px); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
.level-bubble { |
|
background: rgba(0, 0, 0, 0.3); |
|
border-radius: 20px; |
|
padding: 5px 15px; |
|
backdrop-filter: blur(5px); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
.end-screen { |
|
backdrop-filter: blur(10px); |
|
border-radius: 20px; |
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); |
|
border: 2px solid rgba(255, 255, 255, 0.2); |
|
} |
|
|
|
.win-screen { |
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.8) 0%, rgba(5, 150, 105, 0.8) 100%); |
|
} |
|
|
|
.lose-screen { |
|
background: linear-gradient(135deg, rgba(239, 68, 68, 0.8) 0%, rgba(220, 38, 38, 0.8) 100%); |
|
} |
|
|
|
.critter-shadow { |
|
position: absolute; |
|
bottom: -10px; |
|
left: 50%; |
|
transform: translateX(-50%); |
|
width: 60%; |
|
height: 15px; |
|
background: rgba(0, 0, 0, 0.2); |
|
border-radius: 50%; |
|
filter: blur(5px); |
|
} |
|
|
|
.bin-shadow { |
|
position: absolute; |
|
bottom: -10px; |
|
left: 50%; |
|
transform: translateX(-50%); |
|
width: 80%; |
|
height: 15px; |
|
background: rgba(0, 0, 0, 0.3); |
|
border-radius: 50%; |
|
filter: blur(8px); |
|
} |
|
|
|
|
|
@media (max-width: 768px) { |
|
.game-area { |
|
height: 400px; |
|
} |
|
|
|
.source-area { |
|
height: 300px; |
|
} |
|
|
|
.critter { |
|
width: 60px !important; |
|
height: 60px !important; |
|
font-size: 1.5rem !important; |
|
} |
|
|
|
.bin { |
|
width: 70px !important; |
|
height: 70px !important; |
|
} |
|
|
|
.bins-area { |
|
gap: 15px; |
|
} |
|
} |
|
|
|
@media (max-width: 480px) { |
|
.game-area { |
|
height: 350px; |
|
} |
|
|
|
.source-area { |
|
height: 250px; |
|
} |
|
|
|
.critter { |
|
width: 50px !important; |
|
height: 50px !important; |
|
font-size: 1.2rem !important; |
|
} |
|
|
|
.bin { |
|
width: 60px !important; |
|
height: 60px !important; |
|
font-size: 1.5rem !important; |
|
} |
|
|
|
.bins-area { |
|
gap: 10px; |
|
} |
|
|
|
.game-header { |
|
font-size: 0.9rem; |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gradient-to-br from-indigo-900 via-purple-900 to-violet-900 text-white font-sans min-h-screen flex flex-col overflow-hidden"> |
|
|
|
<div id="background-particles" class="fixed inset-0 pointer-events-none"></div> |
|
|
|
|
|
<div id="game-container" class="game-container flex-1 flex flex-col items-center justify-center p-4 relative z-10"> |
|
|
|
<div id="start-screen" class="start-screen flex flex-col items-center justify-center p-8 w-full max-w-md text-center"> |
|
<div class="text-6xl mb-4 float-animation">🎮</div> |
|
<h1 class="text-5xl font-bold mb-2 text-yellow-300 glow-text">Sort 'Em Silly</h1> |
|
<h2 class="text-3xl mb-6 text-pink-300 glow-text">Critter Chaos</h2> |
|
<div class="text-lg mb-8 bg-black/20 p-4 rounded-lg border border-white/20 backdrop-blur-sm"> |
|
<p class="mb-2 rainbow-animation">DEMO VERSION</p> |
|
<p>Drag critters to matching bins before time runs out!</p> |
|
</div> |
|
<button id="start-button" class="bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white font-bold py-3 px-8 rounded-full text-lg transition-all transform hover:scale-105 btn-glow"> |
|
<i class="fas fa-play mr-2"></i> Start Demo |
|
</button> |
|
</div> |
|
|
|
|
|
<div id="game-screen" class="hidden w-full h-full flex flex-col"> |
|
|
|
<div class="game-header flex justify-between items-center mb-4 p-3"> |
|
<div class="level-bubble text-lg font-bold flex items-center"> |
|
<i class="fas fa-flag mr-2 text-yellow-300"></i> |
|
<span>Level <span id="level-display">1</span></span> |
|
</div> |
|
<div id="timer" class="timer-bubble text-lg font-mono px-4 py-1 flex items-center"> |
|
<i class="fas fa-clock mr-2"></i> |
|
<span>0:<span id="timer-value">30</span></span> |
|
</div> |
|
<div id="score-display" class="score-bubble text-lg flex items-center"> |
|
<i class="fas fa-paw mr-2 text-pink-300"></i> |
|
<span id="sorted-count">0</span>/<span id="total-critters">0</span> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="game-area"> |
|
|
|
<div id="source-area" class="source-area"> |
|
|
|
</div> |
|
|
|
|
|
<div id="bins-area" class="bins-area"> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="win-screen" class="end-screen win-screen hidden absolute inset-0 flex flex-col items-center justify-center p-8 text-center"> |
|
<div class="text-8xl mb-6 float-animation">🎉</div> |
|
<h2 class="text-5xl font-bold mb-4 win-title">LEVEL COMPLETE!</h2> |
|
<div class="text-xl mb-8 bg-black/20 p-4 rounded-lg border border-white/20 backdrop-blur-sm"> |
|
<p>Great sorting! Ready for more chaos?</p> |
|
</div> |
|
<div class="flex gap-4 flex-wrap justify-center"> |
|
<button id="next-level-button" class="bg-gradient-to-r from-yellow-500 to-yellow-600 hover:from-yellow-600 hover:to-yellow-700 text-white font-bold py-3 px-8 rounded-full text-lg transition-all transform hover:scale-105 btn-glow"> |
|
<i class="fas fa-arrow-right mr-2"></i> Next Level |
|
</button> |
|
<button id="main-menu-button" class="bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-bold py-3 px-8 rounded-full text-lg transition-all transform hover:scale-105 btn-glow"> |
|
<i class="fas fa-home mr-2"></i> Main Menu |
|
</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="lose-screen" class="end-screen lose-screen hidden absolute inset-0 flex flex-col items-center justify-center p-8 text-center"> |
|
<div class="text-8xl mb-6 wobble-animation">😵</div> |
|
<h2 class="text-5xl font-bold mb-4 lose-title glow-text">TIME'S UP!</h2> |
|
<div class="text-xl mb-8 bg-black/20 p-4 rounded-lg border border-white/20 backdrop-blur-sm"> |
|
<p>Those critters got the best of you...</p> |
|
</div> |
|
<div class="flex gap-4 flex-wrap justify-center"> |
|
<button id="retry-button" class="bg-gradient-to-r from-yellow-500 to-yellow-600 hover:from-yellow-600 hover:to-yellow-700 text-white font-bold py-3 px-8 rounded-full text-lg transition-all transform hover:scale-105 btn-glow"> |
|
<i class="fas fa-redo mr-2"></i> Retry Level |
|
</button> |
|
<button id="main-menu-button-2" class="bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-bold py-3 px-8 rounded-full text-lg transition-all transform hover:scale-105 btn-glow"> |
|
<i class="fas fa-home mr-2"></i> Main Menu |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<audio id="success-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-positive-interface-beep-221.mp3"></audio> |
|
<audio id="fail-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-wrong-answer-fail-notification-946.mp3"></audio> |
|
<audio id="win-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3"></audio> |
|
<audio id="lose-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3"></audio> |
|
<audio id="background-music" loop src="https://assets.mixkit.co/music/preview/mixkit-game-show-suspense-waiting-668.mp3"></audio> |
|
|
|
<script> |
|
|
|
const gameState = { |
|
currentLevel: 1, |
|
timer: 30, |
|
timerInterval: null, |
|
sortedCount: 0, |
|
totalCritters: 0, |
|
isPlaying: false, |
|
levels: [ |
|
{ |
|
time: 30, |
|
critters: [ |
|
{ type: 'red', x: 15, y: 15 }, |
|
{ type: 'blue', x: 85, y: 15 }, |
|
{ type: 'green', x: 50, y: 30 } |
|
] |
|
}, |
|
{ |
|
time: 25, |
|
critters: [ |
|
{ type: 'red', x: 10, y: 10 }, |
|
{ type: 'blue', x: 90, y: 10 }, |
|
{ type: 'green', x: 50, y: 20 }, |
|
{ type: 'yellow', x: 30, y: 40 }, |
|
{ type: 'red', x: 70, y: 40 } |
|
] |
|
}, |
|
{ |
|
time: 20, |
|
critters: [ |
|
{ type: 'red', x: 5, y: 5 }, |
|
{ type: 'blue', x: 25, y: 15 }, |
|
{ type: 'green', x: 75, y: 15 }, |
|
{ type: 'yellow', x: 95, y: 5 }, |
|
{ type: 'red', x: 15, y: 40 }, |
|
{ type: 'blue', x: 50, y: 50 }, |
|
{ type: 'green', x: 85, y: 40 } |
|
] |
|
} |
|
], |
|
critterTypes: { |
|
red: { emoji: '🐙', color: 'bg-red-500', name: 'Octopus' }, |
|
blue: { emoji: '🐬', color: 'bg-blue-500', name: 'Dolphin' }, |
|
green: { emoji: '🐸', color: 'bg-green-500', name: 'Frog' }, |
|
yellow: { emoji: '🦆', color: 'bg-yellow-400', name: 'Duck' } |
|
} |
|
}; |
|
|
|
|
|
const elements = { |
|
startScreen: document.getElementById('start-screen'), |
|
gameScreen: document.getElementById('game-screen'), |
|
winScreen: document.getElementById('win-screen'), |
|
loseScreen: document.getElementById('lose-screen'), |
|
levelDisplay: document.getElementById('level-display'), |
|
timerDisplay: document.getElementById('timer'), |
|
timerValue: document.getElementById('timer-value'), |
|
sortedCount: document.getElementById('sorted-count'), |
|
totalCritters: document.getElementById('total-critters'), |
|
binsArea: document.getElementById('bins-area'), |
|
sourceArea: document.getElementById('source-area'), |
|
startButton: document.getElementById('start-button'), |
|
nextLevelButton: document.getElementById('next-level-button'), |
|
mainMenuButton: document.getElementById('main-menu-button'), |
|
mainMenuButton2: document.getElementById('main-menu-button-2'), |
|
retryButton: document.getElementById('retry-button'), |
|
successSound: document.getElementById('success-sound'), |
|
failSound: document.getElementById('fail-sound'), |
|
winSound: document.getElementById('win-sound'), |
|
loseSound: document.getElementById('lose-sound'), |
|
backgroundMusic: document.getElementById('background-music'), |
|
backgroundParticles: document.getElementById('background-particles') |
|
}; |
|
|
|
|
|
function initGame() { |
|
|
|
elements.startButton.addEventListener('click', startGame); |
|
elements.nextLevelButton.addEventListener('click', nextLevel); |
|
elements.mainMenuButton.addEventListener('click', goToMainMenu); |
|
elements.mainMenuButton2.addEventListener('click', goToMainMenu); |
|
elements.retryButton.addEventListener('click', restartGame); |
|
|
|
|
|
elements.successSound.load(); |
|
elements.failSound.load(); |
|
elements.winSound.load(); |
|
elements.loseSound.load(); |
|
elements.backgroundMusic.volume = 0.3; |
|
|
|
|
|
createBackgroundParticles(); |
|
} |
|
|
|
|
|
function createBackgroundParticles() { |
|
const particleCount = 30; |
|
const colors = ['rgba(255,255,255,0.1)', 'rgba(255,255,255,0.05)', 'rgba(255,255,255,0.02)']; |
|
|
|
for (let i = 0; i < particleCount; i++) { |
|
const particle = document.createElement('div'); |
|
particle.className = 'particle absolute rounded-full'; |
|
|
|
const size = Math.random() * 10 + 5; |
|
const color = colors[Math.floor(Math.random() * colors.length)]; |
|
|
|
particle.style.width = `${size}px`; |
|
particle.style.height = `${size}px`; |
|
particle.style.left = `${Math.random() * 100}%`; |
|
particle.style.top = `${Math.random() * 100}%`; |
|
particle.style.backgroundColor = color; |
|
|
|
|
|
const duration = Math.random() * 20 + 10; |
|
const delay = Math.random() * 5; |
|
const xMovement = (Math.random() - 0.5) * 100; |
|
const yMovement = (Math.random() - 0.5) * 100; |
|
|
|
particle.style.animation = `float ${duration}s ease-in-out ${delay}s infinite`; |
|
particle.style.transform = `translate(${xMovement}px, ${yMovement}px)`; |
|
|
|
elements.backgroundParticles.appendChild(particle); |
|
} |
|
} |
|
|
|
|
|
function startGame() { |
|
elements.startScreen.classList.add('hidden'); |
|
elements.gameScreen.classList.remove('hidden'); |
|
elements.backgroundMusic.play(); |
|
loadLevel(gameState.currentLevel); |
|
} |
|
|
|
|
|
function loadLevel(levelNum) { |
|
|
|
clearInterval(gameState.timerInterval); |
|
gameState.sortedCount = 0; |
|
|
|
|
|
const levelIndex = levelNum - 1; |
|
const level = gameState.levels[levelIndex]; |
|
gameState.timer = level.time; |
|
gameState.totalCritters = level.critters.length; |
|
|
|
|
|
elements.levelDisplay.textContent = levelNum; |
|
elements.timerValue.textContent = gameState.timer.toString().padStart(2, '0'); |
|
elements.sortedCount.textContent = gameState.sortedCount; |
|
elements.totalCritters.textContent = gameState.totalCritters; |
|
elements.timerDisplay.classList.remove('timer-critical', 'pulse-animation'); |
|
|
|
|
|
elements.binsArea.innerHTML = ''; |
|
elements.sourceArea.innerHTML = ''; |
|
|
|
|
|
const critterTypesInLevel = new Set(level.critters.map(c => c.type)); |
|
|
|
critterTypesInLevel.forEach(type => { |
|
const bin = createBin(type); |
|
elements.binsArea.appendChild(bin); |
|
}); |
|
|
|
|
|
level.critters.forEach((critter, index) => { |
|
const critterElement = createCritter(critter.type, critter.x, critter.y, index); |
|
elements.sourceArea.appendChild(critterElement); |
|
}); |
|
|
|
|
|
gameState.timerInterval = setInterval(updateTimer, 1000); |
|
gameState.isPlaying = true; |
|
} |
|
|
|
|
|
function createCritter(type, xPercent, yPercent, index) { |
|
const critterData = gameState.critterTypes[type]; |
|
const critter = document.createElement('div'); |
|
|
|
critter.className = `critter ${critterData.color}`; |
|
critter.style.left = `${xPercent}%`; |
|
critter.style.top = `${yPercent}%`; |
|
critter.style.width = '80px'; |
|
critter.style.height = '80px'; |
|
critter.dataset.type = type; |
|
critter.dataset.index = index; |
|
|
|
critter.textContent = critterData.emoji; |
|
|
|
|
|
const shadow = document.createElement('div'); |
|
shadow.className = 'critter-shadow'; |
|
critter.appendChild(shadow); |
|
|
|
|
|
critter.addEventListener('mousedown', startDrag); |
|
critter.addEventListener('touchstart', startDrag, { passive: false }); |
|
|
|
|
|
const duration = Math.random() * 3 + 2; |
|
const delay = Math.random() * 2; |
|
critter.style.animation = `float ${duration}s ease-in-out ${delay}s infinite`; |
|
|
|
return critter; |
|
} |
|
|
|
|
|
function createBin(type) { |
|
const critterData = gameState.critterTypes[type]; |
|
const bin = document.createElement('div'); |
|
|
|
bin.className = `bin ${critterData.color}`; |
|
bin.style.width = '100px'; |
|
bin.style.height = '100px'; |
|
bin.dataset.type = type; |
|
|
|
const emoji = document.createElement('div'); |
|
emoji.className = 'text-4xl mb-1'; |
|
emoji.textContent = critterData.emoji; |
|
bin.appendChild(emoji); |
|
|
|
const label = document.createElement('div'); |
|
label.className = 'text-xs font-bold text-white'; |
|
label.textContent = critterData.name; |
|
bin.appendChild(label); |
|
|
|
|
|
const shadow = document.createElement('div'); |
|
shadow.className = 'bin-shadow'; |
|
bin.appendChild(shadow); |
|
|
|
return bin; |
|
} |
|
|
|
|
|
function createParticles(x, y, color, count = 15) { |
|
for (let i = 0; i < count; i++) { |
|
const particle = document.createElement('div'); |
|
particle.className = 'particle'; |
|
particle.style.backgroundColor = color; |
|
particle.style.width = `${Math.random() * 12 + 6}px`; |
|
particle.style.height = particle.style.width; |
|
particle.style.left = `${x}px`; |
|
particle.style.top = `${y}px`; |
|
|
|
const angle = Math.random() * Math.PI * 2; |
|
const velocity = Math.random() * 8 + 3; |
|
const lifetime = Math.random() * 1000 + 500; |
|
|
|
document.getElementById('game-screen').appendChild(particle); |
|
|
|
const startTime = Date.now(); |
|
|
|
function updateParticle() { |
|
const elapsed = Date.now() - startTime; |
|
const progress = elapsed / lifetime; |
|
|
|
if (progress >= 1) { |
|
particle.remove(); |
|
return; |
|
} |
|
|
|
const distance = velocity * elapsed / 1000; |
|
particle.style.transform = `translate(${Math.cos(angle) * distance}px, ${Math.sin(angle) * distance}px)`; |
|
particle.style.opacity = 1 - progress; |
|
|
|
requestAnimationFrame(updateParticle); |
|
} |
|
|
|
requestAnimationFrame(updateParticle); |
|
} |
|
} |
|
|
|
|
|
let draggedCritter = null; |
|
let startX, startY; |
|
let startLeft, startTop; |
|
|
|
function startDrag(e) { |
|
e.preventDefault(); |
|
|
|
if (!gameState.isPlaying) return; |
|
|
|
draggedCritter = e.target.closest('.critter'); |
|
if (!draggedCritter) return; |
|
|
|
|
|
const rect = draggedCritter.getBoundingClientRect(); |
|
startX = e.clientX || e.touches[0].clientX; |
|
startY = e.clientY || e.touches[0].clientY; |
|
startLeft = parseFloat(draggedCritter.style.left); |
|
startTop = parseFloat(draggedCritter.style.top); |
|
|
|
|
|
draggedCritter.classList.add('dragging'); |
|
draggedCritter.style.animation = 'none'; |
|
|
|
|
|
document.addEventListener('mousemove', dragCritter); |
|
document.addEventListener('touchmove', dragCritter, { passive: false }); |
|
document.addEventListener('mouseup', endDrag); |
|
document.addEventListener('touchend', endDrag); |
|
|
|
|
|
const bins = document.querySelectorAll('.bin'); |
|
bins.forEach(bin => { |
|
if (bin.dataset.type === draggedCritter.dataset.type) { |
|
bin.classList.add('highlight'); |
|
} |
|
}); |
|
} |
|
|
|
function dragCritter(e) { |
|
if (!draggedCritter) return; |
|
e.preventDefault(); |
|
|
|
const clientX = e.clientX || e.touches[0].clientX; |
|
const clientY = e.clientY || e.touches[0].clientY; |
|
|
|
const dx = clientX - startX; |
|
const dy = clientY - startY; |
|
|
|
|
|
const gameArea = elements.sourceArea.getBoundingClientRect(); |
|
const percentX = (dx / gameArea.width) * 100; |
|
const percentY = (dy / gameArea.height) * 100; |
|
|
|
draggedCritter.style.left = `${startLeft + percentX}%`; |
|
draggedCritter.style.top = `${startTop + percentY}%`; |
|
} |
|
|
|
function endDrag(e) { |
|
if (!draggedCritter) return; |
|
|
|
|
|
document.removeEventListener('mousemove', dragCritter); |
|
document.removeEventListener('touchmove', dragCritter); |
|
document.removeEventListener('mouseup', endDrag); |
|
document.removeEventListener('touchend', endDrag); |
|
|
|
|
|
draggedCritter.classList.remove('dragging'); |
|
|
|
|
|
const bins = document.querySelectorAll('.bin'); |
|
bins.forEach(bin => bin.classList.remove('highlight')); |
|
|
|
|
|
const binElements = document.querySelectorAll('.bin'); |
|
let droppedOnBin = false; |
|
|
|
const critterRect = draggedCritter.getBoundingClientRect(); |
|
const critterCenter = { |
|
x: critterRect.left + critterRect.width / 2, |
|
y: critterRect.top + critterRect.height / 2 |
|
}; |
|
|
|
for (const bin of binElements) { |
|
const binRect = bin.getBoundingClientRect(); |
|
|
|
if ( |
|
critterCenter.x >= binRect.left && |
|
critterCenter.x <= binRect.right && |
|
critterCenter.y >= binRect.top && |
|
critterCenter.y <= binRect.bottom |
|
) { |
|
droppedOnBin = true; |
|
|
|
if (bin.dataset.type === draggedCritter.dataset.type) { |
|
|
|
elements.successSound.play(); |
|
bin.classList.add('pop-animation'); |
|
bin.classList.add('flash-animation'); |
|
|
|
|
|
const binRect = bin.getBoundingClientRect(); |
|
createParticles( |
|
binRect.left + binRect.width / 2, |
|
binRect.top + binRect.height / 2, |
|
getComputedStyle(bin).backgroundColor, |
|
20 |
|
); |
|
|
|
setTimeout(() => { |
|
bin.classList.remove('pop-animation', 'flash-animation'); |
|
}, 300); |
|
|
|
|
|
draggedCritter.remove(); |
|
|
|
|
|
gameState.sortedCount++; |
|
elements.sortedCount.textContent = gameState.sortedCount; |
|
|
|
|
|
if (gameState.sortedCount === gameState.totalCritters) { |
|
winGame(); |
|
} |
|
} else { |
|
|
|
elements.failSound.play(); |
|
draggedCritter.classList.add('shake-animation'); |
|
|
|
setTimeout(() => { |
|
draggedCritter.classList.remove('shake-animation'); |
|
}, 300); |
|
|
|
|
|
draggedCritter.style.left = `${startLeft}%`; |
|
draggedCritter.style.top = `${startTop}%`; |
|
} |
|
|
|
break; |
|
} |
|
} |
|
|
|
if (!droppedOnBin) { |
|
|
|
draggedCritter.style.left = `${startLeft}%`; |
|
draggedCritter.style.top = `${startTop}%`; |
|
} |
|
|
|
|
|
const duration = Math.random() * 3 + 2; |
|
const delay = Math.random() * 2; |
|
draggedCritter.style.animation = `float ${duration}s ease-in-out ${delay}s infinite`; |
|
|
|
draggedCritter = null; |
|
} |
|
|
|
|
|
function updateTimer() { |
|
gameState.timer--; |
|
elements.timerValue.textContent = gameState.timer.toString().padStart(2, '0'); |
|
|
|
if (gameState.timer <= 5) { |
|
elements.timerDisplay.classList.add('timer-critical', 'pulse-animation'); |
|
} |
|
|
|
if (gameState.timer <= 0) { |
|
loseGame(); |
|
} |
|
} |
|
|
|
|
|
function winGame() { |
|
clearInterval(gameState.timerInterval); |
|
gameState.isPlaying = false; |
|
elements.winSound.play(); |
|
elements.gameScreen.classList.add('hidden'); |
|
elements.winScreen.classList.remove('hidden'); |
|
|
|
|
|
if (gameState.currentLevel >= gameState.levels.length) { |
|
elements.nextLevelButton.classList.add('hidden'); |
|
} else { |
|
elements.nextLevelButton.classList.remove('hidden'); |
|
} |
|
} |
|
|
|
function loseGame() { |
|
clearInterval(gameState.timerInterval); |
|
gameState.isPlaying = false; |
|
elements.loseSound.play(); |
|
elements.gameScreen.classList.add('hidden'); |
|
elements.loseScreen.classList.remove('hidden'); |
|
} |
|
|
|
|
|
function nextLevel() { |
|
gameState.currentLevel++; |
|
elements.winScreen.classList.add('hidden'); |
|
elements.gameScreen.classList.remove('hidden'); |
|
loadLevel(gameState.currentLevel); |
|
} |
|
|
|
function restartGame() { |
|
elements.loseScreen.classList.add('hidden'); |
|
elements.gameScreen.classList.remove('hidden'); |
|
loadLevel(gameState.currentLevel); |
|
} |
|
|
|
function goToMainMenu() { |
|
elements.backgroundMusic.pause(); |
|
elements.winScreen.classList.add('hidden'); |
|
elements.loseScreen.classList.add('hidden'); |
|
elements.gameScreen.classList.add('hidden'); |
|
elements.startScreen.classList.remove('hidden'); |
|
} |
|
|
|
|
|
window.addEventListener('DOMContentLoaded', initGame); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/game-3-sort-em-silly-critter-chaos" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |