🎮ArcadeLab

Algebra Adventure: Candy Kingdom Rescue

by AtomicBolt10
377 lines13.4 KB
▶ Play
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Algebra Adventure: Candy Kingdom Rescue</title>
    <style>
        /* === COLOR PALETTE & FONTS === */
        @import url('https://fonts.googleapis.com/css2?family=Fredoka+One&family=Nunito:wght@400;700&display=swap');
        
        :root {
            --bg-candy: linear-gradient(135deg, #FFB7B2 0%, #E2F0CB 100%);
            --glass-bg: rgba(255, 255, 255, 0.4);
            --glass-border: rgba(255, 255, 255, 0.6);
            --btn-glow: 0 0 15px rgba(255, 154, 162, 0.8);
            --text-dark: #6B5B95;
        }

        * { box-sizing: border-box; margin: 0; padding: 0; }

        body {
            background: var(--bg-candy);
            font-family: 'Nunito', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            overflow: hidden;
        }

        /* === GAME CONTAINER (16:9) === */
        #game-container {
            width: 100%;
            max-width: 900px;
            aspect-ratio: 16/9;
            background: url('https://www.transparenttextures.com/patterns/stardust.png'), radial-gradient(circle, #FFDAC1 0%, #FFB7B2 100%);
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.2);
            position: relative;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        .screen {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            transition: opacity 0.5s ease;
            padding: 20px;
            text-align: center;
        }

        .hidden { opacity: 0; pointer-events: none; z-index: -1; }
        .active { opacity: 1; z-index: 10; }

        /* === UI ELEMENTS === */
        h1, h2 { font-family: 'Fredoka One', cursive; color: #FF9AA2; text-shadow: 2px 2px 0px #fff; letter-spacing: 2px; }
        
        .glass-panel {
            background: var(--glass-bg);
            backdrop-filter: blur(10px);
            border: 2px solid var(--glass-border);
            border-radius: 20px;
            padding: 20px;
            box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
        }

        .btn {
            background: linear-gradient(45deg, #FF9AA2, #FFDAC1);
            border: none;
            border-radius: 30px;
            padding: 12px 25px;
            font-size: 1.2rem;
            font-family: 'Fredoka One', cursive;
            color: white;
            cursor: pointer;
            margin: 10px;
            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
            text-shadow: 1px 1px 0px rgba(0,0,0,0.2);
            box-shadow: 0 4px 10px rgba(255, 154, 162, 0.5);
        }

        .btn:hover {
            transform: translateY(-5px) scale(1.05);
            box-shadow: var(--btn-glow);
        }

        /* === HUD (Heads Up Display) === */
        #hud {
            position: absolute;
            top: 15px; left: 15px; right: 15px;
            display: flex;
            justify-content: space-between;
            font-family: 'Fredoka One', cursive;
            color: var(--text-dark);
            z-index: 20;
        }

        .hud-box {
            background: rgba(255,255,255,0.7);
            padding: 5px 15px;
            border-radius: 20px;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        /* === CHARACTER & ANIMATIONS === */
        .character {
            font-size: 80px;
            animation: float 3s ease-in-out infinite;
            filter: drop-shadow(0 10px 10px rgba(0,0,0,0.2));
            margin-bottom: 10px;
        }

        @keyframes float {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(-15px); }
        }

        @keyframes shake {
            0%, 100% { transform: translateX(0); }
            20%, 60% { transform: translateX(-10px); }
            40%, 80% { transform: translateX(10px); }
        }

        .shake { animation: shake 0.5s; }

        /* === QUIZ LAYOUT === */
        #options { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; width: 100%; max-width: 600px; margin-top: 20px;}
        .dialog-box { font-size: 1.2rem; color: var(--text-dark); margin-bottom: 20px; font-weight: bold; }
        
        /* Particle / Confetti Layer */
        #particles { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 100;}
    </style>
</head>
<body>

<div id="game-container">
    <!-- HUD Layer -->
    <div id="hud" class="hidden">
        <div class="hud-box" id="health-bar">❤️❤️❤️</div>
        <div class="hud-box">⏳ <span id="timer">30</span>s</div>
        <div class="hud-box">💎 <span id="score">0</span></div>
    </div>

    <!-- Intro Screen -->
    <div id="intro-screen" class="screen active">
        <div class="character">🧙‍♂️🧸</div>
        <h1>Algebra Adventure</h1>
        <div class="glass-panel" style="margin: 20px 0;">
            <p style="color: var(--text-dark); font-weight: bold;">Candy Kingdom Diserang Monster Aljabar!</p>
            <p style="color: var(--text-dark);">Bantu Milo si petualang kecil menyelesaikan misi matematika untuk mendapatkan Crystal Power!</p>
        </div>
        <button class="btn" onclick="startGame()">▶ Start Game</button>
    </div>

    <!-- Quiz Screen -->
    <div id="quiz-screen" class="screen hidden">
        <div class="character" id="milo-sprite">🧙‍♂️🧸</div>
        <div class="glass-panel dialog-box" id="question-text">Loading question...</div>
        <div id="options">
            <!-- Buttons generated via JS -->
        </div>
    </div>

    <!-- Game Over Screen -->
    <div id="game-over-screen" class="screen hidden">
        <div class="character">😿</div>
        <h1 style="color: #FF6B6B;">Game Over!</h1>
        <p class="dialog-box">Candy Kingdom masih dalam bahaya. Ayo coba lagi, Milo!</p>
        <button class="btn" onclick="location.reload()">🔄 Restart</button>
    </div>

    <!-- Victory Screen -->
    <div id="victory-screen" class="screen hidden">
        <div class="character">🎉🧙‍♂️✨</div>
        <h1 style="color: #A8E6CF;">Victory!</h1>
        <p class="dialog-box">Kamu berhasil menyelamatkan Candy Kingdom!</p>
        <div class="glass-panel" style="margin-bottom: 20px;">
            <p>Total Crystal: 💎 <span id="final-score">0</span></p>
            <p>Sisa Nyawa: <span id="final-health"></span></p>
        </div>
        <button class="btn" onclick="location.reload()">🔄 Main Lagi</button>
    </div>

    <div id="particles"></div>
</div>

<script>
    // --- DATA SOAL MATEMATIKA (Kontekstual SMP) ---
    const questions = [
        {
            q: "Milo ingin membeli 3 Ramuan Permen (x). Koin Milo awalnya 50, dan kini sisa 20. Persamaan aljabar yang tepat adalah?",
            opts: ["50 - 3x = 20", "50 + 3x = 20", "3x - 50 = 20", "20 - 3x = 50"],
            ans: 0
        },
        {
            q: "Di hutan, Milo menemukan suku sejenis! Bentuk paling sederhana dari 4a + 3b - 2a + b adalah?",
            opts: ["2a + 2b", "6a + 4b", "2a + 4b", "4a - 2b"],
            ans: 2
        },
        {
            q: "Tongkat crystal Milo memiliki kekuatan sihir 5y - 7. Jika energi batu crystal (y) bernilai 4, berapa total kekuatan Milo?",
            opts: ["12", "13", "14", "15"],
            ans: 1
        },
        {
            q: "Boss Monster Aljabar memiliki HP = 100. Milo menyerang dengan pedang '2p + 10'. Jika p = 20, berapa sisa HP monster?",
            opts: ["0", "40", "50", "60"],
            ans: 2
        }
    ];

    // --- GAME STATE ---
    let currentLevel = 0;
    let score = 0;
    let health = 3;
    let timerInterval;
    let timeLeft = 30;

    // --- DOM ELEMENTS ---
    const screens = {
        intro: document.getElementById('intro-screen'),
        quiz: document.getElementById('quiz-screen'),
        gameOver: document.getElementById('game-over-screen'),
        victory: document.getElementById('victory-screen')
    };
    const hud = document.getElementById('hud');
    const healthBar = document.getElementById('health-bar');
    const timerDisplay = document.getElementById('timer');
    const scoreDisplay = document.getElementById('score');
    const questionText = document.getElementById('question-text');
    const optionsContainer = document.getElementById('options');
    const miloSprite = document.getElementById('milo-sprite');

    // --- FUNCTIONS ---
    function switchScreen(screenKey) {
        Object.values(screens).forEach(s => s.classList.remove('active'));
        Object.values(screens).forEach(s => s.classList.add('hidden'));
        screens[screenKey].classList.remove('hidden');
        screens[screenKey].classList.add('active');
    }

    function startGame() {
        switchScreen('quiz');
        hud.classList.remove('hidden');
        loadQuestion();
    }

    function loadQuestion() {
        if (currentLevel >= questions.length) {
            winGame();
            return;
        }

        miloSprite.textContent = "🧙‍♂️🧸"; // Reset animasi idle
        timeLeft = 30; // Reset Timer per soal
        timerDisplay.textContent = timeLeft;
        clearInterval(timerInterval);
        
        timerInterval = setInterval(() => {
            timeLeft--;
            timerDisplay.textContent = timeLeft;
            if (timeLeft <= 0) {
                clearInterval(timerInterval);
                takeDamage();
            }
        }, 1000);

        const currentQ = questions[currentLevel];
        questionText.textContent = `Level ${currentLevel + 1}: ${currentQ.q}`;
        optionsContainer.innerHTML = '';

        currentQ.opts.forEach((opt, index) => {
            const btn = document.createElement('button');
            btn.className = 'btn glass-panel';
            btn.style.color = 'var(--text-dark)';
            btn.textContent = opt;
            btn.onclick = () => checkAnswer(index);
            optionsContainer.appendChild(btn);
        });
    }

    function checkAnswer(selectedIndex) {
        clearInterval(timerInterval);
        const correctIndex = questions[currentLevel].ans;

        if (selectedIndex === correctIndex) {
            // BENAR
            miloSprite.textContent = "✨🤩✨";
            score += 100 + (timeLeft * 2); // Bonus waktu
            scoreDisplay.textContent = score;
            createConfetti();
            setTimeout(() => {
                currentLevel++;
                loadQuestion();
            }, 1500);
        } else {
            // SALAH
            takeDamage();
        }
    }

    function takeDamage() {
        miloSprite.textContent = "🤕😿";
        screens.quiz.classList.add('shake');
        setTimeout(() => screens.quiz.classList.remove('shake'), 500);
        
        health--;
        updateHealthBar();

        if (health <= 0) {
            setTimeout(loseGame, 1000);
        } else {
            setTimeout(loadQuestion, 1500); // Reload current question
        }
    }

    function updateHealthBar() {
        healthBar.textContent = '❤️'.repeat(health) + '🖤'.repeat(3 - health);
    }

    function winGame() {
        hud.classList.add('hidden');
        switchScreen('victory');
        document.getElementById('final-score').textContent = score;
        document.getElementById('final-health').textContent = '❤️'.repeat(health);
        createConfetti();
        createConfetti(); // Double confetti for win!
    }

    function loseGame() {
        hud.classList.add('hidden');
        switchScreen('gameOver');
    }

    // --- VISUAL EFFECTS ---
    function createConfetti() {
        const colors = ['#FF9AA2', '#FFB7B2', '#FFDAC1', '#E2F0CB', '#B5EAD7', '#C7CEEA'];
        const particlesContainer = document.getElementById('particles');
        
        for(let i=0; i<30; i++) {
            const particle = document.createElement('div');
            particle.style.position = 'absolute';
            particle.style.width = '10px';
            particle.style.height = '10px';
            particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
            particle.style.borderRadius = '50%';
            
            // Random start position near character
            particle.style.left = '50%';
            particle.style.top = '30%';
            
            const destX = (Math.random() - 0.5) * 300;
            const destY = (Math.random() - 0.5) * 300;
            
            particle.animate([
                { transform: 'translate(0, 0) scale(1)', opacity: 1 },
                { transform: `translate(${destX}px, ${destY}px) scale(0)`, opacity: 0 }
            ], {
                duration: 1000 + Math.random() * 500,
                easing: 'cubic-bezier(0, .9, .57, 1)'
            });
            
            particlesContainer.appendChild(particle);
            setTimeout(() => particle.remove(), 1500);
        }
    }
</script>

</body>
</html>

Game Source: Algebra Adventure: Candy Kingdom Rescue

Creator: AtomicBolt10

Libraries: none

Complexity: complex (377 lines, 13.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: algebra-adventure-candy-kingdom-rescue-atomicbolt10" to link back to the original. Then publish at arcadelab.ai/publish.