LukasBe's picture
Add 3 files
5517c64 verified
<!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);
}
/* Responsive adjustments */
@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">
<!-- Background particles -->
<div id="background-particles" class="fixed inset-0 pointer-events-none"></div>
<!-- Game Container -->
<div id="game-container" class="game-container flex-1 flex flex-col items-center justify-center p-4 relative z-10">
<!-- Start Screen -->
<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>
<!-- Game Screen -->
<div id="game-screen" class="hidden w-full h-full flex flex-col">
<!-- Game Header -->
<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>
<!-- Game Area -->
<div class="game-area">
<!-- Source Area -->
<div id="source-area" class="source-area">
<!-- Critters will be added here -->
</div>
<!-- Bins Area -->
<div id="bins-area" class="bins-area">
<!-- Bins will be added here -->
</div>
</div>
</div>
<!-- Win Screen -->
<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>
<!-- Lose Screen -->
<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 Elements -->
<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>
// Game state
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' }
}
};
// DOM elements
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')
};
// Initialize game
function initGame() {
// Event listeners
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);
// Preload sounds
elements.successSound.load();
elements.failSound.load();
elements.winSound.load();
elements.loseSound.load();
elements.backgroundMusic.volume = 0.3;
// Create background particles
createBackgroundParticles();
}
// Create background particles
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;
// Random animation
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);
}
}
// Start the game
function startGame() {
elements.startScreen.classList.add('hidden');
elements.gameScreen.classList.remove('hidden');
elements.backgroundMusic.play();
loadLevel(gameState.currentLevel);
}
// Load a level
function loadLevel(levelNum) {
// Reset game state
clearInterval(gameState.timerInterval);
gameState.sortedCount = 0;
// Get level data
const levelIndex = levelNum - 1;
const level = gameState.levels[levelIndex];
gameState.timer = level.time;
gameState.totalCritters = level.critters.length;
// Update UI
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');
// Clear game areas
elements.binsArea.innerHTML = '';
elements.sourceArea.innerHTML = '';
// Create bins for all critter types in this level
const critterTypesInLevel = new Set(level.critters.map(c => c.type));
critterTypesInLevel.forEach(type => {
const bin = createBin(type);
elements.binsArea.appendChild(bin);
});
// Create critters
level.critters.forEach((critter, index) => {
const critterElement = createCritter(critter.type, critter.x, critter.y, index);
elements.sourceArea.appendChild(critterElement);
});
// Start timer
gameState.timerInterval = setInterval(updateTimer, 1000);
gameState.isPlaying = true;
}
// Create a critter element
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;
// Add shadow
const shadow = document.createElement('div');
shadow.className = 'critter-shadow';
critter.appendChild(shadow);
// Add drag events
critter.addEventListener('mousedown', startDrag);
critter.addEventListener('touchstart', startDrag, { passive: false });
// Add random float animation
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;
}
// Create a bin element
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);
// Add shadow
const shadow = document.createElement('div');
shadow.className = 'bin-shadow';
bin.appendChild(shadow);
return bin;
}
// Create particles for effects
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);
}
}
// Drag functionality
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;
// Get initial position
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);
// Set dragging state
draggedCritter.classList.add('dragging');
draggedCritter.style.animation = 'none';
// Add move and end listeners
document.addEventListener('mousemove', dragCritter);
document.addEventListener('touchmove', dragCritter, { passive: false });
document.addEventListener('mouseup', endDrag);
document.addEventListener('touchend', endDrag);
// Highlight potential bins
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;
// Convert pixel movement to percentage
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;
// Remove event listeners
document.removeEventListener('mousemove', dragCritter);
document.removeEventListener('touchmove', dragCritter);
document.removeEventListener('mouseup', endDrag);
document.removeEventListener('touchend', endDrag);
// Remove dragging state
draggedCritter.classList.remove('dragging');
// Remove bin highlights
const bins = document.querySelectorAll('.bin');
bins.forEach(bin => bin.classList.remove('highlight'));
// Check if dropped on a bin
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) {
// Correct bin - success!
elements.successSound.play();
bin.classList.add('pop-animation');
bin.classList.add('flash-animation');
// Create particles
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);
// Remove critter
draggedCritter.remove();
// Update score
gameState.sortedCount++;
elements.sortedCount.textContent = gameState.sortedCount;
// Check win condition
if (gameState.sortedCount === gameState.totalCritters) {
winGame();
}
} else {
// Wrong bin - fail!
elements.failSound.play();
draggedCritter.classList.add('shake-animation');
setTimeout(() => {
draggedCritter.classList.remove('shake-animation');
}, 300);
// Return to start position
draggedCritter.style.left = `${startLeft}%`;
draggedCritter.style.top = `${startTop}%`;
}
break;
}
}
if (!droppedOnBin) {
// Not dropped on any bin - return to start
draggedCritter.style.left = `${startLeft}%`;
draggedCritter.style.top = `${startTop}%`;
}
// Restore float animation
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;
}
// Timer functions
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();
}
}
// Game end functions
function winGame() {
clearInterval(gameState.timerInterval);
gameState.isPlaying = false;
elements.winSound.play();
elements.gameScreen.classList.add('hidden');
elements.winScreen.classList.remove('hidden');
// Hide next level button if no more levels
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');
}
// Level navigation
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');
}
// Initialize the game when the page loads
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>