friend-quest / index.html
WebashalarForML's picture
Add 2 files
ae7b651 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Friend Quest</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Custom CSS for pixel art and game elements */
.pixel-art {
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
}
#gameCanvas {
background-color: #6b8cff;
border: 4px solid #333;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
}
.quiz-modal {
background-color: #f0e6d2;
border: 4px solid #8b4513;
box-shadow: 0 0 0 8px #d2b48c, 0 0 0 12px #8b4513;
}
.quiz-option {
background-color: #d2b48c;
border: 3px solid #8b4513;
transition: all 0.2s;
}
.quiz-option:hover {
background-color: #f0e6d2;
transform: scale(1.05);
}
.quiz-option.correct {
background-color: #8fbc8f;
border-color: #556b2f;
}
.quiz-option.wrong {
background-color: #cd5c5c;
border-color: #8b0000;
}
@keyframes jump {
0% { transform: translateY(0); }
50% { transform: translateY(-20px); }
100% { transform: translateY(0); }
}
.jump-animation {
animation: jump 0.5s ease;
}
.character {
transition: transform 0.1s;
}
.moving {
animation: walk 0.5s steps(4) infinite;
}
@keyframes walk {
from { background-position: 0 0; }
to { background-position: -64px 0; }
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
<div class="text-center mb-4">
<h1 class="text-4xl font-bold mb-2 text-yellow-300 font-mono">FRIEND QUEST</h1>
<p class="text-lg text-gray-300">Find your lost friend by answering questions about them!</p>
</div>
<div class="relative">
<!-- Game Canvas -->
<canvas id="gameCanvas" width="800" height="400" class="pixel-art"></canvas>
<!-- Start Screen -->
<div id="startScreen" class="absolute inset-0 bg-black bg-opacity-70 flex flex-col items-center justify-center">
<div class="text-center p-8 bg-gray-800 rounded-lg border-4 border-yellow-400 max-w-md">
<h2 class="text-3xl font-bold mb-4 text-yellow-400">FRIEND QUEST</h2>
<p class="mb-6">Your friend is lost in this strange world! Jump over obstacles and answer questions about them to find your way.</p>
<button id="startButton" class="bg-yellow-500 hover:bg-yellow-400 text-black font-bold py-3 px-6 rounded-lg text-xl transition-all transform hover:scale-105">
START ADVENTURE
</button>
</div>
</div>
<!-- Quiz Modal -->
<div id="quizModal" class="absolute inset-0 hidden items-center justify-center">
<div class="quiz-modal p-6 rounded-lg w-full max-w-md">
<h3 class="text-2xl font-bold mb-4 text-center text-brown-800">What do you remember about your friend?</h3>
<p id="quizQuestion" class="text-lg mb-6 text-center text-brown-800">Loading question...</p>
<div class="grid grid-cols-1 gap-3">
<button class="quiz-option py-3 px-4 rounded-lg text-lg" data-answer="1">Option 1</button>
<button class="quiz-option py-3 px-4 rounded-lg text-lg" data-answer="2">Option 2</button>
<button class="quiz-option py-3 px-4 rounded-lg text-lg" data-answer="3">Option 3</button>
<button class="quiz-option py-3 px-4 rounded-lg text-lg" data-answer="4">Option 4</button>
</div>
<div id="quizFeedback" class="mt-4 text-center text-lg font-bold hidden"></div>
</div>
</div>
<!-- Game Over Screen -->
<div id="gameOverScreen" class="absolute inset-0 hidden bg-black bg-opacity-70 flex flex-col items-center justify-center">
<div class="text-center p-8 bg-gray-800 rounded-lg border-4 border-red-500 max-w-md">
<h2 class="text-3xl font-bold mb-4 text-red-500">GAME OVER</h2>
<p class="mb-6" id="gameOverMessage">You failed to find your friend!</p>
<button id="restartButton" class="bg-red-500 hover:bg-red-400 text-white font-bold py-3 px-6 rounded-lg text-xl transition-all transform hover:scale-105">
TRY AGAIN
</button>
</div>
</div>
<!-- Win Screen -->
<div id="winScreen" class="absolute inset-0 hidden bg-black bg-opacity-70 flex flex-col items-center justify-center">
<div class="text-center p-8 bg-gray-800 rounded-lg border-4 border-green-500 max-w-md">
<h2 class="text-3xl font-bold mb-4 text-green-500">VICTORY!</h2>
<p class="mb-6">You found your friend! Thanks to your great memory about them.</p>
<button id="playAgainButton" class="bg-green-500 hover:bg-green-400 text-white font-bold py-3 px-6 rounded-lg text-xl transition-all transform hover:scale-105">
PLAY AGAIN
</button>
</div>
</div>
</div>
<div class="mt-6 flex gap-8">
<div class="text-center">
<p class="text-lg">Controls:</p>
<p class="text-gray-400">Arrow keys to move, Space to jump</p>
</div>
<div class="text-center">
<p class="text-lg">Progress:</p>
<p id="scoreDisplay" class="text-yellow-400">Questions: 0/5</p>
</div>
</div>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const startButton = document.getElementById('startButton');
const quizModal = document.getElementById('quizModal');
const quizQuestion = document.getElementById('quizQuestion');
const quizOptions = document.querySelectorAll('.quiz-option');
const quizFeedback = document.getElementById('quizFeedback');
const gameOverScreen = document.getElementById('gameOverScreen');
const gameOverMessage = document.getElementById('gameOverMessage');
const restartButton = document.getElementById('restartButton');
const winScreen = document.getElementById('winScreen');
const playAgainButton = document.getElementById('playAgainButton');
const scoreDisplay = document.getElementById('scoreDisplay');
// Game state
let gameRunning = false;
let quizActive = false;
let currentQuestion = null;
let correctAnswers = 0;
const totalQuestions = 5;
// Character properties
const character = {
x: 50,
y: 300,
width: 32,
height: 48,
speed: 5,
velocityY: 0,
gravity: 0.5,
jumpForce: -12,
isJumping: false,
direction: 1, // 1 for right, -1 for left
moving: false
};
// Game world properties
const world = {
width: 2000,
scrollX: 0,
groundHeight: 350
};
// Platform and obstacle data
const platforms = [
{ x: 0, y: world.groundHeight, width: 500, height: 50 },
{ x: 600, y: world.groundHeight - 100, width: 200, height: 50 },
{ x: 900, y: world.groundHeight, width: 300, height: 50 },
{ x: 1300, y: world.groundHeight - 150, width: 200, height: 50 },
{ x: 1600, y: world.groundHeight, width: 400, height: 50 }
];
// Checkpoints that trigger quizzes
const checkpoints = [
{ x: 300, quizTaken: false },
{ x: 800, quizTaken: false },
{ x: 1100, quizTaken: false },
{ x: 1400, quizTaken: false },
{ x: 1800, quizTaken: false }
];
// Enemy data
const enemies = [
{ x: 400, y: world.groundHeight - 30, width: 32, height: 32, speed: 2, direction: 1 },
{ x: 700, y: world.groundHeight - 130, width: 32, height: 32, speed: 1, direction: -1 },
{ x: 1000, y: world.groundHeight - 30, width: 32, height: 32, speed: 3, direction: 1 },
{ x: 1500, y: world.groundHeight - 180, width: 32, height: 32, speed: 2, direction: -1 }
];
// Friend (goal) position
const friend = {
x: 1900,
y: world.groundHeight - 50,
width: 32,
height: 48
};
// Quiz questions
const questions = [
{
question: "What is your friend's favorite color?",
options: ["Blue", "Red", "Green", "Yellow"],
correct: 0
},
{
question: "What food does your friend dislike?",
options: ["Pizza", "Broccoli", "Ice cream", "Pasta"],
correct: 1
},
{
question: "Where did you first meet your friend?",
options: ["School", "Park", "Library", "Sports club"],
correct: 2
},
{
question: "What is your friend's favorite season?",
options: ["Winter", "Spring", "Summer", "Fall"],
correct: 3
},
{
question: "What is your friend's biggest fear?",
options: ["Spiders", "Heights", "Darkness", "Clowns"],
correct: 1
}
];
// Keyboard state
const keys = {
ArrowLeft: false,
ArrowRight: false,
ArrowUp: false,
Space: false
};
// Event listeners
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', resetGame);
playAgainButton.addEventListener('click', resetGame);
quizOptions.forEach(option => {
option.addEventListener('click', () => checkAnswer(option));
});
document.addEventListener('keydown', (e) => {
if (e.code in keys) {
keys[e.code] = true;
e.preventDefault();
}
});
document.addEventListener('keyup', (e) => {
if (e.code in keys) {
keys[e.code] = false;
e.preventDefault();
}
});
// Game functions
function startGame() {
gameRunning = true;
startScreen.classList.add('hidden');
correctAnswers = 0;
updateScoreDisplay();
resetGameState();
requestAnimationFrame(gameLoop);
}
function resetGame() {
gameOverScreen.classList.add('hidden');
winScreen.classList.add('hidden');
resetGameState();
requestAnimationFrame(gameLoop);
}
function resetGameState() {
character.x = 50;
character.y = 300;
character.velocityY = 0;
character.isJumping = false;
world.scrollX = 0;
// Reset checkpoints
checkpoints.forEach(cp => cp.quizTaken = false);
// Reset enemies positions
enemies[0].x = 400;
enemies[1].x = 700;
enemies[2].x = 1000;
enemies[3].x = 1500;
correctAnswers = 0;
updateScoreDisplay();
gameRunning = true;
quizActive = false;
}
function updateScoreDisplay() {
scoreDisplay.textContent = `Questions: ${correctAnswers}/${totalQuestions}`;
}
function gameLoop() {
if (!gameRunning) return;
update();
render();
requestAnimationFrame(gameLoop);
}
function update() {
if (quizActive) return;
// Handle movement
if (keys.ArrowLeft || keys['a']) {
character.x -= character.speed;
character.direction = -1;
character.moving = true;
} else if (keys.ArrowRight || keys['d']) {
character.x += character.speed;
character.direction = 1;
character.moving = true;
} else {
character.moving = false;
}
// Handle jumping
if ((keys.ArrowUp || keys.Space || keys['w']) && !character.isJumping) {
character.velocityY = character.jumpForce;
character.isJumping = true;
}
// Apply gravity
character.velocityY += character.gravity;
character.y += character.velocityY;
// Check for ground collision
let onGround = false;
for (const platform of platforms) {
if (character.y + character.height >= platform.y &&
character.y + character.height <= platform.y + platform.height &&
character.x + character.width > platform.x &&
character.x < platform.x + platform.width &&
character.velocityY > 0) {
character.y = platform.y - character.height;
character.velocityY = 0;
character.isJumping = false;
onGround = true;
}
}
// Check for enemy collisions
for (const enemy of enemies) {
if (checkCollision(character, enemy)) {
gameOver("You got hit by an enemy!");
return;
}
}
// Check for friend collision (win condition)
if (checkCollision(character, friend) && correctAnswers === totalQuestions) {
gameRunning = false;
winScreen.classList.remove('hidden');
return;
}
// Check for falling off the world
if (character.y > canvas.height) {
gameOver("You fell off the world!");
return;
}
// Update enemy positions
enemies.forEach(enemy => {
enemy.x += enemy.speed * enemy.direction;
// Simple enemy AI - turn around at edges
let enemyOnPlatform = false;
for (const platform of platforms) {
if (enemy.x >= platform.x && enemy.x + enemy.width <= platform.x + platform.width &&
enemy.y + enemy.height >= platform.y && enemy.y + enemy.height <= platform.y + platform.height) {
enemyOnPlatform = true;
// Check if enemy is at platform edge
if ((enemy.x <= platform.x && enemy.direction === -1) ||
(enemy.x + enemy.width >= platform.x + platform.width && enemy.direction === 1)) {
enemy.direction *= -1;
}
break;
}
}
if (!enemyOnPlatform) {
enemy.direction *= -1;
}
});
// Check for checkpoint collisions
checkpoints.forEach((checkpoint, index) => {
if (!checkpoint.quizTaken &&
character.x + character.width > checkpoint.x &&
character.x < checkpoint.x + 10) {
checkpoint.quizTaken = true;
showQuiz(index);
}
});
// Update camera (world scroll)
if (character.x > canvas.width / 2) {
world.scrollX = character.x - canvas.width / 2;
// Don't scroll past the end of the world
if (world.scrollX > world.width - canvas.width) {
world.scrollX = world.width - canvas.width;
}
} else {
world.scrollX = 0;
}
}
function render() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw sky background
ctx.fillStyle = '#6b8cff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw clouds (simple background elements)
ctx.fillStyle = '#ffffff';
ctx.fillRect(100 - world.scrollX / 3, 50, 60, 20);
ctx.fillRect(300 - world.scrollX / 3, 80, 80, 25);
ctx.fillRect(500 - world.scrollX / 3, 60, 70, 20);
ctx.fillRect(800 - world.scrollX / 3, 70, 90, 30);
// Draw platforms
ctx.fillStyle = '#8b4513'; // Brown
platforms.forEach(platform => {
ctx.fillRect(platform.x - world.scrollX, platform.y, platform.width, platform.height);
});
// Draw ground
ctx.fillStyle = '#556b2f'; // Dark green
ctx.fillRect(0, world.groundHeight, world.width, canvas.height - world.groundHeight);
// Draw checkpoints (flag markers)
ctx.fillStyle = '#ff0000';
checkpoints.forEach(checkpoint => {
if (!checkpoint.quizTaken) {
ctx.fillRect(checkpoint.x - world.scrollX, world.groundHeight - 60, 10, 60);
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.moveTo(checkpoint.x - world.scrollX + 10, world.groundHeight - 60);
ctx.lineTo(checkpoint.x - world.scrollX + 30, world.groundHeight - 40);
ctx.lineTo(checkpoint.x - world.scrollX + 10, world.groundHeight - 20);
ctx.fill();
ctx.fillStyle = '#ff0000';
}
});
// Draw enemies (simple pixel art)
ctx.fillStyle = '#ff0000';
enemies.forEach(enemy => {
// Simple enemy sprite - a red square with eyes
ctx.fillRect(enemy.x - world.scrollX, enemy.y, enemy.width, enemy.height);
// Eyes
ctx.fillStyle = '#ffffff';
ctx.fillRect(enemy.x - world.scrollX + 5, enemy.y + 5, 8, 8);
ctx.fillRect(enemy.x - world.scrollX + 19, enemy.y + 5, 8, 8);
ctx.fillStyle = '#000000';
ctx.fillRect(enemy.x - world.scrollX + 7, enemy.y + 7, 4, 4);
ctx.fillRect(enemy.x - world.scrollX + 21, enemy.y + 7, 4, 4);
ctx.fillStyle = '#ff0000';
});
// Draw friend (goal)
ctx.fillStyle = '#ffff00'; // Yellow
ctx.fillRect(friend.x - world.scrollX, friend.y, friend.width, friend.height);
// Face details
ctx.fillStyle = '#000000';
ctx.fillRect(friend.x - world.scrollX + 8, friend.y + 10, 4, 4); // Left eye
ctx.fillRect(friend.x - world.scrollX + 20, friend.y + 10, 4, 4); // Right eye
ctx.fillRect(friend.x - world.scrollX + 10, friend.y + 20, 12, 4); // Mouth
// Draw character (simple pixel art)
ctx.fillStyle = '#00ff00'; // Green
ctx.fillRect(character.x - world.scrollX, character.y, character.width, character.height);
// Face details (changes direction)
ctx.fillStyle = '#000000';
if (character.direction === 1) {
// Facing right
ctx.fillRect(character.x - world.scrollX + 8, character.y + 10, 4, 4); // Left eye
ctx.fillRect(character.x - world.scrollX + 20, character.y + 10, 4, 4); // Right eye
ctx.fillRect(character.x - world.scrollX + 10, character.y + 20, 12, 4); // Mouth
} else {
// Facing left
ctx.fillRect(character.x - world.scrollX + 8, character.y + 10, 4, 4); // Left eye
ctx.fillRect(character.x - world.scrollX + 20, character.y + 10, 4, 4); // Right eye
ctx.fillRect(character.x - world.scrollX + 10, character.y + 20, 12, 4); // Mouth
}
// Draw score
ctx.fillStyle = '#ffffff';
ctx.font = '20px Arial';
ctx.fillText(`Questions: ${correctAnswers}/${totalQuestions}`, 20, 30);
}
function checkCollision(obj1, obj2) {
return obj1.x < obj2.x + obj2.width &&
obj1.x + obj1.width > obj2.x &&
obj1.y < obj2.y + obj2.height &&
obj1.y + obj1.height > obj2.y;
}
function showQuiz(index) {
if (index >= questions.length) return;
gameRunning = false;
quizActive = true;
currentQuestion = questions[index];
quizQuestion.textContent = currentQuestion.question;
quizOptions.forEach((option, i) => {
option.textContent = currentQuestion.options[i];
option.classList.remove('correct', 'wrong');
});
quizFeedback.classList.add('hidden');
quizModal.classList.remove('hidden');
}
function checkAnswer(selectedOption) {
if (!currentQuestion) return;
const selectedAnswer = parseInt(selectedOption.dataset.answer) - 1;
const isCorrect = selectedAnswer === currentQuestion.correct;
if (isCorrect) {
selectedOption.classList.add('correct');
quizFeedback.textContent = "Correct! You remember your friend well.";
quizFeedback.classList.remove('hidden', 'text-red-500');
quizFeedback.classList.add('text-green-600');
correctAnswers++;
updateScoreDisplay();
// After a delay, hide the quiz and resume game
setTimeout(() => {
quizModal.classList.add('hidden');
quizActive = false;
gameRunning = true;
requestAnimationFrame(gameLoop);
}, 1500);
} else {
selectedOption.classList.add('wrong');
quizOptions[currentQuestion.correct].classList.add('correct');
quizFeedback.textContent = "Wrong! Try to remember better next time.";
quizFeedback.classList.remove('hidden', 'text-green-600');
quizFeedback.classList.add('text-red-500');
// After a delay, hide the quiz and resume game
setTimeout(() => {
quizModal.classList.add('hidden');
quizActive = false;
gameRunning = true;
requestAnimationFrame(gameLoop);
}, 1500);
}
}
function gameOver(message) {
gameRunning = false;
gameOverMessage.textContent = message;
gameOverScreen.classList.remove('hidden');
}
// Initialize with start screen visible
startScreen.classList.remove('hidden');
</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=WebashalarForML/friend-quest" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>