🎮ArcadeLab

Darts

by PlasmaGalaxy71
730 lines27.1 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Darts Game</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; touch-action: none; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #ffeaa7 0%, #fab1a0 50%, #ff7675 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            overflow-x: hidden;
        }
        .header {
            width: 100%;
            padding: 10px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            background: rgba(255,255,255,0.3);
            backdrop-filter: blur(10px);
        }
        .logo { font-size: 24px; font-weight: bold; color: #2d3436; }
        .mode-select { display: flex; gap: 10px; }
        .mode-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 20px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s;
            background: rgba(255,255,255,0.5);
            color: #2d3436;
        }
        .mode-btn.active { background: #fd79a8; color: white; }
        .game-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 20px;
            width: 100%;
            max-width: 800px;
        }
        .scoreboard {
            display: flex;
            justify-content: space-around;
            width: 100%;
            margin-bottom: 20px;
            gap: 20px;
        }
        .player-card {
            background: rgba(255,255,255,0.9);
            border-radius: 15px;
            padding: 15px 25px;
            text-align: center;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            min-width: 140px;
        }
        .player-card.current { border: 3px solid #fd79a8; }
        .player-name { font-size: 14px; color: #636e72; margin-bottom: 5px; }
        .player-score { font-size: 36px; font-weight: bold; color: #2d3436; }
        .dartboard-container {
            position: relative;
            width: 320px;
            height: 320px;
        }
        .dartboard {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            position: relative;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
        }
        .dartboard canvas { width: 100%; height: 100%; }
        .power-bar-container {
            width: 250px;
            height: 30px;
            background: rgba(255,255,255,0.8);
            border-radius: 15px;
            margin: 20px 0;
            overflow: hidden;
            position: relative;
            box-shadow: inset 0 2px 5px rgba(0,0,0,0.1);
        }
        .power-bar {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, #00b894, #fdcb6e, #e17055, #d63031);
            border-radius: 15px;
            transition: width 0.05s;
        }
        .power-text {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 12px;
            font-weight: bold;
            color: #2d3436;
        }
        .instructions {
            color: #2d3436;
            font-size: 14px;
            text-align: center;
            margin: 10px 0;
            opacity: 0.8;
        }
        .thrown-darts {
            position: absolute;
            top: 0; left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
        }
        .thrown-dart {
            position: absolute;
            width: 20px;
            height: 20px;
            transform: translate(-50%, -50%);
        }
        .darts-left {
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }
        .dart-icon {
            width: 30px;
            height: 30px;
            background: #636e72;
            border-radius: 50% 50% 50% 0;
            transform: rotate(-45deg);
            transition: all 0.3s;
        }
        .dart-icon.used { opacity: 0.3; }
        .banner-ad {
            width: 320px;
            height: 50px;
            background: linear-gradient(45deg, #a29bfe, #6c5ce7);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 12px;
            margin-top: 20px;
            box-shadow: 0 3px 10px rgba(108,92,231,0.3);
        }
        .modal {
            position: fixed;
            top: 0; left: 0;
            width: 100%; height: 100%;
            background: rgba(0,0,0,0.5);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 100;
            display: none;
        }
        .modal.show { display: flex; }
        .modal-content {
            background: white;
            border-radius: 20px;
            padding: 30px;
            text-align: center;
            max-width: 300px;
        }
        .modal-title { font-size: 24px; margin-bottom: 15px; color: #2d3436; }
        .modal-text { font-size: 16px; color: #636e72; margin-bottom: 20px; }
        .modal-btn {
            padding: 12px 30px;
            border: none;
            border-radius: 25px;
            background: #fd79a8;
            color: white;
            font-size: 16px;
            cursor: pointer;
            transition: all 0.3s;
        }
        .modal-btn:hover { transform: scale(1.05); background: #e84393; }
        .new-game-btn {
            padding: 10px 20px;
            border: none;
            border-radius: 20px;
            background: #00b894;
            color: white;
            font-size: 14px;
            cursor: pointer;
            margin-top: 15px;
        }
        .round-info {
            font-size: 12px;
            color: #636e72;
            margin-top: 5px;
        }
        @keyframes dartHit {
            0% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; }
            50% { transform: translate(-50%, -50%) scale(0.8); }
            100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
        }
        .dart-hit { animation: dartHit 0.3s ease-out; }
        @keyframes scorePopup {
            0% { transform: translateY(0) scale(1); opacity: 1; }
            100% { transform: translateY(-50px) scale(1.5); opacity: 0; }
        }
        .score-popup {
            position: absolute;
            font-size: 24px;
            font-weight: bold;
            color: #00b894;
            pointer-events: none;
            animation: scorePopup 1s ease-out forwards;
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="logo">🎯 Darts</div>
        <div class="mode-select">
            <button class="mode-btn active" data-mode="practice">Practice</button>
            <button class="mode-btn" data-mode="301">301</button>
        </div>
    </div>
    
    <div class="game-container">
        <div class="scoreboard">
            <div class="player-card" id="player1Card">
                <div class="player-name">You</div>
                <div class="player-score" id="player1Score">0</div>
                <div class="round-info">Round: <span id="p1Round">1</span></div>
            </div>
            <div class="player-card" id="player2Card">
                <div class="player-name">AI Opponent</div>
                <div class="player-score" id="player2Score">301</div>
                <div class="round-info">Round: <span id="p2Round">1</span></div>
            </div>
        </div>
        
        <div class="dartboard-container" id="dartboardContainer">
            <div class="dartboard">
                <canvas id="dartboard" width="640" height="640"></canvas>
            </div>
            <div class="thrown-darts" id="thrownDarts"></div>
        </div>
        
        <div class="power-bar-container">
            <div class="power-bar" id="powerBar"></div>
            <div class="power-text" id="powerText">Power: 0%</div>
        </div>
        
        <div class="instructions" id="instructions">Drag on dartboard to aim, release to throw!</div>
        
        <div class="darts-left" id="dartsLeft">
            <div class="dart-icon"></div>
            <div class="dart-icon"></div>
            <div class="dart-icon"></div>
        </div>
        
        <button class="new-game-btn" id="newGameBtn" style="display:none;">New Game</button>
        
        <div class="banner-ad">Advertisement Space - 320x50</div>
    </div>
    
    <div class="modal" id="gameOverModal">
        <div class="modal-content">
            <div class="modal-title" id="modalTitle">Game Over!</div>
            <div class="modal-text" id="modalText">You Win!</div>
            <button class="modal-btn" id="playAgainBtn">Play Again</button>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('dartboard');
        const ctx = canvas.getContext('2d');
        const container = document.getElementById('dartboardContainer');
        
        const COLORS = {
            singleOuter: ['#2d3436', '#d63031'],
            singleInner: ['#d63031', '#2d3436'],
            triple: ['#00b894', '#2d3436'],
            double: ['#d63031', '#2d3436'],
            bull: '#00b894',
            bullseye: '#d63031',
            board: '#0a0a0a'
        };
        
        let gameMode = 'practice';
        let isDragging = false;
        let aimX = 160, aimY = 160;
        let power = 0;
        let powerDirection = 1;
        let powerInterval = null;
        let dartsThrown = 0;
        let currentPlayer = 1;
        let player1Score = 0;
        let player2Score = 301;
        let aiHitChance = 0.7;
        let thrownDarts = [];
        let roundNumber = { 1: 1, 2: 1 };
        
        function drawDartboard() {
            const scale = canvas.width / 320;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // Background
            ctx.fillStyle = '#f5f5f5';
            ctx.beginPath();
            ctx.arc(160*scale, 160*scale, 165*scale, 0, Math.PI * 2);
            ctx.fill();
            
            // Double ring (outer)
            drawRing(160*scale, 160*scale, 160*scale, 150*scale, COLORS.double);
            
            // Outer single ring
            drawRing(160*scale, 160*scale, 150*scale, 105*scale, COLORS.singleOuter);
            
            // Triple ring
            drawRing(160*scale, 160*scale, 105*scale, 95*scale, COLORS.triple);
            
            // Inner single ring
            drawRing(160*scale, 160*scale, 95*scale, 30*scale, COLORS.singleInner);
            
            // Outer bull
            ctx.fillStyle = COLORS.bull;
            ctx.beginPath();
            ctx.arc(160*scale, 160*scale, 30*scale, 0, Math.PI * 2);
            ctx.fill();
            
            // Inner bull (bullseye)
            ctx.fillStyle = COLORS.bullseye;
            ctx.beginPath();
            ctx.arc(160*scale, 160*scale, 12*scale, 0, Math.PI * 2);
            ctx.fill();
            
            // Wire lines
            ctx.strokeStyle = '#888';
            ctx.lineWidth = 1;
            for (let i = 0; i < 20; i++) {
                const angle = (i * 18 - 90) * Math.PI / 180;
                ctx.beginPath();
                ctx.moveTo(160*scale, 160*scale);
                ctx.lineTo(160*scale + Math.cos(angle) * 160*scale, 160*scale + Math.sin(angle) * 160*scale);
                ctx.stroke();
            }
            
            // Numbers
            ctx.fillStyle = '#fff';
            ctx.font = `bold ${14*scale}px Arial`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            const numbers = [20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5];
            for (let i = 0; i < 20; i++) {
                const angle = (i * 18 - 90) * Math.PI / 180;
                const x = 160*scale + Math.cos(angle) * 170*scale;
                const y = 160*scale + Math.sin(angle) * 170*scale;
                ctx.fillText(numbers[i].toString(), x, y);
            }
            
            // Aim indicator
            if (isDragging || gameMode === 'practice') {
                ctx.fillStyle = 'rgba(253, 121, 168, 0.8)';
                ctx.beginPath();
                ctx.arc(aimX*scale, aimY*scale, 8*scale, 0, Math.PI * 2);
                ctx.fill();
                ctx.strokeStyle = '#fff';
                ctx.lineWidth = 2;
                ctx.stroke();
            }
        }
        
        function drawRing(cx, cy, outerR, innerR, colors) {
            for (let i = 0; i < 20; i++) {
                const startAngle = (i * 18 - 90) * Math.PI / 180;
                const endAngle = ((i + 1) * 18 - 90) * Math.PI / 180;
                
                ctx.fillStyle = colors[i % 2];
                ctx.beginPath();
                ctx.arc(cx, cy, outerR, startAngle, endAngle);
                ctx.arc(cx, cy, innerR, endAngle, startAngle, true);
                ctx.closePath();
                ctx.fill();
            }
        }
        
        function calculateScore(x, y) {
            const scale = canvas.width / 320;
            x *= scale; y *= scale;
            const cx = 160*scale, cy = 160*scale;
            const dx = x - cx, dy = y - cy;
            const distance = Math.sqrt(dx*dx + dy*dy);
            let angle = Math.atan2(dy, dx) * 180 / Math.PI + 90;
            if (angle < 0) angle += 360;
            
            const numbers = [20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5];
            const segment = Math.floor((angle + 9) / 18) % 20;
            let baseScore = numbers[segment];
            
            let multiplier = 1;
            const outerR = 160*scale, innerR = 150*scale, tripleIn = 105*scale, tripleOut = 95*scale, bullR = 30*scale, bullseyeR = 12*scale;
            
            if (distance <= bullseyeR) return { score: 50, label: 'BULLSEYE!' };
            if (distance <= bullR) return { score: 25, label: 'BULL' };
            if (distance >= tripleOut && distance <= innerR) { multiplier = 3; baseScore = numbers[(segment + 1) % 20]; }
            if (distance >= tripleIn && distance <= tripleOut) multiplier = 3;
            if (distance >= outerR) multiplier = 2;
            
            return { score: baseScore * multiplier, label: multiplier > 1 ? `${baseScore} x${multiplier}` : baseScore.toString() };
        }
        
        function addRandomOffset(x, y, power) {
            const maxOffset = 15 * (1 - power * 0.5);
            const angle = Math.random() * Math.PI * 2;
            const offset = Math.random() * maxOffset;
            return {
                x: Math.max(10, Math.min(310, x + Math.cos(angle) * offset)),
                y: Math.max(10, Math.min(310, y + Math.sin(angle) * offset))
            };
        }
        
        function throwDart(targetX, targetY, isPlayer = true) {
            const pos = addRandomOffset(targetX, targetY, power);
            const result = calculateScore(pos.x, pos.y);
            
            // Add dart to board
            const dartEl = document.createElement('div');
            dartEl.className = 'thrown-dart dart-hit';
            dartEl.style.left = `${(pos.x / 320) * 100}%`;
            dartEl.style.top = `${(pos.y / 320) * 100}%`;
            dartEl.innerHTML = '🎯';
            document.getElementById('thrownDarts').appendChild(dartEl);
            thrownDarts.push({ x: pos.x, y: pos.y, player: currentPlayer });
            
            // Score popup
            const popup = document.createElement('div');
            popup.className = 'score-popup';
            popup.textContent = `+${result.score}`;
            popup.style.left = `${(pos.x / 320) * 100}%`;
            popup.style.top = `${(pos.y / 320) * 100}%`;
            document.getElementById('thrownDarts').appendChild(popup);
            setTimeout(() => popup.remove(), 1000);
            
            return result;
        }
        
        function updateScore(result) {
            if (gameMode === 'practice') {
                player1Score += result.score;
                document.getElementById('player1Score').textContent = player1Score;
            } else {
                if (currentPlayer === 1) {
                    const newScore = player2Score - result.score;
                    if (newScore === 0) {
                        showGameOver(1);
                    } else if (newScore < 0) {
                        document.getElementById('instructions').textContent = 'Bust! Score reset.';
                        player2Score = 301;
                        document.getElementById('player2Score').textContent = player2Score;
                    } else {
                        player2Score = newScore;
                        document.getElementById('player2Score').textContent = player2Score;
                    }
                } else {
                    const newScore = player1Score - result.score;
                    if (newScore === 0) {
                        showGameOver(2);
                    } else if (newScore < 0) {
                        player1Score = 301;
                        document.getElementById('player1Score').textContent = player1Score;
                    } else {
                        player1Score = newScore;
                        document.getElementById('player1Score').textContent = player1Score;
                    }
                }
            }
        }
        
        function nextTurn() {
            dartsThrown++;
            updateDartsDisplay();
            
            if (gameMode === 'practice') {
                if (dartsThrown >= 3) {
                    setTimeout(resetRound, 500);
                }
            } else {
                if (dartsThrown >= 3) {
                    if (currentPlayer === 2) {
                        roundNumber[2]++;
                        document.getElementById('p2Round').textContent = roundNumber[2];
                    }
                    setTimeout(() => {
                        currentPlayer = 1;
                        updateCurrentPlayer();
                        resetRound();
                    }, 500);
                }
            }
        }
        
        function resetRound() {
            dartsThrown = 0;
            document.getElementById('thrownDarts').innerHTML = '';
            thrownDarts = [];
            updateDartsDisplay();
            power = 0;
            updatePowerBar();
            
            if (gameMode === 'practice') {
                document.getElementById('instructions').textContent = 'Drag on dartboard to aim, release to throw!';
            }
        }
        
        function updateDartsDisplay() {
            const darts = document.querySelectorAll('.dart-icon');
            darts.forEach((dart, i) => {
                dart.classList.toggle('used', i < dartsThrown);
            });
        }
        
        function updateCurrentPlayer() {
            document.getElementById('player1Card').classList.toggle('current', currentPlayer === 1);
            document.getElementById('player2Card').classList.toggle('current', currentPlayer === 2);
            document.getElementById('instructions').textContent = currentPlayer === 1 
                ? 'Your turn! Drag to aim and throw.' 
                : 'AI is throwing...';
        }
        
        function aiTurn() {
            document.getElementById('instructions').textContent = 'AI is throwing...';
            
            let aiDarts = 0;
            const aiInterval = setInterval(() => {
                if (aiDarts >= 3) {
                    clearInterval(aiInterval);
                    currentPlayer = 1;
                    roundNumber[1]++;
                    document.getElementById('p1Round').textContent = roundNumber[1];
                    updateCurrentPlayer();
                    resetRound();
                    return;
                }
                
                setTimeout(() => {
                    if (Math.random() < aiHitChance) {
                        // Hit - aim for a good score
                        const angles = [0, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198, 216, 234, 252, 270, 288, 306, 324, 342];
                        const targetAngle = angles[Math.floor(Math.random() * angles.length)] * Math.PI / 180 - Math.PI/2;
                        const rings = [0.3, 0.5, 0.7, 0.9];
                        const targetDist = rings[Math.floor(Math.random() * rings.length)] * 140;
                        const targetX = 160 + Math.cos(targetAngle) * targetDist;
                        const targetY = 160 + Math.sin(targetAngle) * targetDist;
                        throwDart(targetX, targetY, false);
                    } else {
                        // Miss
                        const missX = Math.random() < 0.5 ? Math.random() * 80 : 240 + Math.random() * 80;
                        const missY = Math.random() < 0.5 ? Math.random() * 80 : 240 + Math.random() * 80;
                        throwDart(missX, missY, false);
                    }
                    
                    const lastDart = thrownDarts[thrownDarts.length - 1];
                    const result = calculateScore(lastDart.x, lastDart.y);
                    updateScore(result);
                    
                    aiDarts++;
                }, 300);
            }, 1000);
        }
        
        function showGameOver(winner) {
            const modal = document.getElementById('gameOverModal');
            const title = document.getElementById('modalTitle');
            const text = document.getElementById('modalText');
            
            if (winner === 1) {
                title.textContent = '🎉 Victory!';
                text.textContent = gameMode === '301' ? 'You won the game!' : 'Great practice session!';
            } else {
                title.textContent = '😢 Game Over';
                text.textContent = 'AI wins this round!';
            }
            
            modal.classList.add('show');
        }
        
        function updatePowerBar() {
            document.getElementById('powerBar').style.width = `${power * 100}%`;
            document.getElementById('powerText').textContent = `Power: ${Math.round(power * 100)}%`;
        }
        
        function startPowerBar() {
            power = 0;
            powerDirection = 1;
            if (powerInterval) clearInterval(powerInterval);
            powerInterval = setInterval(() => {
                power += powerDirection * 0.02;
                if (power >= 1) { power = 1; powerDirection = -1; }
                if (power <= 0) { power = 0; powerDirection = 1; }
                updatePowerBar();
            }, 50);
        }
        
        function stopPowerBar() {
            if (powerInterval) {
                clearInterval(powerInterval);
                powerInterval = null;
            }
        }
        
        function getEventPos(e) {
            const rect = container.getBoundingClientRect();
            const clientX = e.touches ? e.touches[0].clientX : e.clientX;
            const clientY = e.touches ? e.touches[0].clientY : e.clientY;
            return {
                x: (clientX - rect.left) / rect.width * 320,
                y: (clientY - rect.top) / rect.height * 320
            };
        }
        
        function handleStart(e) {
            if (currentPlayer !== 1 || gameMode === '301' && player2Score === 0) return;
            e.preventDefault();
            isDragging = true;
            const pos = getEventPos(e);
            aimX = pos.x;
            aimY = pos.y;
            startPowerBar();
            drawDartboard();
        }
        
        function handleMove(e) {
            if (!isDragging) return;
            e.preventDefault();
            const pos = getEventPos(e);
            aimX = Math.max(0, Math.min(320, pos.x));
            aimY = Math.max(0, Math.min(320, pos.y));
            drawDartboard();
        }
        
        function handleEnd(e) {
            if (!isDragging) return;
            isDragging = false;
            stopPowerBar();
            
            const result = throwDart(aimX, aimY, true);
            updateScore(result);
            nextTurn();
            
            if (gameMode === '301' && currentPlayer === 1 && dartsThrown < 3 && player2Score > 0) {
                setTimeout(() => {
                    currentPlayer = 2;
                    updateCurrentPlayer();
                    setTimeout(aiTurn, 500);
                }, 300);
            }
            
            drawDartboard();
        }
        
        // Event listeners
        container.addEventListener('mousedown', handleStart);
        container.addEventListener('mousemove', handleMove);
        container.addEventListener('mouseup', handleEnd);
        container.addEventListener('mouseleave', handleEnd);
        container.addEventListener('touchstart', handleStart, { passive: false });
        container.addEventListener('touchmove', handleMove, { passive: false });
        container.addEventListener('touchend', handleEnd);
        
        // Mode buttons
        document.querySelectorAll('.mode-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
                btn.classList.add('active');
                gameMode = btn.dataset.mode;
                startNewGame();
            });
        });
        
        // New game button
        document.getElementById('newGameBtn').addEventListener('click', startNewGame);
        document.getElementById('playAgainBtn').addEventListener('click', () => {
            document.getElementById('gameOverModal').classList.remove('show');
            startNewGame();
        });
        
        function startNewGame() {
            player1Score = 0;
            player2Score = 301;
            currentPlayer = 1;
            dartsThrown = 0;
            thrownDarts = [];
            roundNumber = { 1: 1, 2: 1 };
            power = 0;
            
            document.getElementById('player1Score').textContent = player1Score;
            document.getElementById('player2Score').textContent = player2Score;
            document.getElementById('p1Round').textContent = '1';
            document.getElementById('p2Round').textContent = '1';
            document.getElementById('thrownDarts').innerHTML = '';
            document.getElementById('newGameBtn').style.display = 'none';
            
            updateDartsDisplay();
            updatePowerBar();
            updateCurrentPlayer();
            drawDartboard();
        }
        
        // Initialize
        drawDartboard();
        updateCurrentPlayer();
        
        // Animate aim in practice mode
        if (gameMode === 'practice') {
            aimX = 160; aimY = 160;
            setInterval(() => {
                if (!isDragging && gameMode === 'practice') {
                    aimX = 160 + Math.sin(Date.now() / 1000) * 50;
                    aimY = 160 + Math.cos(Date.now() / 800) * 50;
                    drawDartboard();
                }
            }, 50);
        }
    </script>
</body>
</html>

Game Source: Darts

Creator: PlasmaGalaxy71

Libraries: none

Complexity: complex (730 lines, 27.1 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: darts-plasmagalaxy71" to link back to the original. Then publish at arcadelab.ai/publish.