ex-7 / index.html
Mankeysocks's picture
Update index.html
6fb5614 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Metal Detection Minigame</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
body {
font-family: 'Orbitron', monospace;
background: radial-gradient(ellipse at center, #0a0a0a 0%, #000000 100%);
overflow: hidden;
}
.radar-container {
position: relative;
width: 320px;
height: 320px;
background: radial-gradient(circle at center, rgba(16, 185, 129, 0.1) 0%, rgba(0, 0, 0, 0.8) 70%),
repeating-radial-gradient(circle at center,
transparent 0,
transparent 39px,
rgba(16, 185, 129, 0.3) 40px,
rgba(16, 185, 129, 0.3) 41px);
border: 3px solid #10b981;
box-shadow: 0 0 50px rgba(16, 185, 129, 0.5),
inset 0 0 50px rgba(16, 185, 129, 0.2);
animation: radarPulse 2s ease-in-out infinite;
}
@keyframes radarPulse {
0%, 100% { box-shadow: 0 0 50px rgba(16, 185, 129, 0.5), inset 0 0 50px rgba(16, 185, 129, 0.2); }
50% { box-shadow: 0 0 80px rgba(16, 185, 129, 0.8), inset 0 0 80px rgba(16, 185, 129, 0.4); }
}
.sweep-line {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
height: 2px;
background: linear-gradient(to right, #10b981, transparent);
transform-origin: left center;
box-shadow: 0 0 10px #10b981, 0 0 20px #10b981;
animation: sweepRotate 4s linear infinite;
}
.sweep-line::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to right, rgba(16, 185, 129, 0.8), transparent);
filter: blur(3px);
}
.blip {
position: absolute;
width: 12px;
height: 12px;
background: radial-gradient(circle, #ef4444 0%, #dc2626 70%, transparent 100%);
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 20px #ef4444, 0 0 40px #ef4444;
animation: blipGlow 1.5s ease-in-out infinite;
}
@keyframes blipGlow {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-50%, -50%) scale(1.3);
opacity: 0.7;
}
}
.locked-blip {
background: radial-gradient(circle, #10b981 0%, #059669 70%, transparent 100%);
animation: lockedGlow 1s ease-in-out infinite;
box-shadow: 0 0 30px #10b981, 0 0 60px #10b981;
}
@keyframes lockedGlow {
0%, 100% {
transform: translate(-50%, -50%) scale(1) rotate(0deg);
filter: brightness(1);
}
50% {
transform: translate(-50%, -50%) scale(1.2) rotate(180deg);
filter: brightness(1.5);
}
}
.hit-effect {
position: absolute;
width: 60px;
height: 60px;
border: 3px solid #10b981;
border-radius: 50%;
transform: translate(-50%, -50%);
animation: hitRipple 0.6s ease-out forwards;
}
@keyframes hitRipple {
0% {
transform: translate(-50%, -50%) scale(0.5);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(3);
opacity: 0;
}
}
.score-display {
font-size: 1.5rem;
font-weight: 700;
text-shadow: 0 0 10px #10b981;
letter-spacing: 2px;
}
.instructions {
font-size: 0.9rem;
opacity: 0.8;
letter-spacing: 1px;
}
.key-hint {
color: #10b981;
font-weight: 700;
text-shadow: 0 0 5px #10b981;
animation: keyPulse 2s ease-in-out infinite;
}
@keyframes keyPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.hud-element {
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(16, 185, 129, 0.3);
box-shadow: 0 0 20px rgba(16, 185, 129, 0.2);
}
.grid-line {
position: absolute;
background: rgba(16, 185, 129, 0.2);
}
.grid-line.horizontal {
width: 100%;
height: 1px;
top: 50%;
transform: translateY(-50%);
}
.grid-line.vertical {
width: 1px;
height: 100%;
left: 50%;
transform: translateX(-50%);
}
.angle-markers {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.angle-marker {
position: absolute;
width: 2px;
height: 10px;
background: rgba(16, 185, 129, 0.5);
transform-origin: center 160px;
left: 50%;
top: 0;
}
</style>
</head>
<body class="flex flex-col items-center justify-center min-h-screen text-green-400">
<div class="absolute top-4 left-4 hud-element p-4 rounded-lg">
<div class="text-sm opacity-70">METAL DETECTOR</div>
<div class="text-lg font-bold">Model XR-7</div>
</div>
<div class="absolute top-4 right-4 hud-element p-4 rounded-lg">
<div class="text-sm opacity-70">FREQUENCY</div>
<div class="text-lg font-bold">14.5 kHz</div>
</div>
<div class="flex flex-col items-center">
<h1 class="text-4xl font-bold mb-2 tracking-wider">METAL DETECTION</h1>
<p class="instructions mb-8 text-center">Press <span class="key-hint">E</span> when the sweep line crosses a <span class="text-red-400">red blip</span></p>
<div class="relative radar-container rounded-full">
<!-- Grid lines -->
<div class="grid-line horizontal"></div>
<div class="grid-line vertical"></div>
<!-- Angle markers -->
<div class="angle-markers">
<div class="angle-marker" style="transform: rotate(0deg)"></div>
<div class="angle-marker" style="transform: rotate(30deg)"></div>
<div class="angle-marker" style="transform: rotate(60deg)"></div>
<div class="angle-marker" style="transform: rotate(90deg)"></div>
<div class="angle-marker" style="transform: rotate(120deg)"></div>
<div class="angle-marker" style="transform: rotate(150deg)"></div>
<div class="angle-marker" style="transform: rotate(180deg)"></div>
<div class="angle-marker" style="transform: rotate(210deg)"></div>
<div class="angle-marker" style="transform: rotate(240deg)"></div>
<div class="angle-marker" style="transform: rotate(270deg)"></div>
<div class="angle-marker" style="transform: rotate(300deg)"></div>
<div class="angle-marker" style="transform: rotate(330deg)"></div>
</div>
<!-- Scanning circles -->
<div class="absolute inset-8 rounded-full border border-green-500 opacity-20"></div>
<div class="absolute inset-16 rounded-full border border-green-500 opacity-20"></div>
<div class="absolute inset-24 rounded-full border border-green-500 opacity-20"></div>
<!-- Radar sweep line -->
<div class="sweep-line" id="sweepLine"></div>
<!-- Blips will be added here dynamically -->
<div id="blipsContainer"></div>
<!-- Hit effects -->
<div id="hitEffects"></div>
</div>
<div class="mt-8 flex items-center space-x-8">
<div class="hud-element px-6 py-3 rounded-lg">
<div class="text-sm opacity-70">TARGETS LOCKED</div>
<div class="score-display text-3xl" id="scoreDisplay">0/3</div>
</div>
<div class="hud-element px-6 py-3 rounded-lg">
<div class="text-sm opacity-70">ACCURACY</div>
<div class="score-display text-3xl" id="accuracyDisplay">0%</div>
</div>
</div>
<div class="mt-4 hud-element px-6 py-3 rounded-lg">
<div id="statusDisplay" class="text-lg">Scanning for metals...</div>
</div>
</div>
<audio id="hitSound" preload="auto">
<source src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU..." type="audio/wav">
</audio>
<audio id="missSound" preload="auto">
<source src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU..." type="audio/wav">
</audio>
<audio id="successSound" preload="auto">
<source src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU..." type="audio/wav">
</audio>
<script>
// Game variables
let score = 0;
let gameActive = true;
let blips = [];
let lockedBlips = [];
let sweepAngle = 0;
let totalAttempts = 0;
let successfulHits = 0;
// Game constants
const TOTAL_TARGETS = 3;
const SWEEP_SPEED = 4; // seconds per rotation
const HIT_TOLERANCE = 6; // degrees
// DOM elements
const sweepLine = document.getElementById('sweepLine');
const blipsContainer = document.getElementById('blipsContainer');
const scoreDisplay = document.getElementById('scoreDisplay');
const statusDisplay = document.getElementById('statusDisplay');
const accuracyDisplay = document.getElementById('accuracyDisplay');
const hitEffects = document.getElementById('hitEffects');
const hitSound = document.getElementById('hitSound');
const missSound = document.getElementById('missSound');
const successSound = document.getElementById('successSound');
// Create oscilloscope-style audio using Web Audio API
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
function playHitSound() {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.3);
}
function playMissSound() {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(200, audioContext.currentTime);
oscillator.type = 'sawtooth';
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.2);
}
function playSuccessSound() {
const notes = [523.25, 659.25, 783.99]; // C, E, G
notes.forEach((freq, index) => {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(freq, audioContext.currentTime + index * 0.1);
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime + index * 0.1);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + index * 0.1 + 0.5);
oscillator.start(audioContext.currentTime + index * 0.1);
oscillator.stop(audioContext.currentTime + index * 0.1 + 0.5);
});
}
// Initialize the game
function initGame() {
// Create initial blips
createBlips();
// Set up keyboard event listener
document.addEventListener('keydown', handleKeyPress);
// Update the sweep line rotation
setInterval(updateSweep, 50);
}
// Create blips on the radar
function createBlips() {
const numBlips = 5;
const minAngleDiff = 12; // Minimum angle difference between blips
const maxAttempts = 5; // Prevent infinite loop
let placed = 0;
let attempts = 0;
while (placed < numBlips && attempts < maxAttempts) {
const angle = Math.random() * 360;
const distance = 0.3 + Math.random() * 0.5;
const x = 160 + 160 * distance * Math.cos(angle * Math.PI / 180);
const y = 160 + 160 * distance * Math.sin(angle * Math.PI / 180);
// Check angular distance from existing blips
let tooClose = blips.some(blip => {
const diff = Math.abs(blip.angle - angle);
const angleDiff = Math.min(diff, 360 - diff); // handle wraparound
return angleDiff < minAngleDiff;
});
if (!tooClose) {
const blip = document.createElement('div');
blip.className = 'blip';
blip.style.left = x + 'px';
blip.style.top = y + 'px';
blip.dataset.angle = angle;
blipsContainer.appendChild(blip);
blips.push({
element: blip,
angle: angle,
locked: false
});
placed++;
}
attempts++;
}
}
// Update the sweep line rotation
function updateSweep() {
sweepAngle = (sweepAngle + (360 / (SWEEP_SPEED * 20))) % 360;
sweepLine.style.transform = `rotate(${sweepAngle}deg)`;
}
// Handle key press
function handleKeyPress(event) {
if (!gameActive || event.key.toLowerCase() !== 'e') return;
totalAttempts++;
let hit = false;
// Check if any blip is within the tolerance
for (let i = 0; i < blips.length; i++) {
const blip = blips[i];
if (blip.locked) continue;
const angleDiff = Math.abs((blip.angle - sweepAngle + 360) % 360);
const isWithinTolerance = angleDiff <= HIT_TOLERANCE || angleDiff >= (360 - HIT_TOLERANCE);
if (isWithinTolerance) {
// Hit!
blip.locked = true;
blip.element.classList.add('locked-blip');
lockedBlips.push(blip);
// Create hit effect
createHitEffect(blip.element.offsetLeft + 6, blip.element.offsetTop + 6);
score++;
successfulHits++;
updateScoreDisplay();
playHitSound();
hit = true;
break;
}
}
if (!hit) {
playMissSound();
}
updateAccuracy();
// Check if game is complete
if (score >= TOTAL_TARGETS) {
gameComplete();
}
}
// Create hit effect
function createHitEffect(x, y) {
const effect = document.createElement('div');
effect.className = 'hit-effect';
effect.style.left = x + 'px';
effect.style.top = y + 'px';
hitEffects.appendChild(effect);
// Remove after animation completes
setTimeout(() => {
effect.remove();
}, 600);
}
// Update score display
function updateScoreDisplay() {
scoreDisplay.textContent = `${score}/${TOTAL_TARGETS}`;
if (score < TOTAL_TARGETS) {
statusDisplay.textContent = `Lock onto target ${score + 1}...`;
}
}
// Update accuracy display
function updateAccuracy() {
const accuracy = totalAttempts > 0 ? Math.round((successfulHits / totalAttempts) * 100) : 0;
accuracyDisplay.textContent = `${accuracy}%`;
}
// Game complete
function gameComplete() {
gameActive = false;
statusDisplay.textContent = "All targets locked! Excavation authorized.";
playSuccessSound();
// Add celebration effect
setTimeout(() => {
document.body.style.animation = 'celebration 2s ease-in-out';
}, 1000);
// Remove after 3 seconds
setTimeout(() => {
if (confirm("Metal detection complete! Proceed with excavation?")) {
location.reload();
}
}, 3000);
}
// Add celebration animation
const style = document.createElement('style');
style.textContent = `
@keyframes celebration {
0% { filter: brightness(1); }
50% { filter: brightness(1.5) hue-rotate(120deg); }
100% { filter: brightness(1); }
}
`;
document.head.appendChild(style);
// Start the game
initGame();
</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=Mankeysocks/ex-7" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>