Darts
by PlasmaGalaxy71730 lines27.1 KB
<!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.