| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Virtual Cat Companion</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 bounce { |
| 0%, 100% { transform: translateY(0); } |
| 50% { transform: translateY(-20px); } |
| } |
| @keyframes shake { |
| 0% { transform: rotate(-5deg); } |
| 50% { transform: rotate(5deg); } |
| 100% { transform: rotate(-5deg); } |
| } |
| @keyframes eat { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.05) rotate(2deg); } |
| 100% { transform: scale(1); } |
| } |
| @keyframes walk { |
| 0% { transform: translateX(0); } |
| 50% { transform: translateX(10px); } |
| 100% { transform: translateX(0); } |
| } |
| @keyframes bubble { |
| 0% { transform: scale(0); opacity: 0; } |
| 50% { transform: scale(1.1); opacity: 1; } |
| 100% { transform: scale(1); opacity: 1; } |
| } |
| .bounce { animation: bounce 1s infinite; } |
| .shake { animation: shake 0.5s infinite; } |
| .eat { animation: eat 0.5s infinite; } |
| .walk { animation: walk 1s infinite; } |
| .bubble { animation: bubble 0.5s forwards; } |
| .cat-status-bar { |
| transition: width 0.5s ease; |
| } |
| .game-container { |
| background: linear-gradient(to bottom, #f0f9ff 0%, #cbebff 100%); |
| } |
| .cat-bath { |
| background: linear-gradient(to bottom, #e0f7ff 0%, #b3e0ff 100%); |
| } |
| .cat-park { |
| background: linear-gradient(to bottom, #e8f5e9 0%, #c8e6c9 100%); |
| } |
| .cat-room { |
| background: linear-gradient(to bottom, #fff3e0 0%, #ffe0b2 100%); |
| } |
| .hidden { |
| display: none !important; |
| } |
| .tooltip { |
| position: relative; |
| } |
| .tooltip:hover::after { |
| content: attr(data-tooltip); |
| position: absolute; |
| bottom: 100%; |
| left: 50%; |
| transform: translateX(-50%); |
| background: #333; |
| color: white; |
| padding: 4px 8px; |
| border-radius: 4px; |
| font-size: 12px; |
| white-space: nowrap; |
| z-index: 10; |
| } |
| </style> |
| </head> |
| <body class="font-sans bg-pink-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> |
| |
| <header class="text-center mb-8"> |
| <h1 class="text-4xl font-bold text-pink-600 mb-2">Virtual Cat Companion</h1> |
| <p class="text-gray-600">A cute friend to make you feel better ❤️</p> |
| </header> |
|
|
| |
| <div id="name-screen" class="bg-white rounded-xl shadow-lg p-8 text-center mb-8"> |
| <h2 class="text-2xl font-semibold text-pink-500 mb-4">What would you like to name your cat?</h2> |
| <div class="flex flex-col sm:flex-row gap-4 justify-center items-center"> |
| <input type="text" id="cat-name-input" placeholder="Enter cat name" class="px-4 py-2 border border-pink-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-pink-400 w-full sm:w-64"> |
| <button id="name-submit" class="bg-pink-500 hover:bg-pink-600 text-white px-6 py-2 rounded-lg transition duration-200">Create Cat</button> |
| </div> |
| <div class="mt-6"> |
| <p class="text-gray-500 mb-2">Choose a color for your cat:</p> |
| <div class="flex justify-center gap-3"> |
| <div class="w-10 h-10 rounded-full bg-orange-300 border-2 border-orange-400 cursor-pointer cat-color" data-color="orange"></div> |
| <div class="w-10 h-10 rounded-full bg-gray-300 border-2 border-gray-400 cursor-pointer cat-color" data-color="gray"></div> |
| <div class="w-10 h-10 rounded-full bg-white border-2 border-gray-200 cursor-pointer cat-color" data-color="white"></div> |
| <div class="w-10 h-10 rounded-full bg-black border-2 border-gray-800 cursor-pointer cat-color" data-color="black"></div> |
| <div class="w-10 h-10 rounded-full bg-yellow-100 border-2 border-yellow-200 cursor-pointer cat-color" data-color="cream"></div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="game-screen" class="hidden"> |
| |
| <div class="bg-white rounded-xl shadow-md p-4 mb-6 flex flex-col sm:flex-row justify-between items-center"> |
| <div class="flex items-center mb-3 sm:mb-0"> |
| <h2 id="cat-name-display" class="text-2xl font-bold text-pink-600 mr-4"></h2> |
| <div class="flex items-center"> |
| <span class="text-yellow-500 mr-1"><i class="fas fa-star"></i></span> |
| <span id="happiness-level" class="font-medium">100</span> |
| </div> |
| </div> |
| <div class="flex space-x-4"> |
| <button id="settings-btn" class="text-gray-500 hover:text-pink-500 transition"> |
| <i class="fas fa-cog text-xl"></i> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="game-area" class="game-container rounded-xl shadow-lg overflow-hidden relative h-96 mb-6"> |
| |
| <div id="cat-room" class="cat-room h-full w-full flex flex-col items-center justify-center p-4"> |
| <div id="cat-container" class="relative"> |
| <div id="cat" class="relative"> |
| |
| </div> |
| <div id="speech-bubble" class="absolute -top-20 left-1/2 transform -translate-x-1/2 bg-white rounded-lg p-3 shadow-md hidden bubble"> |
| <p id="cat-speech" class="text-sm"></p> |
| </div> |
| </div> |
| <div id="room-items" class="flex mt-8 gap-4"> |
| <div class="bg-pink-100 rounded-lg p-3 cursor-pointer hover:bg-pink-200 transition tooltip" data-tooltip="Food Bowl" id="food-btn"> |
| <i class="fas fa-utensils text-2xl text-pink-600"></i> |
| </div> |
| <div class="bg-blue-100 rounded-lg p-3 cursor-pointer hover:bg-blue-200 transition tooltip" data-tooltip="Bath Time" id="bath-btn"> |
| <i class="fas fa-shower text-2xl text-blue-600"></i> |
| </div> |
| <div class="bg-green-100 rounded-lg p-3 cursor-pointer hover:bg-green-200 transition tooltip" data-tooltip="Go Outside" id="walk-btn"> |
| <i class="fas fa-tree text-2xl text-green-600"></i> |
| </div> |
| <div class="bg-yellow-100 rounded-lg p-3 cursor-pointer hover:bg-yellow-200 transition tooltip" data-tooltip="Play Games" id="play-btn"> |
| <i class="fas fa-gamepad text-2xl text-yellow-600"></i> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="cat-bath" class="cat-bath h-full w-full hidden flex flex-col items-center justify-center p-4"> |
| <div id="bath-cat" class="relative"> |
| |
| </div> |
| <div class="flex gap-4 mt-8"> |
| <button id="use-soap" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-soap mr-2"></i> Use Soap |
| </button> |
| <button id="rinse" class="bg-blue-400 hover:bg-blue-500 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-shower mr-2"></i> Rinse |
| </button> |
| <button id="dry" class="bg-yellow-400 hover:bg-yellow-500 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-wind mr-2"></i> Dry |
| </button> |
| <button id="exit-bath" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-door-open mr-2"></i> Exit |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="cat-park" class="cat-park h-full w-full hidden flex flex-col items-center justify-center p-4"> |
| <div id="walking-cat" class="relative"> |
| |
| </div> |
| <div class="flex gap-4 mt-8"> |
| <button id="play-fetch" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-dog mr-2"></i> Play Fetch |
| </button> |
| <button id="chase-butterflies" class="bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-butterfly mr-2"></i> Chase Butterflies |
| </button> |
| <button id="exit-park" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-door-open mr-2"></i> Go Home |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="mini-games" class="bg-indigo-50 h-full w-full hidden flex flex-col items-center justify-center p-4"> |
| <h3 class="text-2xl font-semibold text-indigo-600 mb-6">Choose a Game to Play</h3> |
| <div class="grid grid-cols-1 sm:grid-cols-2 gap-4 w-full max-w-md"> |
| <button id="memory-game-btn" class="bg-indigo-500 hover:bg-indigo-600 text-white px-6 py-4 rounded-lg transition flex flex-col items-center"> |
| <i class="fas fa-brain text-3xl mb-2"></i> |
| <span>Memory Game</span> |
| </button> |
| <button id="whack-a-mouse-btn" class="bg-red-500 hover:bg-red-600 text-white px-6 py-4 rounded-lg transition flex flex-col items-center"> |
| <i class="fas fa-chess-pawn text-3xl mb-2"></i> |
| <span>Whack-a-Mouse</span> |
| </button> |
| <button id="laser-pointer-btn" class="bg-yellow-500 hover:bg-yellow-600 text-white px-6 py-4 rounded-lg transition flex flex-col items-center"> |
| <i class="fas fa-lightbulb text-3xl mb-2"></i> |
| <span>Laser Pointer</span> |
| </button> |
| <button id="exit-games" class="bg-gray-500 hover:bg-gray-600 text-white px-6 py-4 rounded-lg transition flex flex-col items-center"> |
| <i class="fas fa-home text-3xl mb-2"></i> |
| <span>Back Home</span> |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="memory-game" class="h-full w-full hidden flex flex-col items-center justify-center p-4 bg-blue-50"> |
| <h3 class="text-2xl font-semibold text-blue-600 mb-4">Memory Game</h3> |
| <p class="text-gray-600 mb-6">Match all the pairs of cat pictures!</p> |
| <div id="memory-board" class="grid grid-cols-4 gap-3 mb-6"> |
| |
| </div> |
| <div class="flex items-center gap-4"> |
| <span id="memory-score" class="font-bold text-lg">Matches: 0/6</span> |
| <button id="exit-memory-game" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-times mr-2"></i> Exit Game |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="whack-a-mouse" class="h-full w-full hidden flex flex-col items-center justify-center p-4 bg-red-50"> |
| <h3 class="text-2xl font-semibold text-red-600 mb-4">Whack-a-Mouse</h3> |
| <p class="text-gray-600 mb-2">Click the mice when they pop up!</p> |
| <p id="whack-timer" class="text-xl font-bold mb-4">Time: 30s</p> |
| <div id="whack-score-display" class="text-xl font-bold mb-6">Score: 0</div> |
| <div id="whack-game-area" class="relative w-64 h-64 bg-amber-100 rounded-full border-4 border-amber-300 mb-6"> |
| |
| </div> |
| <button id="exit-whack-game" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-times mr-2"></i> Exit Game |
| </button> |
| </div> |
|
|
| |
| <div id="laser-pointer" class="h-full w-full hidden flex flex-col items-center justify-center p-4 bg-yellow-50"> |
| <h3 class="text-2xl font-semibold text-yellow-600 mb-4">Laser Pointer</h3> |
| <p class="text-gray-600 mb-6">Move your mouse to make the cat chase the laser!</p> |
| <div id="laser-game-area" class="relative w-full h-64 bg-gray-100 rounded-lg mb-6"> |
| <div id="laser-dot" class="w-4 h-4 bg-red-500 rounded-full absolute hidden"></div> |
| <div id="laser-cat" class="absolute bottom-0 left-1/2 transform -translate-x-1/2"> |
| |
| </div> |
| </div> |
| <div class="flex items-center gap-4"> |
| <span id="laser-score" class="font-bold text-lg">Points: 0</span> |
| <button id="exit-laser-game" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-times mr-2"></i> Exit Game |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-md p-4 mb-6"> |
| <div class="grid grid-cols-1 sm:grid-cols-2 gap-4"> |
| <div> |
| <div class="flex justify-between mb-1"> |
| <span class="font-medium text-gray-700"><i class="fas fa-heart text-red-500 mr-2"></i> Happiness</span> |
| <span id="happiness-percent" class="font-medium">100%</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div id="happiness-bar" class="cat-status-bar bg-pink-500 h-2.5 rounded-full" style="width: 100%"></div> |
| </div> |
| </div> |
| <div> |
| <div class="flex justify-between mb-1"> |
| <span class="font-medium text-gray-700"><i class="fas fa-utensils text-yellow-500 mr-2"></i> Hunger</span> |
| <span id="hunger-percent" class="font-medium">0%</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div id="hunger-bar" class="cat-status-bar bg-yellow-500 h-2.5 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| <div> |
| <div class="flex justify-between mb-1"> |
| <span class="font-medium text-gray-700"><i class="fas fa-shower text-blue-500 mr-2"></i> Cleanliness</span> |
| <span id="clean-percent" class="font-medium">100%</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div id="clean-bar" class="cat-status-bar bg-blue-500 h-2.5 rounded-full" style="width: 100%"></div> |
| </div> |
| </div> |
| <div> |
| <div class="flex justify-between mb-1"> |
| <span class="font-medium text-gray-700"><i class="fas fa-bolt text-green-500 mr-2"></i> Energy</span> |
| <span id="energy-percent" class="font-medium">100%</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div id="energy-bar" class="cat-status-bar bg-green-500 h-2.5 rounded-full" style="width: 100%"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="food-selection" class="hidden bg-white rounded-xl shadow-md p-4 mb-6"> |
| <h3 class="text-xl font-semibold text-gray-700 mb-4">What would you like to feed your cat?</h3> |
| <div class="grid grid-cols-2 sm:grid-cols-4 gap-4"> |
| <div class="food-item bg-pink-50 rounded-lg p-4 text-center cursor-pointer hover:bg-pink-100 transition" data-food="fish" data-energy="10"> |
| <i class="fas fa-fish text-3xl text-blue-500 mb-2"></i> |
| <p class="font-medium">Fish</p> |
| </div> |
| <div class="food-item bg-yellow-50 rounded-lg p-4 text-center cursor-pointer hover:bg-yellow-100 transition" data-food="chicken" data-energy="15"> |
| <i class="fas fa-drumstick-bite text-3xl text-yellow-600 mb-2"></i> |
| <p class="font-medium">Chicken</p> |
| </div> |
| <div class="food-item bg-green-50 rounded-lg p-4 text-center cursor-pointer hover:bg-green-100 transition" data-food="treat" data-energy="5"> |
| <i class="fas fa-cookie text-3xl text-brown-500 mb-2"></i> |
| <p class="font-medium">Treat</p> |
| </div> |
| <div class="food-item bg-blue-50 rounded-lg p-4 text-center cursor-pointer hover:bg-blue-100 transition" data-food="milk" data-energy="7"> |
| <i class="fas fa-wine-bottle text-3xl text-blue-300 mb-2"></i> |
| <p class="font-medium">Milk</p> |
| </div> |
| </div> |
| <button id="cancel-food" class="mt-4 bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-times mr-2"></i> Cancel |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="settings-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-xl p-6 max-w-md w-full mx-4"> |
| <h3 class="text-xl font-semibold mb-4">Settings</h3> |
| <div class="mb-4"> |
| <label class="block text-gray-700 mb-2">Change Cat Name</label> |
| <div class="flex"> |
| <input type="text" id="rename-input" class="px-4 py-2 border border-gray-300 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-pink-400 w-full"> |
| <button id="rename-btn" class="bg-pink-500 hover:bg-pink-600 text-white px-4 py-2 rounded-r-lg transition">Change</button> |
| </div> |
| </div> |
| <div class="mb-6"> |
| <label class="block text-gray-700 mb-2">Change Cat Color</label> |
| <div class="flex gap-3"> |
| <div class="w-10 h-10 rounded-full bg-orange-300 border-2 border-orange-400 cursor-pointer cat-color-setting" data-color="orange"></div> |
| <div class="w-10 h-10 rounded-full bg-gray-300 border-2 border-gray-400 cursor-pointer cat-color-setting" data-color="gray"></div> |
| <div class="w-10 h-10 rounded-full bg-white border-2 border-gray-200 cursor-pointer cat-color-setting" data-color="white"></div> |
| <div class="w-10 h-10 rounded-full bg-black border-2 border-gray-800 cursor-pointer cat-color-setting" data-color="black"></div> |
| <div class="w-10 h-10 rounded-full bg-yellow-100 border-2 border-yellow-200 cursor-pointer cat-color-setting" data-color="cream"></div> |
| </div> |
| </div> |
| <div class="flex justify-between"> |
| <button id="reset-game" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-trash-alt mr-2"></i> Reset Game |
| </button> |
| <button id="close-settings" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| <i class="fas fa-times mr-2"></i> Close |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="reset-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-xl p-6 max-w-md w-full mx-4"> |
| <h3 class="text-xl font-semibold mb-4">Reset Game</h3> |
| <p class="text-gray-700 mb-6">Are you sure you want to reset your game? All progress will be lost.</p> |
| <div class="flex justify-end gap-4"> |
| <button id="cancel-reset" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition"> |
| Cancel |
| </button> |
| <button id="confirm-reset" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition"> |
| Reset |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const gameState = { |
| catName: "", |
| catColor: "orange", |
| happiness: 100, |
| hunger: 0, |
| cleanliness: 100, |
| energy: 100, |
| lastFed: null, |
| lastPlayed: null, |
| lastCleaned: null, |
| speechTimeout: null, |
| needs: { |
| hungry: false, |
| dirty: false, |
| lonely: false, |
| tired: false |
| }, |
| gameActive: false |
| }; |
| |
| |
| const nameScreen = document.getElementById('name-screen'); |
| const gameScreen = document.getElementById('game-screen'); |
| const catNameInput = document.getElementById('cat-name-input'); |
| const nameSubmit = document.getElementById('name-submit'); |
| const catNameDisplay = document.getElementById('cat-name-display'); |
| const catContainer = document.getElementById('cat'); |
| const bathCat = document.getElementById('bath-cat'); |
| const walkingCat = document.getElementById('walking-cat'); |
| const laserCat = document.getElementById('laser-cat'); |
| const speechBubble = document.getElementById('speech-bubble'); |
| const catSpeech = document.getElementById('cat-speech'); |
| const happinessBar = document.getElementById('happiness-bar'); |
| const hungerBar = document.getElementById('hunger-bar'); |
| const cleanBar = document.getElementById('clean-bar'); |
| const energyBar = document.getElementById('energy-bar'); |
| const happinessPercent = document.getElementById('happiness-percent'); |
| const hungerPercent = document.getElementById('hunger-percent'); |
| const cleanPercent = document.getElementById('clean-percent'); |
| const energyPercent = document.getElementById('energy-percent'); |
| const happinessLevel = document.getElementById('happiness-level'); |
| const foodBtn = document.getElementById('food-btn'); |
| const bathBtn = document.getElementById('bath-btn'); |
| const walkBtn = document.getElementById('walk-btn'); |
| const playBtn = document.getElementById('play-btn'); |
| const catRoom = document.getElementById('cat-room'); |
| const catBath = document.getElementById('cat-bath'); |
| const catPark = document.getElementById('cat-park'); |
| const miniGames = document.getElementById('mini-games'); |
| const foodSelection = document.getElementById('food-selection'); |
| const cancelFood = document.getElementById('cancel-food'); |
| const useSoap = document.getElementById('use-soap'); |
| const rinse = document.getElementById('rinse'); |
| const dry = document.getElementById('dry'); |
| const exitBath = document.getElementById('exit-bath'); |
| const playFetch = document.getElementById('play-fetch'); |
| const chaseButterflies = document.getElementById('chase-butterflies'); |
| const exitPark = document.getElementById('exit-park'); |
| const exitGames = document.getElementById('exit-games'); |
| const memoryGameBtn = document.getElementById('memory-game-btn'); |
| const whackAMouseBtn = document.getElementById('whack-a-mouse-btn'); |
| const laserPointerBtn = document.getElementById('laser-pointer-btn'); |
| const memoryGame = document.getElementById('memory-game'); |
| const whackAMouse = document.getElementById('whack-a-mouse'); |
| const laserPointer = document.getElementById('laser-pointer'); |
| const memoryBoard = document.getElementById('memory-board'); |
| const memoryScore = document.getElementById('memory-score'); |
| const exitMemoryGame = document.getElementById('exit-memory-game'); |
| const whackGameArea = document.getElementById('whack-game-area'); |
| const whackTimer = document.getElementById('whack-timer'); |
| const whackScoreDisplay = document.getElementById('whack-score-display'); |
| const exitWhackGame = document.getElementById('exit-whack-game'); |
| const laserGameArea = document.getElementById('laser-game-area'); |
| const laserDot = document.getElementById('laser-dot'); |
| const laserScore = document.getElementById('laser-score'); |
| const exitLaserGame = document.getElementById('exit-laser-game'); |
| const settingsBtn = document.getElementById('settings-btn'); |
| const settingsModal = document.getElementById('settings-modal'); |
| const closeSettings = document.getElementById('close-settings'); |
| const renameInput = document.getElementById('rename-input'); |
| const renameBtn = document.getElementById('rename-btn'); |
| const resetGame = document.getElementById('reset-game'); |
| const resetModal = document.getElementById('reset-modal'); |
| const cancelReset = document.getElementById('cancel-reset'); |
| const confirmReset = document.getElementById('confirm-reset'); |
| const colorButtons = document.querySelectorAll('.cat-color'); |
| const colorSettingButtons = document.querySelectorAll('.cat-color-setting'); |
| const foodItems = document.querySelectorAll('.food-item'); |
| |
| |
| function initGame() { |
| |
| const savedGame = localStorage.getItem('virtualCatGame'); |
| if (savedGame) { |
| const parsedGame = JSON.parse(savedGame); |
| Object.assign(gameState, parsedGame); |
| |
| |
| nameScreen.classList.add('hidden'); |
| gameScreen.classList.remove('hidden'); |
| |
| |
| catNameDisplay.textContent = gameState.catName; |
| renameInput.value = gameState.catName; |
| updateCatColor(); |
| |
| |
| updateStats(); |
| gameState.gameActive = true; |
| startNeedsDecay(); |
| } |
| } |
| |
| |
| nameSubmit.addEventListener('click', () => { |
| const name = catNameInput.value.trim(); |
| if (name) { |
| gameState.catName = name; |
| catNameDisplay.textContent = name; |
| renameInput.value = name; |
| |
| nameScreen.classList.add('hidden'); |
| gameScreen.classList.remove('hidden'); |
| |
| updateCatColor(); |
| gameState.gameActive = true; |
| saveGame(); |
| startNeedsDecay(); |
| |
| |
| setTimeout(() => { |
| makeCatTalk(`Hi ${name}! I'm so happy to meet you!`); |
| }, 1000); |
| } |
| }); |
| |
| |
| colorButtons.forEach(button => { |
| button.addEventListener('click', () => { |
| gameState.catColor = button.dataset.color; |
| updateCatColor(); |
| }); |
| }); |
| |
| colorSettingButtons.forEach(button => { |
| button.addEventListener('click', () => { |
| gameState.catColor = button.dataset.color; |
| updateCatColor(); |
| saveGame(); |
| }); |
| }); |
| |
| |
| function updateCatColor() { |
| let catColorClass = ''; |
| switch(gameState.catColor) { |
| case 'orange': |
| catColorClass = 'bg-orange-300'; |
| break; |
| case 'gray': |
| catColorClass = 'bg-gray-300'; |
| break; |
| case 'white': |
| catColorClass = 'bg-white'; |
| break; |
| case 'black': |
| catColorClass = 'bg-gray-800'; |
| break; |
| case 'cream': |
| catColorClass = 'bg-yellow-100'; |
| break; |
| } |
| |
| |
| catContainer.innerHTML = ` |
| <div class="relative w-32 h-24 ${catColorClass} rounded-tl-full rounded-tr-full rounded-br-full rounded-bl-full"> |
| <div class="absolute -top-2 -left-2 w-8 h-6 ${catColorClass} rounded-full transform rotate-45"></div> |
| <div class="absolute -top-2 -right-2 w-8 h-6 ${catColorClass} rounded-full transform -rotate-45"></div> |
| <div class="absolute top-6 left-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-6 right-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-7 left-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-7 right-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-10 left-1/2 transform -translate-x-1/2 w-3 h-2 bg-pink-300 rounded-full"></div> |
| <div class="absolute -bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-8 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 left-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 right-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| </div> |
| `; |
| |
| |
| bathCat.innerHTML = ` |
| <div class="relative w-32 h-24 ${catColorClass} rounded-tl-full rounded-tr-full rounded-br-full rounded-bl-full"> |
| <div class="absolute -top-2 -left-2 w-8 h-6 ${catColorClass} rounded-full transform rotate-45"></div> |
| <div class="absolute -top-2 -right-2 w-8 h-6 ${catColorClass} rounded-full transform -rotate-45"></div> |
| <div class="absolute top-6 left-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-6 right-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-7 left-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-7 right-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-10 left-1/2 transform -translate-x-1/2 w-3 h-2 bg-pink-300 rounded-full"></div> |
| <div class="absolute -bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-8 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 left-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 right-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| </div> |
| `; |
| |
| |
| walkingCat.innerHTML = ` |
| <div class="relative w-32 h-24 ${catColorClass} rounded-tl-full rounded-tr-full rounded-br-full rounded-bl-full walk"> |
| <div class="absolute -top-2 -left-2 w-8 h-6 ${catColorClass} rounded-full transform rotate-45"></div> |
| <div class="absolute -top-2 -right-2 w-8 h-6 ${catColorClass} rounded-full transform -rotate-45"></div> |
| <div class="absolute top-6 left-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-6 right-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-7 left-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-7 right-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-10 left-1/2 transform -translate-x-1/2 w-3 h-2 bg-pink-300 rounded-full"></div> |
| <div class="absolute -bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-8 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 left-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 right-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| </div> |
| `; |
| |
| |
| laserCat.innerHTML = ` |
| <div class="relative w-32 h-24 ${catColorClass} rounded-tl-full rounded-tr-full rounded-br-full rounded-bl-full"> |
| <div class="absolute -top-2 -left-2 w-8 h-6 ${catColorClass} rounded-full transform rotate-45"></div> |
| <div class="absolute -top-2 -right-2 w-8 h-6 ${catColorClass} rounded-full transform -rotate-45"></div> |
| <div class="absolute top-6 left-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-6 right-4 w-4 h-4 bg-white rounded-full"></div> |
| <div class="absolute top-7 left-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-7 right-5 w-2 h-2 bg-black rounded-full"></div> |
| <div class="absolute top-10 left-1/2 transform -translate-x-1/2 w-3 h-2 bg-pink-300 rounded-full"></div> |
| <div class="absolute -bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-8 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 left-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| <div class="absolute -bottom-8 right-6 w-4 h-6 ${catColorClass} rounded-b-full"></div> |
| </div> |
| `; |
| } |
| |
| |
| function makeCatTalk(message) { |
| if (gameState.speechTimeout) { |
| clearTimeout(gameState.speechTimeout); |
| } |
| |
| catSpeech.textContent = message; |
| speechBubble.classList.remove('hidden'); |
| speechBubble.classList.add('bubble'); |
| |
| |
| gameState.speechTimeout = setTimeout(() => { |
| speechBubble.classList.add('hidden'); |
| }, 3000); |
| } |
| |
| |
| function updateStats() { |
| happinessBar.style.width = `${gameState.happiness}%`; |
| hungerBar.style.width = `${gameState.hunger}%`; |
| cleanBar.style.width = `${gameState.cleanliness}%`; |
| energyBar.style.width = `${gameState.energy}%`; |
| |
| happinessPercent.textContent = `${gameState.happiness}%`; |
| hungerPercent.textContent = `${gameState.hunger}%`; |
| cleanPercent.textContent = `${gameState.cleanliness}%`; |
| energyPercent.textContent = `${gameState.energy}%`; |
| |
| happinessLevel.textContent = gameState.happiness; |
| |
| |
| checkNeeds(); |
| } |
| |
| |
| function checkNeeds() { |
| gameState.needs.hungry = gameState.hunger > 70; |
| gameState.needs.dirty = gameState.cleanliness < 30; |
| gameState.needs.lonely = gameState.happiness < 30; |
| gameState.needs.tired = gameState.energy < 20; |
| |
| |
| if (gameState.needs.hungry && Math.random() > 0.7) { |
| makeCatTalk("I'm so hungry..."); |
| } else if (gameState.needs.dirty && Math.random() > 0.7) { |
| makeCatTalk("I feel dirty..."); |
| } else if (gameState.needs.lonely && Math.random() > 0.7) { |
| makeCatTalk("Play with me!"); |
| } else if (gameState.needs.tired && Math.random() > 0.7) { |
| makeCatTalk("I'm so tired..."); |
| } else if (Math.random() > 0.95) { |
| const randomMessages = [ |
| "I love you!", |
| "Pet me please!", |
| "You're the best!", |
| "Meow!", |
| "Purrrrr...", |
| `Hi ${gameState.catName}!` |
| ]; |
| makeCatTalk(randomMessages[Math.floor(Math.random() * randomMessages.length)]); |
| } |
| } |
| |
| |
| function startNeedsDecay() { |
| setInterval(() => { |
| if (!gameState.gameActive) return; |
| |
| |
| if (gameState.happiness > 0) { |
| gameState.happiness = Math.max(0, gameState.happiness - 0.2); |
| } |
| |
| |
| if (gameState.hunger < 100) { |
| gameState.hunger = Math.min(100, gameState.hunger + 0.3); |
| } |
| |
| |
| if (gameState.cleanliness > 0) { |
| gameState.cleanliness = Math.max(0, gameState.cleanliness - 0.1); |
| } |
| |
| |
| if (gameState.energy < 100) { |
| gameState.energy = Math.min(100, gameState.energy + 0.1); |
| } |
| |
| updateStats(); |
| saveGame(); |
| }, 1000); |
| } |
| |
| |
| function saveGame() { |
| localStorage.setItem('virtualCatGame', JSON.stringify(gameState)); |
| } |
| |
| |
| foodBtn.addEventListener('click', () => { |
| catRoom.classList.add('hidden'); |
| foodSelection.classList.remove('hidden'); |
| }); |
| |
| |
| foodItems.forEach(item => { |
| item.addEventListener('click', () => { |
| const foodType = item.dataset.food; |
| const energyGain = parseInt(item.dataset.energy); |
| |
| |
| gameState.hunger = Math.max(0, gameState.hunger - 30); |
| |
| |
| gameState.energy = Math.min(100, gameState.energy + energyGain); |
| |
| |
| gameState.happiness = Math.min(100, gameState.happiness + 5); |
| |
| |
| updateStats(); |
| saveGame(); |
| |
| |
| catContainer.classList.add('eat'); |
| makeCatTalk(`Yummy ${foodType}!`); |
| |
| |
| foodSelection.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| |
| |
| setTimeout(() => { |
| catContainer.classList.remove('eat'); |
| }, 2000); |
| }); |
| }); |
| |
| |
| cancelFood.addEventListener('click', () => { |
| foodSelection.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| bathBtn.addEventListener('click', () => { |
| catRoom.classList.add('hidden'); |
| catBath.classList.remove('hidden'); |
| makeCatTalk("Do I really need a bath?"); |
| }); |
| |
| |
| useSoap.addEventListener('click', () => { |
| makeCatTalk("Bubbles everywhere!"); |
| bathCat.innerHTML += ` |
| <div class="absolute top-0 left-0 w-full h-full flex justify-center items-center"> |
| <div class="bubble w-8 h-8 bg-white rounded-full opacity-70"></div> |
| <div class="bubble w-6 h-6 bg-white rounded-full opacity-70" style="animation-delay: 0.2s"></div> |
| <div class="bubble w-5 h-5 bg-white rounded-full opacity-70" style="animation-delay: 0.4s"></div> |
| </div> |
| `; |
| |
| |
| gameState.cleanliness = Math.min(100, gameState.cleanliness + 20); |
| updateStats(); |
| saveGame(); |
| }); |
| |
| |
| rinse.addEventListener('click', () => { |
| makeCatTalk("That feels nice!"); |
| bathCat.innerHTML += ` |
| <div class="absolute top-0 left-0 w-full h-full flex justify-center items-center"> |
| <div class="absolute top-0 left-1/2 transform -translate-x-1/2 w-4 h-16 bg-blue-200 rounded-full"></div> |
| </div> |
| `; |
| |
| |
| gameState.cleanliness = Math.min(100, gameState.cleanliness + 30); |
| updateStats(); |
| saveGame(); |
| }); |
| |
| |
| dry.addEventListener('click', () => { |
| makeCatTalk("Ahhh, all dry now!"); |
| |
| |
| gameState.cleanliness = 100; |
| updateStats(); |
| saveGame(); |
| |
| |
| setTimeout(() => { |
| catBath.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }, 1000); |
| }); |
| |
| |
| exitBath.addEventListener('click', () => { |
| catBath.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| walkBtn.addEventListener('click', () => { |
| if (gameState.energy < 20) { |
| makeCatTalk("I'm too tired to go outside..."); |
| return; |
| } |
| |
| catRoom.classList.add('hidden'); |
| catPark.classList.remove('hidden'); |
| makeCatTalk("Yay! Outside time!"); |
| |
| |
| gameState.happiness = Math.min(100, gameState.happiness + 10); |
| updateStats(); |
| saveGame(); |
| }); |
| |
| |
| playFetch.addEventListener('click', () => { |
| if (gameState.energy < 15) { |
| makeCatTalk("I need to rest first..."); |
| return; |
| } |
| |
| makeCatTalk("Throw the ball! Throw the ball!"); |
| |
| |
| const ball = document.createElement('div'); |
| ball.className = 'absolute bottom-10 left-1/2 transform -translate-x-1/2 w-8 h-8 bg-red-500 rounded-full bounce'; |
| walkingCat.parentNode.appendChild(ball); |
| |
| |
| gameState.energy = Math.max(0, gameState.energy - 15); |
| gameState.happiness = Math.min(100, gameState.happiness + 15); |
| updateStats(); |
| saveGame(); |
| |
| |
| setTimeout(() => { |
| ball.remove(); |
| }, 2000); |
| }); |
| |
| |
| chaseButterflies.addEventListener('click', () => { |
| if (gameState.energy < 10) { |
| makeCatTalk("Maybe later..."); |
| return; |
| } |
| |
| makeCatTalk("Butterflies! I must catch them!"); |
| |
| |
| for (let i = 0; i < 3; i++) { |
| const butterfly = document.createElement('div'); |
| butterfly.className = `absolute top-${10 + i*10} left-${20 + i*20} text-2xl text-pink-400 fas fa-butterfly`; |
| butterfly.style.animation = `fly${i+1} 3s infinite`; |
| walkingCat.parentNode.appendChild(butterfly); |
| |
| |
| const style = document.createElement('style'); |
| style.textContent = ` |
| @keyframes fly${i+1} { |
| 0% { transform: translate(0, 0); } |
| 25% { transform: translate(${20 + i*10}px, ${10 + i*5}px); } |
| 50% { transform: translate(${40 + i*20}px, 0); } |
| 75% { transform: translate(${20 + i*10}px, ${-10 - i*5}px); } |
| 100% { transform: translate(0, 0); } |
| } |
| `; |
| document.head.appendChild(style); |
| } |
| |
| |
| gameState.energy = Math.max(0, gameState.energy - 10); |
| gameState.happiness = Math.min(100, gameState.happiness + 20); |
| updateStats(); |
| saveGame(); |
| |
| |
| setTimeout(() => { |
| const butterflies = document.querySelectorAll('.fa-butterfly'); |
| butterflies.forEach(b => b.remove()); |
| }, 3000); |
| }); |
| |
| |
| exitPark.addEventListener('click', () => { |
| catPark.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| playBtn.addEventListener('click', () => { |
| catRoom.classList.add('hidden'); |
| miniGames.classList.remove('hidden'); |
| }); |
| |
| |
| exitGames.addEventListener('click', () => { |
| miniGames.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| memoryGameBtn.addEventListener('click', () => { |
| if (gameState.energy < 20) { |
| makeCatTalk("I'm too tired to play right now..."); |
| miniGames.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| return; |
| } |
| |
| miniGames.classList.add('hidden'); |
| memoryGame.classList.remove('hidden'); |
| setupMemoryGame(); |
| }); |
| |
| |
| function setupMemoryGame() { |
| memoryBoard.innerHTML = ''; |
| const emojis = ['😺', '🐱', '😸', '🐈', '😹', '🐾']; |
| const cards = [...emojis, ...emojis].sort(() => 0.5 - Math.random()); |
| |
| let flippedCards = []; |
| let matchedPairs = 0; |
| |
| cards.forEach((emoji, index) => { |
| const card = document.createElement('div'); |
| card.className = 'w-16 h-16 bg-white rounded-lg flex items-center justify-center text-3xl cursor-pointer shadow-md hover:bg-gray-100 transition'; |
| card.dataset.emoji = emoji; |
| card.dataset.index = index; |
| card.innerHTML = '<div class="w-full h-full bg-pink-400 rounded-lg flex items-center justify-center">?</div>'; |
| |
| card.addEventListener('click', () => { |
| if (flippedCards.length < 2 && !card.classList.contains('flipped') && !card.classList.contains('matched')) { |
| flipCard(card); |
| |
| if (flippedCards.length === 2) { |
| checkForMatch(); |
| } |
| } |
| }); |
| |
| memoryBoard.appendChild(card); |
| }); |
| |
| memoryScore.textContent = 'Matches: 0/6'; |
| |
| function flipCard(card) { |
| card.classList.add('flipped'); |
| card.innerHTML = card.dataset.emoji; |
| flippedCards.push(card); |
| } |
| |
| function checkForMatch() { |
| const [card1, card2] = flippedCards; |
| |
| if (card1.dataset.emoji === card2.dataset.emoji) { |
| card1.classList.add('matched'); |
| card2.classList.add('matched'); |
| matchedPairs++; |
| memoryScore.textContent = `Matches: ${matchedPairs}/6`; |
| flippedCards = []; |
| |
| if (matchedPairs === 6) { |
| |
| gameState.happiness = Math.min(100, gameState.happiness + 25); |
| gameState.energy = Math.max(0, gameState.energy - 20); |
| updateStats(); |
| saveGame(); |
| |
| setTimeout(() => { |
| makeCatTalk("I won! That was fun!"); |
| memoryGame.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }, 1000); |
| } |
| } else { |
| setTimeout(() => { |
| card1.innerHTML = '<div class="w-full h-full bg-pink-400 rounded-lg flex items-center justify-center">?</div>'; |
| card2.innerHTML = '<div class="w-full h-full bg-pink-400 rounded-lg flex items-center justify-center">?</div>'; |
| card1.classList.remove('flipped'); |
| card2.classList.remove('flipped'); |
| flippedCards = []; |
| }, 1000); |
| } |
| } |
| } |
| |
| |
| exitMemoryGame.addEventListener('click', () => { |
| memoryGame.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| whackAMouseBtn.addEventListener('click', () => { |
| if (gameState.energy < 25) { |
| makeCatTalk("I need a nap first..."); |
| miniGames.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| return; |
| } |
| |
| miniGames.classList.add('hidden'); |
| whackAMouse.classList.remove('hidden'); |
| setupWhackGame(); |
| }); |
| |
| |
| function setupWhackGame() { |
| whackGameArea.innerHTML = ''; |
| let score = 0; |
| let timeLeft = 30; |
| whackScoreDisplay.textContent = 'Score: 0'; |
| whackTimer.textContent = `Time: ${timeLeft}s`; |
| |
| |
| for (let i = 0; i < 6; i++) { |
| const hole = document.createElement('div'); |
| hole.className = 'absolute w-10 h-10 bg-gray-700 rounded-full'; |
| |
| |
| const angle = (i / 6) * Math.PI * 2; |
| const radius = 80; |
| hole.style.left = `calc(50% + ${Math.cos(angle) * radius}px - 20px)`; |
| hole.style.top = `calc(50% + ${Math.sin(angle) * radius}px - 20px)`; |
| |
| whackGameArea.appendChild(hole); |
| } |
| |
| |
| const timer = setInterval(() => { |
| timeLeft--; |
| whackTimer.textContent = `Time: ${timeLeft}s`; |
| |
| if (timeLeft <= 0) { |
| clearInterval(timer); |
| clearInterval(mouseInterval); |
| |
| |
| gameState.happiness = Math.min(100, gameState.happiness + score * 2); |
| gameState.energy = Math.max(0, gameState.energy - 25); |
| updateStats(); |
| saveGame(); |
| |
| setTimeout(() => { |
| makeCatTalk(`I got ${score} mice!`); |
| whackAMouse.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }, 1000); |
| } |
| }, 1000); |
| |
| |
| let mouseInterval = setInterval(() => { |
| if (timeLeft <= 0) return; |
| |
| const holes = document.querySelectorAll('#whack-game-area > div'); |
| const randomHole = holes[Math.floor(Math.random() * holes.length)]; |
| |
| |
| const mouse = document.createElement('div'); |
| mouse.className = 'absolute w-8 h-6 bg-gray-500 rounded-full cursor-pointer'; |
| mouse.style.left = '6px'; |
| mouse.style.top = '-10px'; |
| |
| |
| randomHole.appendChild(mouse); |
| |
| |
| mouse.addEventListener('click', () => { |
| score++; |
| whackScoreDisplay.textContent = `Score: ${score}`; |
| |
| |
| mouse.className = 'absolute w-8 h-6 bg-red-500 rounded-full'; |
| mouse.innerHTML = '<div class="absolute top-0 left-0 w-full h-full flex justify-center items-center text-white">X</div>'; |
| |
| setTimeout(() => { |
| mouse.remove(); |
| }, 500); |
| }); |
| |
| |
| setTimeout(() => { |
| if (mouse.parentNode) { |
| mouse.remove(); |
| } |
| }, 1000); |
| }, 800); |
| } |
| |
| |
| exitWhackGame.addEventListener('click', () => { |
| whackAMouse.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| laserPointerBtn.addEventListener('click', () => { |
| if (gameState.energy < 15) { |
| makeCatTalk("Maybe after a short nap..."); |
| miniGames.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| return; |
| } |
| |
| miniGames.classList.add('hidden'); |
| laserPointer.classList.remove('hidden'); |
| setupLaserGame(); |
| }); |
| |
| |
| function setupLaserGame() { |
| let score = 0; |
| let gameTime = 20; |
| laserScore.textContent = 'Points: 0'; |
| |
| |
| laserDot.classList.remove('hidden'); |
| |
| |
| laserGameArea.addEventListener('mousemove', (e) => { |
| const rect = laserGameArea.getBoundingClientRect(); |
| const x = e.clientX - rect.left; |
| const y = e.clientY - rect.top; |
| |
| laserDot.style.left = `${x - 8}px`; |
| laserDot.style.top = `${y - 8}px`; |
| |
| |
| const catRect = laserCat.getBoundingClientRect(); |
| const catX = catRect.left - rect.left + catRect.width / 2; |
| const catY = catRect.top - rect.top + catRect.height / 2; |
| |
| const dx = x - catX; |
| const dy = y - catY; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| |
| if (distance < 100) { |
| |
| const angle = Math.atan2(dy, dx); |
| const moveX = Math.cos(angle) * 5; |
| const moveY = Math.sin(angle) * 5; |
| |
| laserCat.style.transform = `translateX(-50%) translate(${moveX}px, ${moveY}px)`; |
| |
| |
| score++; |
| laserScore.textContent = `Points: ${score}`; |
| } |
| }); |
| |
| |
| const timer = setInterval(() => { |
| gameTime--; |
| |
| if (gameTime <= 0) { |
| clearInterval(timer); |
| |
| |
| gameState.happiness = Math.min(100, gameState.happiness + score / 10); |
| gameState.energy = Math.max(0, gameState.energy - 15); |
| updateStats(); |
| saveGame(); |
| |
| laserDot.classList.add('hidden'); |
| |
| setTimeout(() => { |
| makeCatTalk("That was exhausting but fun!"); |
| laserPointer.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }, 1000); |
| } |
| }, 1000); |
| } |
| |
| |
| exitLaserGame.addEventListener('click', () => { |
| laserPointer.classList.add('hidden'); |
| catRoom.classList.remove('hidden'); |
| }); |
| |
| |
| settingsBtn.addEventListener('click', () => { |
| settingsModal.classList.remove('hidden'); |
| }); |
| |
| |
| closeSettings.addEventListener('click', () => { |
| settingsModal.classList.add('hidden'); |
| }); |
| |
| |
| renameBtn.addEventListener('click', () => { |
| const newName = renameInput.value.trim(); |
| if (newName) { |
| gameState.catName = newName; |
| catNameDisplay.textContent = newName; |
| saveGame(); |
| makeCatTalk(`I like my new name, ${newName}!`); |
| } |
| }); |
| |
| |
| resetGame.addEventListener('click', () => { |
| settingsModal.classList.add('hidden'); |
| resetModal.classList.remove('hidden'); |
| }); |
| |
| |
| cancelReset.addEventListener('click', () => { |
| resetModal.classList.add('hidden'); |
| }); |
| |
| |
| confirmReset.addEventListener('click', () => { |
| localStorage.removeItem('virtualCatGame'); |
| resetModal.classList.add('hidden'); |
| gameScreen.classList.add('hidden'); |
| nameScreen.classList.remove('hidden'); |
| catNameInput.value = ''; |
| gameState.catName = ""; |
| gameState.happiness = 100; |
| gameState.hunger = 0; |
| gameState.cleanliness = 100; |
| gameState.energy = 100; |
| gameState.gameActive = false; |
| }); |
| |
| |
| catContainer.addEventListener('click', () => { |
| if (!catContainer.classList.contains('shake')) { |
| catContainer.classList.add('shake'); |
| gameState.happiness = Math.min(100, gameState.happiness + 5); |
| updateStats(); |
| saveGame(); |
| |
| setTimeout(() => { |
| catContainer.classList.remove('shake'); |
| }, 500); |
| |
| const happyMessages = [ |
| "Purrrrr...", |
| "That feels nice!", |
| "More pets please!", |
| "I love you!", |
| "You're the best!" |
| ]; |
| makeCatTalk(happyMessages[Math.floor(Math.random() * happyMessages.length)]); |
| } |
| }); |
| |
| |
| 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=velja3/cat" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |