initial commit
60
index.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Les Jeux de Waterloo</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- Start Screen -->
|
||||
<div id="startScreen" class="start-screen">
|
||||
<div class="start-content">
|
||||
<h1>Jeux de Waterloo</h1>
|
||||
<h2>🗺️ District 33 🗺️</h2>
|
||||
<p class="start-description">
|
||||
Magali ! ton aventure est loin d'être finie...<br>
|
||||
Le Capitole t'a miniaturisée et plongée dans un monde virtuel.<br>
|
||||
Retrace tes pas dans ce District 33 que tu connais comme ta poche et replace les animaux cachés.<br>
|
||||
La libération de Sam et le trésor t'attendent à la fin de cette quête !<br>
|
||||
Bonne chance, Chevalière Badass !<br>
|
||||
</p>
|
||||
<button onclick="startGame()" class="start-btn">⚔️ COMMENCER L'AVENTURE ⚔️</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-content">
|
||||
<h1>Les Jeux de Waterloo</h1>
|
||||
<h2>🗺️ District 33 🗺️</h2>
|
||||
</div>
|
||||
<canvas id="map" width="auto" height="auto"></canvas>
|
||||
|
||||
|
||||
<div id="animalCounter" class="animal-counter">
|
||||
Animaux trouvés : <span id="foundCount">0</span> / 10
|
||||
</div>
|
||||
|
||||
<!-- Message popup -->
|
||||
<div id="messageBox" class="message-box hidden">
|
||||
<div class="message-content">
|
||||
<p id="messageText"></p>
|
||||
<div id="animalButtons" class="animal-buttons hidden">
|
||||
<button onclick="selectAnimal('lion')">🦁 Lion</button>
|
||||
<button onclick="selectAnimal('elephant')">🐘 Éléphant</button>
|
||||
<button onclick="selectAnimal('paon')">🦚 Paon</button>
|
||||
<button onclick="selectAnimal('koala')">🐨 Koala</button>
|
||||
<button onclick="selectAnimal('belier')">🐏 Bélier</button>
|
||||
<button onclick="selectAnimal('panda')">🐼 Panda</button>
|
||||
<button onclick="selectAnimal('girafe')">🦒 Giraffe</button>
|
||||
<button onclick="selectAnimal('bear')">🐻 Ours</button>
|
||||
<button onclick="selectAnimal('pingouin')">🐧 Pingouin</button>
|
||||
<button onclick="selectAnimal('singe')">🐒 Singe</button>
|
||||
<!-- <button onclick="selectAnimal('licorne')"><3E> Licorne</button> -->
|
||||
</div>
|
||||
<button id="closeButton" onclick="closeMessage()" class="close-btn hidden">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
849
script.js
Normal file
@@ -0,0 +1,849 @@
|
||||
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.", 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 ! 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: 7, floor: 2 }, // Stairs on floor 2
|
||||
{ x: 9, y: 7, floor: 1 }, // After taking stairs to floor 1
|
||||
{ x: 13, y: 3, 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: 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 !", 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;
|
||||
387
style.css
Normal file
@@ -0,0 +1,387 @@
|
||||
body {
|
||||
font-family: 'Courier New', monospace;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, #2c1810 0%, #4a2c1a 100%);
|
||||
color: #f4e4c1;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Start Screen */
|
||||
.start-screen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #1a0f08 0%, #2c1810 50%, #4a2c1a 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2000;
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
.start-screen.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.start-content {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.start-content h1 {
|
||||
font-size: clamp(1.3em, 5vw, 2.3em);
|
||||
color: #d4af37;
|
||||
text-shadow:
|
||||
3px 3px 0px #000,
|
||||
0 0 20px rgba(212, 175, 55, 0.8);
|
||||
margin-bottom: 20px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.start-content h2 {
|
||||
font-size: clamp(1.2em, 4vw, 1.8em);
|
||||
color: #ffd700;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.start-description {
|
||||
font-size: clamp(1em, 3vw, 1.2em);
|
||||
color: #e8d4a0;
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
line-height: 1.1;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
padding: 15px 20px;
|
||||
font-size: clamp(1.1em, 3vw, 1.5em);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
border: 4px solid #8b6914;
|
||||
background: linear-gradient(180deg, #d4af37 0%, #b8941f 100%);
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
box-shadow:
|
||||
0 6px 0 #5a4010,
|
||||
0 8px 15px rgba(0, 0, 0, 0.5),
|
||||
0 0 30px rgba(212, 175, 55, 0.5);
|
||||
letter-spacing: 2px;
|
||||
margin-top: 20px;
|
||||
transition: all 0.2s;
|
||||
animation: glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0%, 100% { box-shadow: 0 6px 0 #5a4010, 0 8px 15px rgba(0, 0, 0, 0.5), 0 0 30px rgba(212, 175, 55, 0.5); }
|
||||
50% { box-shadow: 0 6px 0 #5a4010, 0 8px 15px rgba(0, 0, 0, 0.5), 0 0 40px rgba(255, 215, 0, 0.8); }
|
||||
}
|
||||
|
||||
.start-btn:hover {
|
||||
background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
|
||||
transform: translateY(3px);
|
||||
box-shadow:
|
||||
0 3px 0 #5a4010,
|
||||
0 5px 10px rgba(0, 0, 0, 0.5),
|
||||
0 0 40px rgba(255, 215, 0, 0.8);
|
||||
}
|
||||
|
||||
.start-btn:active {
|
||||
transform: translateY(6px);
|
||||
box-shadow:
|
||||
0 0px 0 #5a4010,
|
||||
0 2px 5px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.start-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
padding: 12px 30px;
|
||||
}
|
||||
}
|
||||
.header-content {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
font-size: clamp(1.5em, 5vw, 2.5em);
|
||||
color: #d4af37;
|
||||
text-shadow:
|
||||
3px 3px 0px #000,
|
||||
0 0 20px rgba(212, 175, 55, 0.8);
|
||||
margin-bottom: 20px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.header-content h2 {
|
||||
font-size: clamp(1.2em, 4vw, 1.8em);
|
||||
color: #ffd700;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 5px 0;
|
||||
font-size: clamp(1em, 3vw, 1.4em);
|
||||
font-weight: bold;
|
||||
text-shadow: 3px 3px 0px #000;
|
||||
color: #d4af37;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #e8d4a0;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
font-size: clamp(0.9em, 2.5vw, 1.1em);
|
||||
margin: 5px 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #d4c5a0;
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
font-size: clamp(0.8em, 2vw, 1em);
|
||||
margin: 5px 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
border: 4px solid #8b6914;
|
||||
box-shadow:
|
||||
0 0 0 2px #d4af37,
|
||||
0 0 20px rgba(0, 0, 0, 0.8),
|
||||
inset 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
background-color: #2a1a0a;
|
||||
max-width: 100vw;
|
||||
max-height: calc(100vh - 180px);
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 5px auto;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
canvas {
|
||||
max-height: calc(100vh - 200px);
|
||||
border: 3px solid #8b6914;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
canvas {
|
||||
max-height: calc(100vh - 220px);
|
||||
border: 2px solid #8b6914;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animal counter */
|
||||
.animal-counter {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: clamp(1em, 3vw, 1.3em);
|
||||
font-weight: bold;
|
||||
margin: 5px 0;
|
||||
padding: 8px 15px;
|
||||
background: linear-gradient(180deg, #8b6914 0%, #6b5010 100%);
|
||||
color: #f4e4c1;
|
||||
border: 3px solid #d4af37;
|
||||
box-shadow:
|
||||
0 4px 0 #5a4010,
|
||||
0 6px 10px rgba(0, 0, 0, 0.5);
|
||||
display: inline-block;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.animal-counter {
|
||||
padding: 6px 12px;
|
||||
border: 2px solid #d4af37;
|
||||
margin: 3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
#foundCount {
|
||||
font-size: 1.3em;
|
||||
color: #ffd700;
|
||||
text-shadow: 0 0 5px #ff0, 2px 2px 0px #000;
|
||||
}
|
||||
|
||||
/* Message box styles */
|
||||
.message-box {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.message-box.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
background: linear-gradient(180deg, #3d2a1a 0%, #2c1810 100%);
|
||||
padding: 20px;
|
||||
border: 4px solid #d4af37;
|
||||
box-shadow:
|
||||
0 0 0 2px #8b6914,
|
||||
0 0 30px rgba(212, 175, 55, 0.5),
|
||||
inset 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
max-width: 90vw;
|
||||
width: 600px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
text-align: center;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.message-content {
|
||||
padding: 15px;
|
||||
border: 3px solid #d4af37;
|
||||
}
|
||||
}
|
||||
|
||||
.message-content p {
|
||||
font-size: clamp(1em, 3vw, 1.2em);
|
||||
margin-bottom: 15px;
|
||||
color: #f4e4c1;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.animal-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.animal-buttons {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.animal-buttons {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.animal-buttons.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.animal-buttons button {
|
||||
padding: 8px 4px;
|
||||
font-size: clamp(0.75em, 2vw, 0.9em);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
border: 3px solid #8b6914;
|
||||
background: linear-gradient(180deg, #d4af37 0%, #b8941f 100%);
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 3px 0 #5a4010, 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.animal-buttons button {
|
||||
padding: 6px 3px;
|
||||
border: 2px solid #8b6914;
|
||||
}
|
||||
}
|
||||
|
||||
.animal-buttons button:hover {
|
||||
background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
|
||||
transform: translateY(2px);
|
||||
box-shadow: 0 1px 0 #5a4010, 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.animal-buttons button:active {
|
||||
transform: translateY(3px);
|
||||
box-shadow: 0 0px 0 #5a4010, 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
padding: 10px 25px;
|
||||
font-size: clamp(1em, 3vw, 1.2em);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
border: 3px solid #8b6914;
|
||||
background: linear-gradient(180deg, #d4af37 0%, #b8941f 100%);
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 4px 0 #5a4010, 0 6px 10px rgba(0, 0, 0, 0.5);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.close-btn {
|
||||
padding: 8px 20px;
|
||||
border: 2px solid #8b6914;
|
||||
}
|
||||
}
|
||||
|
||||
.close-btn.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
|
||||
transform: translateY(2px);
|
||||
box-shadow: 0 2px 0 #5a4010, 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.close-btn:active {
|
||||
transform: translateY(4px);
|
||||
box-shadow: 0 0px 0 #5a4010, 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
387
style.css.backup
Normal file
@@ -0,0 +1,387 @@
|
||||
body {
|
||||
font-family: 'Courier New', monospace;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, #2c1810 0%, #4a2c1a 100%);
|
||||
color: #f4e4c1;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Start Screen */
|
||||
.start-screen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #1a0f08 0%, #2c1810 50%, #4a2c1a 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2000;
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
.start-screen.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.start-content {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.start-content h1 {
|
||||
font-size: clamp(1.3em, 5vw, 2.3em);
|
||||
color: #d4af37;
|
||||
text-shadow:
|
||||
3px 3px 0px #000,
|
||||
0 0 20px rgba(212, 175, 55, 0.8);
|
||||
margin-bottom: 20px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.start-content h2 {
|
||||
font-size: clamp(1.2em, 4vw, 1.8em);
|
||||
color: #ffd700;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.start-description {
|
||||
font-size: clamp(1em, 3vw, 1.2em);
|
||||
color: #e8d4a0;
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
line-height: 1.1;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
padding: 15px 20px;
|
||||
font-size: clamp(1.1em, 3vw, 1.5em);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
border: 4px solid #8b6914;
|
||||
background: linear-gradient(180deg, #d4af37 0%, #b8941f 100%);
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
box-shadow:
|
||||
0 6px 0 #5a4010,
|
||||
0 8px 15px rgba(0, 0, 0, 0.5),
|
||||
0 0 30px rgba(212, 175, 55, 0.5);
|
||||
letter-spacing: 2px;
|
||||
margin-top: 20px;
|
||||
transition: all 0.2s;
|
||||
animation: glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0%, 100% { box-shadow: 0 6px 0 #5a4010, 0 8px 15px rgba(0, 0, 0, 0.5), 0 0 30px rgba(212, 175, 55, 0.5); }
|
||||
50% { box-shadow: 0 6px 0 #5a4010, 0 8px 15px rgba(0, 0, 0, 0.5), 0 0 40px rgba(255, 215, 0, 0.8); }
|
||||
}
|
||||
|
||||
.start-btn:hover {
|
||||
background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
|
||||
transform: translateY(3px);
|
||||
box-shadow:
|
||||
0 3px 0 #5a4010,
|
||||
0 5px 10px rgba(0, 0, 0, 0.5),
|
||||
0 0 40px rgba(255, 215, 0, 0.8);
|
||||
}
|
||||
|
||||
.start-btn:active {
|
||||
transform: translateY(6px);
|
||||
box-shadow:
|
||||
0 0px 0 #5a4010,
|
||||
0 2px 5px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(212, 175, 55, 0.5);
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.start-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
padding: 12px 30px;
|
||||
}
|
||||
}
|
||||
.header-content {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
font-size: clamp(1.5em, 5vw, 2.5em);
|
||||
color: #d4af37;
|
||||
text-shadow:
|
||||
3px 3px 0px #000,
|
||||
0 0 20px rgba(212, 175, 55, 0.8);
|
||||
margin-bottom: 20px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.header-content h2 {
|
||||
font-size: clamp(1.2em, 4vw, 1.8em);
|
||||
color: #ffd700;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 5px 0;
|
||||
font-size: clamp(1em, 3vw, 1.4em);
|
||||
font-weight: bold;
|
||||
text-shadow: 3px 3px 0px #000;
|
||||
color: #d4af37;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #e8d4a0;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
font-size: clamp(0.9em, 2.5vw, 1.1em);
|
||||
margin: 5px 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #d4c5a0;
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
font-size: clamp(0.8em, 2vw, 1em);
|
||||
margin: 5px 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
border: 4px solid #8b6914;
|
||||
box-shadow:
|
||||
0 0 0 2px #d4af37,
|
||||
0 0 20px rgba(0, 0, 0, 0.8),
|
||||
inset 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
background-color: #2a1a0a;
|
||||
max-width: 100vw;
|
||||
max-height: calc(100vh - 180px);
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 5px auto;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
canvas {
|
||||
max-height: calc(100vh - 200px);
|
||||
border: 3px solid #8b6914;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
canvas {
|
||||
max-height: calc(100vh - 220px);
|
||||
border: 2px solid #8b6914;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animal counter */
|
||||
.animal-counter {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: clamp(1em, 3vw, 1.3em);
|
||||
font-weight: bold;
|
||||
margin: 5px 0;
|
||||
padding: 8px 15px;
|
||||
background: linear-gradient(180deg, #8b6914 0%, #6b5010 100%);
|
||||
color: #f4e4c1;
|
||||
border: 3px solid #d4af37;
|
||||
box-shadow:
|
||||
0 4px 0 #5a4010,
|
||||
0 6px 10px rgba(0, 0, 0, 0.5);
|
||||
display: inline-block;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.animal-counter {
|
||||
padding: 6px 12px;
|
||||
border: 2px solid #d4af37;
|
||||
margin: 3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
#foundCount {
|
||||
font-size: 1.3em;
|
||||
color: #ffd700;
|
||||
text-shadow: 0 0 5px #ff0, 2px 2px 0px #000;
|
||||
}
|
||||
|
||||
/* Message box styles */
|
||||
.message-box {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.message-box.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
background: linear-gradient(180deg, #3d2a1a 0%, #2c1810 100%);
|
||||
padding: 20px;
|
||||
border: 4px solid #d4af37;
|
||||
box-shadow:
|
||||
0 0 0 2px #8b6914,
|
||||
0 0 30px rgba(212, 175, 55, 0.5),
|
||||
inset 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
max-width: 90vw;
|
||||
width: 600px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
text-align: center;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.message-content {
|
||||
padding: 15px;
|
||||
border: 3px solid #d4af37;
|
||||
}
|
||||
}
|
||||
|
||||
.message-content p {
|
||||
font-size: clamp(1em, 3vw, 1.2em);
|
||||
margin-bottom: 15px;
|
||||
color: #f4e4c1;
|
||||
text-shadow: 2px 2px 0px #000;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.animal-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.animal-buttons {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.animal-buttons {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.animal-buttons.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.animal-buttons button {
|
||||
padding: 8px 4px;
|
||||
font-size: clamp(0.75em, 2vw, 0.9em);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
border: 3px solid #8b6914;
|
||||
background: linear-gradient(180deg, #d4af37 0%, #b8941f 100%);
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 3px 0 #5a4010, 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.animal-buttons button {
|
||||
padding: 6px 3px;
|
||||
border: 2px solid #8b6914;
|
||||
}
|
||||
}
|
||||
|
||||
.animal-buttons button:hover {
|
||||
background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
|
||||
transform: translateY(2px);
|
||||
box-shadow: 0 1px 0 #5a4010, 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.animal-buttons button:active {
|
||||
transform: translateY(3px);
|
||||
box-shadow: 0 0px 0 #5a4010, 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
padding: 10px 25px;
|
||||
font-size: clamp(1em, 3vw, 1.2em);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
border: 3px solid #8b6914;
|
||||
background: linear-gradient(180deg, #d4af37 0%, #b8941f 100%);
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 4px 0 #5a4010, 0 6px 10px rgba(0, 0, 0, 0.5);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.close-btn {
|
||||
padding: 8px 20px;
|
||||
border: 2px solid #8b6914;
|
||||
}
|
||||
}
|
||||
|
||||
.close-btn.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
|
||||
transform: translateY(2px);
|
||||
box-shadow: 0 2px 0 #5a4010, 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.close-btn:active {
|
||||
transform: translateY(4px);
|
||||
box-shadow: 0 0px 0 #5a4010, 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
BIN
tiles/bear.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
tiles/belier.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
tiles/door.png
Normal file
|
After Width: | Height: | Size: 590 KiB |
BIN
tiles/elephant.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
tiles/giraffe.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
tiles/koala.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
tiles/lion.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
tiles/panda.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
tiles/paon.png
Normal file
|
After Width: | Height: | Size: 330 KiB |
BIN
tiles/pingouin.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
tiles/player.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
tiles/singe.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
tiles/sky.png
Normal file
|
After Width: | Height: | Size: 704 KiB |
BIN
tiles/sol.png
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
tiles/stairs.png
Normal file
|
After Width: | Height: | Size: 245 KiB |
BIN
tiles/tileset.png
Normal file
|
After Width: | Height: | Size: 748 KiB |
BIN
tiles/wall.png
Normal file
|
After Width: | Height: | Size: 302 KiB |
BIN
tiles/wind.png
Normal file
|
After Width: | Height: | Size: 578 KiB |