const canvas = document.getElementById('map'); const ctx = canvas.getContext('2d'); const tileSize = 64; // Game started flag let gameStarted = false; // Start game function function startGame() { const startScreen = document.getElementById('startScreen'); startScreen.classList.add('hidden'); // Show welcome message setTimeout(() => { showMessage("Bienvenue dans l'arène ! Tu peux te déplacer en glissant ton doigt dans la direction que tu veux. N'oublie pas les câlins à Sky !", false); gameStarted = true; }, 300); } // Charger les tiles individuelles const tiles = { wall: new Image(), floor: new Image(), stair: new Image(), wind: new Image(), door: new Image(), }; tiles.wall.src = 'tiles/wall.png'; tiles.floor.src = 'tiles/sol.png'; tiles.stair.src = 'tiles/stairs.png'; tiles.wind.src = 'tiles/wind.png'; tiles.door.src = 'tiles/door.png'; // Animal sprites const animalImages = { lion: new Image(), elephant: new Image(), paon: new Image(), koala: new Image(), belier: new Image(), panda: new Image(), girafe: new Image(), bear: new Image(), pingouin: new Image(), singe: new Image() }; animalImages.lion.src = 'tiles/lion.png'; animalImages.elephant.src = 'tiles/elephant.png'; animalImages.paon.src = 'tiles/paon.png'; animalImages.koala.src = 'tiles/koala.png'; animalImages.belier.src = 'tiles/belier.png'; animalImages.panda.src = 'tiles/panda.png'; animalImages.girafe.src = 'tiles/giraffe.png'; animalImages.bear.src = 'tiles/bear.png'; animalImages.pingouin.src = 'tiles/pingouin.png'; animalImages.singe.src = 'tiles/singe.png'; // Player sprite const player = { image: new Image(), x: 3, // Starting grid position y: 10, currentFloor: 2 }; player.image.src = 'tiles/player.png'; // You'll need to add this image const sky = { image: new Image(), x: 10, y : 5, currentFloor: 2, isMoving: false, targetX: 1, targetY: 3, barkAnimations: [] // Store active bark animations }; sky.image.src = 'tiles/sky.png'; // You'll need to add this image // Bark animation class class BarkAnimation { constructor(x, y, text = 'WAOUF!') { this.x = x; this.y = y; this.text = text; this.opacity = 1; this.offsetY = 0; this.lifetime = 0; this.maxLifetime = 60; // frames (about 1 second at 60fps) } update() { this.lifetime++; this.offsetY -= 1; // Move up this.opacity = 1 - (this.lifetime / this.maxLifetime); return this.lifetime < this.maxLifetime; } draw(ctx) { ctx.save(); ctx.globalAlpha = this.opacity; ctx.font = 'bold 30px Courier New'; ctx.fillStyle = '#fff'; ctx.strokeStyle = '#000'; ctx.lineWidth = 3; ctx.textAlign = 'center'; const text = this.text; ctx.strokeText(text, this.x, this.y + this.offsetY); ctx.fillText(text, this.x, this.y + this.offsetY); ctx.restore(); } } // Game state const gameState = { animalPuzzles: [ // Floor 2 - 1 animal { x: 1, y: 8, floor: 2, animal: 'panda', solved: false }, // Floor 1 - 8 animals { x: 12, y: 3, floor: 1, animal: 'lion', solved: false }, // cuisine table { x: 1, y: 5, floor: 1, animal: 'koala', solved: false }, // livre { x: 4, y: 1, floor: 1, animal: 'elephant', solved: false }, // bureau { x: 14, y: 10, floor: 1, animal: 'paon', solved: false }, // climatiseur { x: 3, y: 8, floor: 1, animal: 'belier', solved: false }, // table basse { x: 8, y: 2, floor: 1, animal: 'bear', solved: false }, // salle de bain { x: 11, y: 1, floor: 1, animal: 'pingouin', solved: false }, // cuisine { x: 7, y: 7, floor: 1, animal: 'singe', solved: false }, // canapé // Floor 0 - 1 animals { x: 2, y: 8, floor: 0, animal: 'girafe', solved: false }, // salle grimpe sous sol ], allPuzzlesSolved: false, skyMessageShown: false, treasureRevealed: false }; // Multiple floors const maps = [ // Floor 0 (Basement Floor) [ ['wall', 'wall', 'wall', 'wall', 'wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','wall','floor','door','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','wall','floor','wall','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','wall','wall','wall','floor','floor', 'floor','floor','floor','wall'], ['wall', 'wall', 'wall', 'wall', 'wall', 'door','wall','wall','wall','wall','stair','wall','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'wall', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','door','floor','floor', 'floor','floor','floor','wall'], ['wall', 'door', 'wall', 'floor', 'floor', 'floor','floor','floor','wall','wall','door','wall','wall','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','wall','floor','floor','floor','floor','wall','floor', 'floor','wall','door','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','wall','floor','floor','floor','floor','wall','floor', 'floor','wall','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','wall','floor','floor','floor','floor','wall','floor', 'floor','wall','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','wall','floor','floor','floor','floor','wall','floor', 'floor','wall','floor','wall'], ['wall', 'wall', 'wall', 'wall', 'wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall'], ], // Floor 1 (Ground floor) [ ['wall', 'wall', 'wall', 'wind', 'wall','wall','wall','wind','wall','wall','wall','wind','door','wall','wall','wind','wall','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'wall','floor','floor','floor','wall','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'wall','floor','floor','floor','wall','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'wall','floor','floor','floor','wall','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'wall','wall','door','wall','wall','stair','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'door','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'wall', 'wall', 'wall', 'wall', 'wall','floor','wall','wall','wall','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','floor','floor','floor','floor','floor', 'wall','wall','door','wall'], ['wind', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','floor','floor','floor','floor','floor', 'floor','wind','floor','floor'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','stair','floor','floor','floor','floor', 'floor','wall','floor','floor'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','wall','floor','floor'], ['wall', 'wall', 'wall', 'wind', 'wind','wall','wall','wall','wall','door','wall','wall','wind','wind','wall','wall','floor','floor'], ], // Floor 2 (Upper floor) [ ['wall', 'wall', 'wall', 'wall', 'wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','floor','floor','floor','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','floor','wall','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','floor','wall','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','stair','wall','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'floor', 'floor', 'floor', 'floor', 'floor','floor','floor','wall','wall','wall','floor','floor','floor', 'floor','floor','floor','wall'], ['wall', 'wall', 'wall', 'wall', 'wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall','wall'], ] ]; let map = maps[player.currentFloor]; // Stair connections: define which stair position leads to which floor // Format: { floor: { 'x,y': destinationFloor } } const stairConnections = { 0: { // Basement floor stairs '10,1': 1, // Stair at x=10, y=1 goes to floor 1 '10,2': 1, '10,3': 1, '10,4': 1, '9,7': 1, // Stair at x=9, y=7 goes to floor 1 '9,8': 1, '9,9': 1, '16,11': 1, // Stair at x=16, y=11 goes to floor 1 '17,11': 1 }, 1: { // Ground floor stairs '10,1': 0, // These stairs go down to basement '10,2': 0, '10,3': 0, '10,4': 0, '9,7': 2, // These stairs go up to floor 2 '9,8': 2, '9,9': 2, '16,11': 2, // These stairs go up to floor 2 '17,11': 2 }, 2: { // Upper floor stairs '10,1': 1, // All stairs on floor 2 go down to floor 1 '10,2': 1, '10,3': 1, '10,4': 1, '9,7': 1, '9,8': 1, '9,9': 1, '16,11': 1 } }; // Set canvas size based on map dimensions canvas.width = map[0].length * tileSize; // number of columns * tile size canvas.height = map.length * tileSize; // number of rows * tile size // Check if player can move to a position function canMoveTo(x, y) { if (y < 0 || y >= map.length || x < 0 || x >= map[0].length) { return false; } const tile = map[y][x]; // Player can walk on floor, door, and stair tiles return tile === 'floor' || tile === 'door' || tile === 'stair'; } // Check if player is on stairs and handle floor change function checkStairs() { const currentTile = map[player.y][player.x]; if (currentTile === 'stair') { const stairKey = `${player.x},${player.y}`; const floorConnections = stairConnections[player.currentFloor]; if (floorConnections && floorConnections[stairKey] !== undefined) { const targetFloor = floorConnections[stairKey]; player.currentFloor = targetFloor; map = maps[player.currentFloor]; // Update canvas size for new floor canvas.width = map[0].length * tileSize; canvas.height = map.length * tileSize; render(); } } } // Une fonction pour dessiner la carte une fois que toutes les images sont chargées function drawMap() { for(let y = 0; y < map.length; y++) { for(let x = 0; x < map[y].length; x++) { const tileKey = map[y][x]; const tileImage = tiles[tileKey]; if(tileImage.complete) { // vérifier que l'image est chargée ctx.drawImage(tileImage, x * tileSize, y * tileSize, tileSize, tileSize); } else { // Réessayer plus tard si image pas encore chargée tileImage.onload = () => { ctx.drawImage(tileImage, x * tileSize, y * tileSize, tileSize, tileSize); }; } } } } // Draw a single tile at position function drawTile(x, y) { const tileKey = map[y][x]; const tileImage = tiles[tileKey]; if(tileImage.complete) { ctx.drawImage(tileImage, x * tileSize, y * tileSize, tileSize, tileSize); } // Draw animal sprite if this tile has a solved puzzle const puzzle = gameState.animalPuzzles.find( p => p.x === x && p.y === y && p.floor === player.currentFloor && p.solved ); if (puzzle && animalImages[puzzle.animal].complete) { ctx.drawImage(animalImages[puzzle.animal], x * tileSize, y * tileSize, tileSize, tileSize); } } // Draw all solved animals on current floor function drawAnimals() { gameState.animalPuzzles.forEach(puzzle => { if (puzzle.solved && puzzle.floor === player.currentFloor) { const animalImg = animalImages[puzzle.animal]; if (animalImg.complete) { ctx.drawImage(animalImg, puzzle.x * tileSize, puzzle.y * tileSize, tileSize, tileSize); } } }); } // Draw player function drawPlayer() { if (player.image.complete) { ctx.drawImage(player.image, player.x * tileSize, player.y * tileSize, tileSize, tileSize); } } // Draw sky function drawSky() { // Only draw sky if on the same floor as player if (sky.currentFloor === player.currentFloor && sky.image.complete) { ctx.drawImage(sky.image, sky.x * tileSize, sky.y * tileSize, tileSize, tileSize); } } // Draw bark animations function drawBarkAnimations() { // Update and draw all active bark animations sky.barkAnimations = sky.barkAnimations.filter(bark => { const isAlive = bark.update(); if (isAlive) { bark.draw(ctx); } return isAlive; }); } // Create a bark animation at Sky's position function createBark() { // Pixel position centered horizontally above Sky const barkX = (sky.x + 0.5) * tileSize; // center of tile const barkY = sky.y * tileSize - 10; // slightly above // Distance check (Manhattan) for heart proximity const distance = Math.abs(player.x - sky.x) + Math.abs(player.y - sky.y); const isPlayerNear = (player.currentFloor === sky.currentFloor) && (distance <= 2); // Choose text const text = isPlayerNear ? '❤️' : 'WAOUF!'; // Push animation with pixel coordinates sky.barkAnimations.push(new BarkAnimation(barkX, barkY, text)); } // Start random barking let barkInterval = null; function startRandomBarking() { if (barkInterval) return; // Already barking function scheduleBark() { createBark(); // Schedule next bark between 1-3 seconds const nextBarkTime = 500 + Math.random() * 1000; // 1000ms to 3000ms barkInterval = setTimeout(scheduleBark, nextBarkTime); } scheduleBark(); } function stopRandomBarking() { if (barkInterval) { clearTimeout(barkInterval); barkInterval = null; } } // Update animal counter display// Update animal counter display function updateAnimalCounter() { const foundCount = gameState.animalPuzzles.filter(p => p.solved).length; const counterElement = document.getElementById('foundCount'); if (counterElement) { counterElement.textContent = foundCount; } } // Render everything (used for initial load and floor changes) function render() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawMap(); drawAnimals(); drawSky(); drawPlayer(); drawBarkAnimations(); updateAnimalCounter(); } // Update player position (more efficient - only redraws affected tiles) function updatePlayer(oldX, oldY) { // Redraw the old position (just the tile, which includes any animal sprite) drawTile(oldX, oldY); // Redraw sky if they were on the same tile if (sky.currentFloor === player.currentFloor && sky.x === oldX && sky.y === oldY) { drawSky(); } // Draw the tile at new position first (which includes any animal sprite) drawTile(player.x, player.y); // Redraw sky if they're on the same tile now if (sky.currentFloor === player.currentFloor && sky.x === player.x && sky.y === player.y) { drawSky(); } // Draw player on top drawPlayer(); // Draw bark animations drawBarkAnimations(); // Update counter updateAnimalCounter(); } // Check if player is on an animal puzzle tile function checkAnimalPuzzle() { const puzzle = gameState.animalPuzzles.find( p => p.x === player.x && p.y === player.y && p.floor === player.currentFloor && !p.solved ); if (puzzle) { currentPuzzle = puzzle; showMessage("Un animal a été trouvé ici ! Lequel ?", true); } } // Check if player is next to Sky function checkNearSky() { if (player.currentFloor !== sky.currentFloor) return false; const dx = Math.abs(player.x - sky.x); const dy = Math.abs(player.y - sky.y); return (dx === 1 && dy === 0) || (dx === 0 && dy === 1); } // Check if all puzzles are solved function checkAllPuzzlesSolved() { return gameState.animalPuzzles.every(p => p.solved); } // Messages personnalisés pour chaque animal const animalMessages = { lion: "La savane te reconnaît, ta puissance se fait entendre comme un rugissement au milieu des tempêtes. 🦁", elephant: "Ta mémoire est ton armure, chaque pas sur ce territoire est une preuve de ta sagesse silencieuse. 🐘", paon: "Sous la lumière, tu déploies ta beauté, chaque plume racontant ta vérité sans craindre le regard des autres. 🦚", koala: "Même suspendue entre deux mondes, tu trouves douceur et réconfort, là où d'autres n'oseraient se poser. 🐨", belier: "Rien ne t'arrête, tu tiens bon face au vent et défends ce qui t'est cher par ta simple présence. 🐏", panda: "La paix t'accompagne, tu trouves le calme au cœur des forêts, là où tu peux enfin respirer. 🐼", girafe: "Depuis les nuages, tu observes, toujours en avant-garde, cherchant ce qui se cache là où personne ne va. 🦒", bear: "Dans la nuit profonde, tu sais où trouver refuge, seule, mais jamais abattue. 🐻", pingouin: "Même dans le froid, tu restes debout, ta joie n'est jamais perdue même lorsque la glace s'étend partout. 🐧", singe: "Agile et rusée, tu t'adaptes à chaque branche, n'abandonnant jamais ton sourire malgré les obstacles. 🐒" }; // Show message to player let currentPuzzle = null; function showMessage(text, showButtons = false) { const messageBox = document.getElementById('messageBox'); const messageText = document.getElementById('messageText'); const animalButtons = document.getElementById('animalButtons'); const closeButton = document.getElementById('closeButton'); messageText.textContent = text; messageBox.classList.remove('hidden'); if (showButtons) { animalButtons.classList.remove('hidden'); closeButton.classList.add('hidden'); } else { animalButtons.classList.add('hidden'); closeButton.classList.remove('hidden'); } } // Select animal for puzzle function selectAnimal(animal) { if (!currentPuzzle) return; if (currentPuzzle.animal === animal) { // Correct answer currentPuzzle.solved = true; closeMessage(); // Show personalized message for the animal const personalizedMessage = animalMessages[animal] || `Bravo ! Le ${animal} est bien placé !`; showMessage(personalizedMessage, false); // Check if all puzzles solved if (checkAllPuzzlesSolved() && !gameState.allPuzzlesSolved) { gameState.allPuzzlesSolved = true; setTimeout(() => { closeMessage(); showMessage("Bravo, tu as replacé tous les animaux ! Sam est enfin libre ! Va maintenant voir Sky pour trouver le trésor !", false); }, 2000); } } else { // Wrong answer - restart game closeMessage(); showMessage("Non, tu t'es trompeé !! Retour à la case départ ! ", false); setTimeout(() => { restartGame(); }, 2000); } currentPuzzle = null; } // Close message function closeMessage() { const messageBox = document.getElementById('messageBox'); messageBox.classList.add('hidden'); } // Restart game function restartGame() { // Reset player position player.x = 3; player.y = 10; player.currentFloor = 2; // Reset sky position sky.x = 1; sky.y = 3; sky.currentFloor = 2; sky.isMoving = false; // Reset game state gameState.animalPuzzles.forEach(p => p.solved = false); gameState.allPuzzlesSolved = false; gameState.skyMessageShown = false; gameState.treasureRevealed = false; // Stop sky random movement if active if (skyRandomMovementInterval) { clearInterval(skyRandomMovementInterval); skyRandomMovementInterval = null; } // Stop barking stopRandomBarking(); // Clear existing bark animations sky.barkAnimations = []; // Reset map and render map = maps[player.currentFloor]; canvas.width = map[0].length * tileSize; canvas.height = map.length * tileSize; closeMessage(); render(); // Restart Sky's random movement startSkyRandomMovement(); // Restart barking setTimeout(() => { startRandomBarking(); }, 2000); } // Random movement for Sky let skyRandomMovementInterval = null; function startSkyRandomMovement() { if (skyRandomMovementInterval) return; // Already moving skyRandomMovementInterval = setInterval(() => { if (sky.isMoving || gameState.treasureRevealed) return; // Random direction const directions = ['up', 'down', 'left', 'right']; const randomDir = directions[Math.floor(Math.random() * directions.length)]; let newX = sky.x; let newY = sky.y; switch(randomDir) { case 'up': newY--; break; case 'down': newY++; break; case 'left': newX--; break; case 'right': newX++; break; } // Check if Sky can move to that position (same rules as player) const currentMap = maps[sky.currentFloor]; if (newY >= 0 && newY < currentMap.length && newX >= 0 && newX < currentMap[0].length) { const tile = currentMap[newY][newX]; if (tile === 'floor' || tile === 'door' || tile === 'stair') { const oldX = sky.x; const oldY = sky.y; sky.x = newX; sky.y = newY; // Render if player is on same floor if (player.currentFloor === sky.currentFloor) { // Redraw old position drawTile(oldX, oldY); // Redraw new position with sky drawTile(sky.x, sky.y); drawSky(); drawPlayer(); } } } }, 500); // Move every 500ms } function stopSkyRandomMovement() { if (skyRandomMovementInterval) { clearInterval(skyRandomMovementInterval); skyRandomMovementInterval = null; } } // Move Sky automatically function moveSkyToTreasure() { sky.isMoving = true; // Path: go to stairs at (9, 7), then to final position const path = [ { x: 9, y: 4, floor: 2 }, // Stairs on floor 2 { x: 9, y: 9, floor: 2 }, // Stairs on floor 2 { x: 9, y: 9, floor: 1 }, // After taking stairs to floor 1 { x: 12, y: 9, floor: 1 }, // After taking stairs to floor 1 { x: 13, y: 2, floor: 1 }, // After taking stairs to floor 1 { x: 10, y: 4, floor: 1 }, // After taking stairs to floor 1 { x: 10, y: 4, floor: 0 }, // After taking stairs to floor 1 { x: 10, y: 5, floor: 0 }, // After taking stairs to floor 1 { x: 15, y: 5, floor: 0 }, // After taking stairs to floor 1 { x: 15, y: 3, floor: 0 }, // After taking stairs to floor 1 { x: 12, y: 4, floor: 0 } // Final treasure position ]; let pathIndex = 0; const moveInterval = setInterval(() => { if (pathIndex >= path.length) { clearInterval(moveInterval); sky.isMoving = false; gameState.treasureRevealed = true; render(); return; } const target = path[pathIndex]; // Change floor if needed if (target.floor !== sky.currentFloor) { sky.currentFloor = target.floor; map = maps[player.currentFloor]; } // Move towards target if (sky.x < target.x) sky.x++; else if (sky.x > target.x) sky.x--; else if (sky.y < target.y) sky.y++; else if (sky.y > target.y) sky.y--; // Check if reached target if (sky.x === target.x && sky.y === target.y) { pathIndex++; } render(); }, 500); } // Move player function (used by both keyboard and touch) function movePlayer(direction) { // Don't allow movement until game has started if (!gameStarted) return; let newX = player.x; let newY = player.y; switch(direction) { case 'up': newY--; break; case 'down': newY++; break; case 'left': newX--; break; case 'right': newX++; break; } // Check if move is valid if (canMoveTo(newX, newY)) { const oldX = player.x; const oldY = player.y; player.x = newX; player.y = newY; updatePlayer(oldX, oldY); checkStairs(); // Check for animal puzzles checkAnimalPuzzle(); // Check if near Sky after all puzzles solved if (gameState.allPuzzlesSolved && !gameState.skyMessageShown && checkNearSky()) { gameState.skyMessageShown = true; stopSkyRandomMovement(); // Stop Sky from moving randomly showMessage("Wouaf Wouaf ! Suis moi pour trouver le trésor final !", false); setTimeout(() => { closeMessage(); moveSkyToTreasure(); }, 2000); } // Check if found Sky at treasure location if (gameState.treasureRevealed && checkNearSky()) { showMessage("Sky est au pied du trésor mais ces petites papattes sont trop courtes, va donc l'aider à le retrouver dans la vraie arène ! N'oublie pas d'appeler Sam maintenant qu'il est libéré afin que vous découvriez le trésor ensemble!", false); } } } // Handle keyboard input document.addEventListener('keydown', (e) => { switch(e.key) { case 'ArrowUp': movePlayer('up'); e.preventDefault(); break; case 'ArrowDown': movePlayer('down'); e.preventDefault(); break; case 'ArrowLeft': movePlayer('left'); e.preventDefault(); break; case 'ArrowRight': movePlayer('right'); e.preventDefault(); break; } }); // Touch controls for mobile let touchStartX = 0; let touchStartY = 0; let touchEndX = 0; let touchEndY = 0; canvas.addEventListener('touchstart', (e) => { e.preventDefault(); touchStartX = e.changedTouches[0].screenX; touchStartY = e.changedTouches[0].screenY; }, { passive: false }); canvas.addEventListener('touchend', (e) => { e.preventDefault(); touchEndX = e.changedTouches[0].screenX; touchEndY = e.changedTouches[0].screenY; handleSwipe(); }, { passive: false }); function handleSwipe() { const deltaX = touchEndX - touchStartX; const deltaY = touchEndY - touchStartY; const minSwipeDistance = 30; // Minimum distance for a swipe // Determine if horizontal or vertical swipe if (Math.abs(deltaX) > Math.abs(deltaY)) { // Horizontal swipe if (Math.abs(deltaX) > minSwipeDistance) { if (deltaX > 0) { movePlayer('right'); } else { movePlayer('left'); } } } else { // Vertical swipe if (Math.abs(deltaY) > minSwipeDistance) { if (deltaY > 0) { movePlayer('down'); } else { movePlayer('up'); } } } } // Attendre que toutes les images soient chargées avant de dessiner let imagesLoaded = 0; const totalImages = Object.keys(tiles).length + Object.keys(animalImages).length + 2; // tiles + animals + player + sky function checkAllImagesLoaded() { imagesLoaded++; if(imagesLoaded === totalImages) { render(); // Start Sky's random movement when game loads setTimeout(() => { startSkyRandomMovement(); }, 100); // Start animation loop for bark animations startAnimationLoop(); // Start random barking setTimeout(() => { startRandomBarking(); }, 2000); // Start barking after 2 seconds } } // Animation loop for continuous bark animations function startAnimationLoop() { function animate() { // Redraw everything if there are active animations if (sky.barkAnimations.length > 0) { ctx.clearRect(0, 0, canvas.width, canvas.height); drawMap(); drawAnimals(); drawSky(); drawPlayer(); drawBarkAnimations(); } requestAnimationFrame(animate); } animate(); } for (const key in tiles) { tiles[key].onload = checkAllImagesLoaded; } for (const key in animalImages) { animalImages[key].onload = checkAllImagesLoaded; } player.image.onload = checkAllImagesLoaded; sky.image.onload = checkAllImagesLoaded; // Make functions globally accessible for HTML onclick window.selectAnimal = selectAnimal; window.closeMessage = closeMessage; window.startGame = startGame;