Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Phantom Frequency Frenzy</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- background: linear-gradient(180deg, #0a0a0a 0%, #1a0f1f 100%);
- color: #e0e0e0;
- font-family: 'Courier New', monospace;
- height: 100vh;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
- #gameContainer {
- width: 90%;
- max-width: 800px;
- position: relative;
- }
- #radio {
- background: linear-gradient(145deg, #2a2a3a, #1a1a2a);
- border-radius: 20px;
- padding: 30px;
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.7),
- inset 0 2px 4px rgba(255, 255, 255, 0.1);
- position: relative;
- }
- #display {
- background: radial-gradient(ellipse at center, #001122, #000511);
- border: 3px solid #3a4a5a;
- border-radius: 10px;
- height: 200px;
- margin-bottom: 20px;
- position: relative;
- overflow: hidden;
- }
- #frequencyDisplay {
- position: absolute;
- top: 10px;
- left: 20px;
- font-size: 24px;
- color: #00ff88;
- text-shadow: 0 0 10px #00ff88;
- font-weight: bold;
- z-index: 10;
- }
- #spectrum {
- width: 100%;
- height: 100%;
- position: relative;
- }
- .wave {
- position: absolute;
- bottom: 0;
- width: 2%;
- background: linear-gradient(to top, transparent, #00ffaa, transparent);
- animation: pulse 0.5s infinite;
- transition: all 0.1s;
- opacity: 0.7;
- }
- @keyframes pulse {
- 0%, 100% { opacity: 0.7; }
- 50% { opacity: 1; }
- }
- .ghost-particle {
- position: absolute;
- width: 4px;
- height: 4px;
- background: radial-gradient(circle, #88ffcc, transparent);
- border-radius: 50%;
- animation: float 3s infinite ease-in-out;
- }
- @keyframes float {
- 0%, 100% { transform: translateY(0) translateX(0); opacity: 0; }
- 50% { transform: translateY(-50px) translateX(20px); opacity: 1; }
- }
- #tuner {
- position: relative;
- height: 80px;
- background: linear-gradient(90deg, #1a1a2a, #2a2a3a, #1a1a2a);
- border-radius: 10px;
- margin-bottom: 20px;
- display: flex;
- align-items: center;
- padding: 0 20px;
- }
- #dialKnob {
- width: 60px;
- height: 60px;
- background: radial-gradient(circle at 30% 30%, #5a6a7a, #2a3a4a);
- border-radius: 50%;
- position: relative;
- cursor: pointer;
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
- transition: transform 0.1s;
- }
- #dialKnob::before {
- content: '';
- position: absolute;
- width: 4px;
- height: 25px;
- background: #00ff88;
- left: 50%;
- top: 5px;
- transform: translateX(-50%);
- box-shadow: 0 0 10px #00ff88;
- }
- #dialKnob:active {
- transform: scale(0.95);
- }
- #frequencyBar {
- flex: 1;
- height: 6px;
- background: #1a1a2a;
- margin-left: 20px;
- border-radius: 3px;
- position: relative;
- overflow: hidden;
- }
- #frequencyIndicator {
- position: absolute;
- width: 20px;
- height: 20px;
- background: radial-gradient(circle, #00ff88, #00aa55);
- border-radius: 50%;
- top: 50%;
- transform: translateY(-50%);
- box-shadow: 0 0 20px #00ff88;
- transition: left 0.1s;
- }
- #ghostMessages {
- background: rgba(0, 0, 0, 0.7);
- border: 1px solid #3a4a5a;
- border-radius: 10px;
- padding: 15px;
- height: 120px;
- overflow-y: auto;
- margin-bottom: 20px;
- position: relative;
- }
- .ghost-message {
- color: #88ffcc;
- margin: 5px 0;
- opacity: 0;
- animation: fadeInGlow 1s forwards;
- text-shadow: 0 0 5px currentColor;
- }
- @keyframes fadeInGlow {
- to {
- opacity: 1;
- text-shadow: 0 0 10px currentColor;
- }
- }
- #controls {
- display: flex;
- justify-content: space-between;
- gap: 10px;
- }
- button {
- background: linear-gradient(145deg, #3a4a5a, #2a3a4a);
- color: #00ff88;
- border: 2px solid #00ff88;
- padding: 10px 20px;
- border-radius: 5px;
- cursor: pointer;
- font-family: inherit;
- font-size: 14px;
- transition: all 0.3s;
- flex: 1;
- }
- button:hover {
- background: linear-gradient(145deg, #4a5a6a, #3a4a5a);
- box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
- transform: translateY(-2px);
- }
- #stats {
- display: flex;
- justify-content: space-around;
- margin-top: 20px;
- padding: 15px;
- background: rgba(0, 20, 40, 0.5);
- border-radius: 10px;
- }
- .stat {
- text-align: center;
- }
- .stat-value {
- font-size: 24px;
- color: #00ff88;
- text-shadow: 0 0 10px #00ff88;
- }
- .stat-label {
- font-size: 12px;
- color: #88aacc;
- margin-top: 5px;
- }
- .static-overlay {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: repeating-linear-gradient(
- 0deg,
- rgba(255, 255, 255, 0.03),
- rgba(255, 255, 255, 0.03) 1px,
- transparent 1px,
- transparent 2px
- );
- pointer-events: none;
- opacity: 0;
- animation: staticNoise 0.1s infinite;
- }
- @keyframes staticNoise {
- 0%, 100% { opacity: 0.1; }
- 50% { opacity: 0.05; }
- }
- .ghost-captured {
- animation: captureFlash 0.5s;
- }
- @keyframes captureFlash {
- 0%, 100% { background: transparent; }
- 50% { background: rgba(0, 255, 136, 0.2); }
- }
- #title {
- text-align: center;
- margin-bottom: 30px;
- font-size: 32px;
- background: linear-gradient(90deg, #00ff88, #88ffcc, #00ff88);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- text-shadow: 0 0 30px rgba(0, 255, 136, 0.5);
- animation: titleGlow 2s infinite alternate;
- }
- @keyframes titleGlow {
- from { filter: brightness(1); }
- to { filter: brightness(1.3); }
- }
- .scanning {
- animation: scan 2s infinite;
- }
- @keyframes scan {
- 0%, 100% { transform: translateX(0); }
- 50% { transform: translateX(10px); }
- }
- </style>
- </head>
- <body>
- <div id="gameContainer">
- <h1 id="title">PHANTOM FREQUENCY FRENZY</h1>
- <div id="radio">
- <div id="display">
- <div id="frequencyDisplay">87.5 MHz</div>
- <canvas id="spectrum"></canvas>
- <div class="static-overlay"></div>
- </div>
- <div id="tuner">
- <div id="dialKnob"></div>
- <div id="frequencyBar">
- <div id="frequencyIndicator" style="left: 0%;"></div>
- </div>
- </div>
- <div id="ghostMessages">
- <div style="color: #88aacc; opacity: 0.5;">Tuning into the spectral realm...</div>
- </div>
- <div id="controls">
- <button id="scanBtn">Auto Scan</button>
- <button id="filterBtn">Filter Static</button>
- <button id="captureBtn">Capture Ghost</button>
- </div>
- <div id="stats">
- <div class="stat">
- <div class="stat-value" id="ghostCount">0/10</div>
- <div class="stat-label">Ghosts Found</div>
- </div>
- <div class="stat">
- <div class="stat-value" id="signalStrength">0%</div>
- <div class="stat-label">Signal Strength</div>
- </div>
- <div class="stat">
- <div class="stat-value" id="chorusSize">0</div>
- <div class="stat-label">Chorus Voices</div>
- </div>
- </div>
- </div>
- </div>
- <script>
- class PhantomRadio {
- constructor() {
- this.frequency = 87.5;
- this.minFreq = 87.5;
- this.maxFreq = 108.0;
- this.ghosts = this.generateGhosts();
- this.capturedGhosts = new Set();
- this.totalChorusVoices = 0;
- this.signalStrength = 0;
- this.isScanning = false;
- this.staticFiltered = false;
- this.currentGhost = null;
- // Power-ups from enhanced version
- this.powerUps = {
- signalBoost: { active: false, duration: 0, cooldown: 0 },
- ghostMagnet: { active: false, duration: 0, cooldown: 0 },
- staticShield: { active: false, duration: 0, cooldown: 0 }
- };
- this.canvas = document.getElementById('spectrum');
- this.ctx = this.canvas.getContext('2d');
- this.setupCanvas();
- this.setupControls();
- this.setupPowerUps();
- this.startPowerUpTimer();
- this.animate();
- }
- generateGhosts() {
- const ghostData = [
- // 1 ghost with 2 voices
- { freq: 88.3, name: "The Whisperer", message: "Can you... hear... me... through the static...", chorusVoices: 2 },
- // 4 ghosts with 2-3 voices
- { freq: 90.1, name: "Lost Child", message: "Where... where is everyone... it's so cold...", chorusVoices: 3 },
- { freq: 92.7, name: "The Guardian", message: "Beware... the frequency... 96.6...", chorusVoices: 2 },
- { freq: 94.4, name: "Echo of Time", message: "1952... the accident... we never left...", chorusVoices: 3 },
- { freq: 98.9, name: "Memory Fragment", message: "Remember us... remember what happened here...", chorusVoices: 2 },
- // 3 ghosts with 4-6 voices
- { freq: 101.3, name: "The Scientist", message: "The experiment... it opened a door... we couldn't close it...", chorusVoices: 5 },
- { freq: 103.7, name: "Sorrow's Voice", message: "They promised we'd be together... lies... all lies...", chorusVoices: 4 },
- { freq: 105.5, name: "The Warning", message: "Don't trust the voices at 96.6... they're not who they claim...", chorusVoices: 6 },
- // 2 ghosts with 3-8 voices (correcting to match requirement)
- { freq: 96.6, name: "The Corrupted", message: "JOIN US... IN THE STATIC... FOREVER...", chorusVoices: 7 },
- { freq: 107.2, name: "The Keeper", message: "I guard the threshold... between your world and ours...", chorusVoices: 7 }
- ];
- return ghostData;
- }
- setupCanvas() {
- this.canvas.width = this.canvas.offsetWidth;
- this.canvas.height = this.canvas.offsetHeight;
- }
- setupControls() {
- const dialKnob = document.getElementById('dialKnob');
- const scanBtn = document.getElementById('scanBtn');
- const filterBtn = document.getElementById('filterBtn');
- const captureBtn = document.getElementById('captureBtn');
- let isDragging = false;
- let startX = 0;
- let startY = 0;
- let currentRotation = 0;
- let lastAngle = 0;
- // Helper function to calculate angle from center
- const getAngleFromCenter = (e, element) => {
- const rect = element.getBoundingClientRect();
- const centerX = rect.left + rect.width / 2;
- const centerY = rect.top + rect.height / 2;
- const angle = Math.atan2(e.clientY - centerY, e.clientX - centerX);
- return angle * (180 / Math.PI);
- };
- dialKnob.addEventListener('mousedown', (e) => {
- isDragging = true;
- startX = e.clientX;
- startY = e.clientY;
- lastAngle = getAngleFromCenter(e, dialKnob);
- e.preventDefault();
- });
- document.addEventListener('mousemove', (e) => {
- if (isDragging) {
- const currentAngle = getAngleFromCenter(e, dialKnob);
- let deltaAngle = currentAngle - lastAngle;
- // Handle angle wrapping (when crossing from -180 to 180 or vice versa)
- if (deltaAngle > 180) deltaAngle -= 360;
- if (deltaAngle < -180) deltaAngle += 360;
- // Update rotation and frequency based on angle change
- currentRotation += deltaAngle;
- dialKnob.style.transform = `rotate(${currentRotation}deg)`;
- // Adjust frequency based on rotation direction and amount
- this.adjustFrequency(deltaAngle * 0.05);
- lastAngle = currentAngle;
- }
- });
- document.addEventListener('mouseup', () => {
- isDragging = false;
- });
- // Mouse wheel control remains the same
- dialKnob.addEventListener('wheel', (e) => {
- e.preventDefault();
- const delta = -e.deltaY * 0.01;
- currentRotation += delta * 10;
- dialKnob.style.transform = `rotate(${currentRotation}deg)`;
- this.adjustFrequency(delta);
- });
- scanBtn.addEventListener('click', () => this.autoScan());
- filterBtn.addEventListener('click', () => this.toggleFilter());
- captureBtn.addEventListener('click', () => this.captureGhost());
- }
- adjustFrequency(delta) {
- this.frequency = Math.max(this.minFreq, Math.min(this.maxFreq, this.frequency + delta));
- this.updateDisplay();
- this.checkForGhosts();
- }
- updateDisplay() {
- const freqDisplay = document.getElementById('frequencyDisplay');
- freqDisplay.textContent = `${this.frequency.toFixed(1)} MHz`;
- const indicator = document.getElementById('frequencyIndicator');
- const percentage = ((this.frequency - this.minFreq) / (this.maxFreq - this.minFreq)) * 100;
- indicator.style.left = `${percentage}%`;
- }
- checkForGhosts() {
- this.currentGhost = null;
- this.signalStrength = 0;
- // Base detection range
- let detectionRange = 0.5;
- // Chorus voices boost detection range (more voices = easier to find)
- const chorusBoost = Math.min(this.totalChorusVoices * 0.02, 0.3);
- detectionRange += chorusBoost;
- // Ghost magnet power-up increases detection range
- if (this.powerUps.ghostMagnet.active) {
- detectionRange += 0.5;
- }
- for (const ghost of this.ghosts) {
- const distance = Math.abs(this.frequency - ghost.freq);
- if (distance < detectionRange) {
- // Base signal calculation
- let strength = Math.max(0, 100 - (distance * 200 / detectionRange));
- // Chorus voices increase signal strength
- strength += this.totalChorusVoices * 2;
- // Signal boost power-up
- if (this.powerUps.signalBoost.active) {
- strength *= 1.25;
- }
- strength = Math.min(100, strength);
- this.signalStrength = strength;
- this.currentGhost = ghost;
- if (distance < 0.1 && !this.capturedGhosts.has(ghost.freq)) {
- this.showGhostMessage(ghost);
- }
- break;
- }
- }
- document.getElementById('signalStrength').textContent = `${Math.round(this.signalStrength)}%`;
- this.updateStaticEffect();
- }
- showGhostMessage(ghost) {
- const messagesDiv = document.getElementById('ghostMessages');
- const messageDiv = document.createElement('div');
- messageDiv.className = 'ghost-message';
- const scrambled = (this.staticFiltered || this.powerUps.staticShield.active) ?
- ghost.message :
- this.scrambleMessage(ghost.message, this.signalStrength);
- messageDiv.textContent = `[${ghost.name}]: ${scrambled}`;
- messagesDiv.appendChild(messageDiv);
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
- // Limit messages
- while (messagesDiv.children.length > 5) {
- messagesDiv.removeChild(messagesDiv.children[1]);
- }
- }
- scrambleMessage(message, strength) {
- if (strength > 80) return message;
- const chars = message.split('');
- const scrambleRate = 1 - (strength / 100);
- return chars.map(char => {
- if (char === ' ') return ' ';
- return Math.random() < scrambleRate ?
- String.fromCharCode(33 + Math.floor(Math.random() * 93)) :
- char;
- }).join('');
- }
- captureGhost() {
- if (this.currentGhost && this.signalStrength > 70) {
- if (!this.capturedGhosts.has(this.currentGhost.freq)) {
- this.capturedGhosts.add(this.currentGhost.freq);
- this.totalChorusVoices += this.currentGhost.chorusVoices;
- // Update displays
- document.getElementById('ghostCount').textContent = `${this.capturedGhosts.size}/10`;
- document.getElementById('chorusSize').textContent = this.totalChorusVoices;
- const radio = document.getElementById('radio');
- radio.classList.add('ghost-captured');
- setTimeout(() => radio.classList.remove('ghost-captured'), 500);
- this.playGhostChorus();
- // Show ghost details in message
- const messagesDiv = document.getElementById('ghostMessages');
- const captureMsg = document.createElement('div');
- captureMsg.className = 'ghost-message';
- captureMsg.style.color = '#ffaa00';
- captureMsg.textContent = `✓ Captured ${this.currentGhost.name}! (+${this.currentGhost.chorusVoices} chorus voices)`;
- messagesDiv.appendChild(captureMsg);
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
- // Check for victory
- if (this.capturedGhosts.size === 10) {
- this.victorySequence();
- }
- }
- }
- }
- victorySequence() {
- // Victory message overlay
- const gameContainer = document.getElementById('gameContainer');
- const victoryOverlay = document.createElement('div');
- victoryOverlay.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.9);
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- z-index: 1000;
- animation: fadeIn 1s;
- `;
- const victoryContent = document.createElement('div');
- victoryContent.style.cssText = `
- text-align: center;
- padding: 40px;
- background: linear-gradient(145deg, #2a3a4a, #1a2a3a);
- border-radius: 20px;
- border: 3px solid #00ff88;
- box-shadow: 0 0 50px rgba(0, 255, 136, 0.5);
- max-width: 500px;
- `;
- victoryContent.innerHTML = `
- <h1 style="color: #00ff88; font-size: 36px; margin-bottom: 20px; text-shadow: 0 0 20px #00ff88;">
- 🎉 CONGRATULATIONS! 🎉
- </h1>
- <p style="color: #88ffcc; font-size: 20px; margin: 10px 0;">
- You've captured all 10/10 ghosts!
- </p>
- <p style="color: #88aacc; font-size: 16px; margin: 20px 0;">
- Total Chorus Voices Collected: <span style="color: #00ff88; font-size: 24px; font-weight: bold;">${this.totalChorusVoices}</span>
- </p>
- <p style="color: #88aacc; font-size: 14px; margin: 20px 0; font-style: italic;">
- The spectral realm is now at peace. The ghosts thank you for freeing them from their eternal static prison.
- </p>
- <button onclick="location.reload()" style="
- margin-top: 30px;
- padding: 15px 30px;
- background: linear-gradient(145deg, #00aa55, #00ff88);
- color: #000;
- border: none;
- border-radius: 10px;
- font-size: 18px;
- font-weight: bold;
- cursor: pointer;
- box-shadow: 0 5px 15px rgba(0, 255, 136, 0.4);
- transition: all 0.3s;
- " onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
- PLAY AGAIN
- </button>
- `;
- victoryOverlay.appendChild(victoryContent);
- document.body.appendChild(victoryOverlay);
- // Add CSS animation
- const style = document.createElement('style');
- style.textContent = `
- @keyframes fadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
- }
- `;
- document.head.appendChild(style);
- // Celebration effects
- this.playCelebration();
- }
- playCelebration() {
- const display = document.getElementById('display');
- // Create celebration particles
- for (let i = 0; i < 50; i++) {
- setTimeout(() => {
- const particle = document.createElement('div');
- particle.className = 'ghost-particle';
- particle.style.left = `${Math.random() * 100}%`;
- particle.style.bottom = '0';
- particle.style.background = `radial-gradient(circle, hsl(${Math.random() * 360}, 100%, 70%), transparent)`;
- particle.style.animationDelay = '0s';
- particle.style.animationDuration = `${2 + Math.random() * 2}s`;
- display.appendChild(particle);
- setTimeout(() => particle.remove(), 4000);
- }, i * 50);
- }
- }
- playGhostChorus() {
- // Visual feedback for chorus
- const particles = 20;
- const display = document.getElementById('display');
- for (let i = 0; i < particles; i++) {
- const particle = document.createElement('div');
- particle.className = 'ghost-particle';
- particle.style.left = `${Math.random() * 100}%`;
- particle.style.bottom = '0';
- particle.style.animationDelay = `${Math.random() * 2}s`;
- display.appendChild(particle);
- setTimeout(() => particle.remove(), 3000);
- }
- }
- setupPowerUps() {
- // Create power-ups section
- const radio = document.getElementById('radio');
- const powerUpSection = document.createElement('div');
- powerUpSection.id = 'powerUps';
- powerUpSection.style.cssText = `
- display: flex;
- justify-content: space-between;
- margin-top: 15px;
- gap: 10px;
- `;
- // Create three power-up cards
- const powerUp1 = this.createPowerUpCard('powerUp1', 'Signal Boost', '+25% signal strength for 10s');
- const powerUp2 = this.createPowerUpCard('powerUp2', 'Ghost Magnet', 'Reveals nearby ghosts');
- const powerUp3 = this.createPowerUpCard('powerUp3', 'Static Shield', 'Reduces static for 15s');
- powerUpSection.appendChild(powerUp1);
- powerUpSection.appendChild(powerUp2);
- powerUpSection.appendChild(powerUp3);
- radio.appendChild(powerUpSection);
- // Add event listeners
- powerUp1.addEventListener('click', () => this.activatePowerUp('signalBoost'));
- powerUp2.addEventListener('click', () => this.activatePowerUp('ghostMagnet'));
- powerUp3.addEventListener('click', () => this.activatePowerUp('staticShield'));
- }
- createPowerUpCard(id, name, description) {
- const card = document.createElement('div');
- card.id = id;
- card.className = 'power-up';
- card.style.cssText = `
- flex: 1;
- background: rgba(0, 40, 80, 0.7);
- border-radius: 8px;
- padding: 12px;
- text-align: center;
- border: 2px solid #3a4a5a;
- cursor: pointer;
- transition: all 0.3s;
- `;
- const nameDiv = document.createElement('div');
- nameDiv.className = 'power-up-name';
- nameDiv.style.cssText = `
- font-size: 13px;
- color: #00ff88;
- margin-bottom: 5px;
- font-weight: bold;
- text-transform: uppercase;
- `;
- nameDiv.textContent = name;
- const descDiv = document.createElement('div');
- descDiv.className = 'power-up-desc';
- descDiv.style.cssText = `
- font-size: 11px;
- color: #88aacc;
- `;
- descDiv.textContent = description;
- card.appendChild(nameDiv);
- card.appendChild(descDiv);
- card.addEventListener('mouseenter', () => {
- if (!card.disabled) {
- card.style.background = 'rgba(0, 60, 120, 0.8)';
- card.style.transform = 'translateY(-2px)';
- card.style.boxShadow = '0 5px 15px rgba(0, 255, 136, 0.3)';
- }
- });
- card.addEventListener('mouseleave', () => {
- if (!card.disabled) {
- card.style.background = 'rgba(0, 40, 80, 0.7)';
- card.style.transform = 'translateY(0)';
- card.style.boxShadow = 'none';
- }
- });
- return card;
- }
- activatePowerUp(type) {
- const powerUp = this.powerUps[type];
- if (powerUp.active || powerUp.cooldown > 0) return;
- powerUp.active = true;
- switch(type) {
- case 'signalBoost':
- powerUp.duration = 10;
- powerUp.cooldown = 30;
- break;
- case 'ghostMagnet':
- powerUp.duration = 15;
- powerUp.cooldown = 45;
- break;
- case 'staticShield':
- powerUp.duration = 15;
- powerUp.cooldown = 40;
- break;
- }
- this.updatePowerUpDisplay();
- this.checkForGhosts();
- }
- updatePowerUpDisplay() {
- const powerUp1 = document.getElementById('powerUp1');
- const powerUp2 = document.getElementById('powerUp2');
- const powerUp3 = document.getElementById('powerUp3');
- // Update Signal Boost
- this.updatePowerUpCard(powerUp1, 'signalBoost', 'Signal Boost', '+25% signal strength for 10s');
- // Update Ghost Magnet
- this.updatePowerUpCard(powerUp2, 'ghostMagnet', 'Ghost Magnet', 'Reveals nearby ghosts');
- // Update Static Shield
- this.updatePowerUpCard(powerUp3, 'staticShield', 'Static Shield', 'Reduces static for 15s');
- }
- updatePowerUpCard(card, powerUpKey, name, description) {
- const powerUp = this.powerUps[powerUpKey];
- const nameDiv = card.querySelector('.power-up-name');
- const descDiv = card.querySelector('.power-up-desc');
- if (powerUp.active) {
- card.style.background = 'rgba(0, 100, 50, 0.8)';
- card.style.borderColor = '#00ff88';
- card.style.boxShadow = '0 0 20px rgba(0, 255, 136, 0.5)';
- nameDiv.style.color = '#00ff88';
- nameDiv.textContent = `${name} ACTIVE`;
- descDiv.textContent = `Active for ${powerUp.duration}s`;
- descDiv.style.color = '#00ff88';
- card.disabled = true;
- card.style.cursor = 'not-allowed';
- } else if (powerUp.cooldown > 0) {
- card.style.background = 'rgba(60, 20, 20, 0.7)';
- card.style.borderColor = '#666';
- card.style.boxShadow = 'none';
- nameDiv.style.color = '#666';
- nameDiv.textContent = name;
- descDiv.textContent = `Cooldown: ${powerUp.cooldown}s`;
- descDiv.style.color = '#666';
- card.disabled = true;
- card.style.cursor = 'not-allowed';
- } else {
- card.style.background = 'rgba(0, 40, 80, 0.7)';
- card.style.borderColor = '#3a4a5a';
- card.style.boxShadow = 'none';
- nameDiv.style.color = '#00ff88';
- nameDiv.textContent = name;
- descDiv.textContent = description;
- descDiv.style.color = '#88aacc';
- card.disabled = false;
- card.style.cursor = 'pointer';
- }
- }
- startPowerUpTimer() {
- setInterval(() => {
- // Update power-up durations and cooldowns
- for (const key in this.powerUps) {
- const powerUp = this.powerUps[key];
- if (powerUp.active) {
- powerUp.duration--;
- if (powerUp.duration <= 0) {
- powerUp.active = false;
- }
- }
- if (powerUp.cooldown > 0) {
- powerUp.cooldown--;
- }
- }
- this.updatePowerUpDisplay();
- }, 1000);
- }
- toggleFilter() {
- this.staticFiltered = !this.staticFiltered;
- const filterBtn = document.getElementById('filterBtn');
- filterBtn.textContent = this.staticFiltered ? 'Filter: ON' : 'Filter: OFF';
- filterBtn.style.borderColor = this.staticFiltered ? '#ffaa00' : '#00ff88';
- filterBtn.style.color = this.staticFiltered ? '#ffaa00' : '#00ff88';
- }
- updateStaticEffect() {
- const staticOverlay = document.querySelector('.static-overlay');
- let opacity = (1 - this.signalStrength / 100) * 0.3;
- // Static shield reduces static
- if (this.powerUps.staticShield.active) {
- opacity *= 0.3;
- } else if (this.staticFiltered) {
- opacity *= 0.5;
- }
- staticOverlay.style.opacity = opacity;
- }
- autoScan() {
- if (this.isScanning) return;
- this.isScanning = true;
- const scanBtn = document.getElementById('scanBtn');
- scanBtn.textContent = 'Scanning...';
- scanBtn.classList.add('scanning');
- const scanInterval = setInterval(() => {
- this.frequency += 0.2;
- if (this.frequency > this.maxFreq) {
- this.frequency = this.minFreq;
- }
- this.updateDisplay();
- this.checkForGhosts();
- if (this.currentGhost && this.signalStrength > 90) {
- clearInterval(scanInterval);
- this.isScanning = false;
- scanBtn.textContent = 'Auto Scan';
- scanBtn.classList.remove('scanning');
- }
- }, 50);
- setTimeout(() => {
- if (this.isScanning) {
- clearInterval(scanInterval);
- this.isScanning = false;
- scanBtn.textContent = 'Auto Scan';
- scanBtn.classList.remove('scanning');
- }
- }, 10000);
- }
- drawSpectrum() {
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- const bars = 50;
- const barWidth = this.canvas.width / bars;
- for (let i = 0; i < bars; i++) {
- const height = Math.random() * this.canvas.height * 0.7;
- const intensity = this.signalStrength / 100;
- // Ghost presence creates pattern
- const ghostEffect = this.currentGhost ?
- Math.sin(Date.now() * 0.001 + i * 0.5) * 0.5 + 0.5 :
- 0;
- const finalHeight = height * (0.3 + intensity * 0.7) + ghostEffect * 50;
- const hue = 160 + intensity * 20;
- const lightness = 50 + intensity * 30;
- this.ctx.fillStyle = `hsla(${hue}, 100%, ${lightness}%, ${0.3 + intensity * 0.5})`;
- this.ctx.fillRect(
- i * barWidth,
- this.canvas.height - finalHeight,
- barWidth - 2,
- finalHeight
- );
- // Add glow effect
- if (intensity > 0.5) {
- this.ctx.shadowBlur = 10;
- this.ctx.shadowColor = `hsla(${hue}, 100%, 70%, ${intensity})`;
- this.ctx.fillRect(
- i * barWidth,
- this.canvas.height - finalHeight,
- barWidth - 2,
- 2
- );
- this.ctx.shadowBlur = 0;
- }
- }
- }
- animate() {
- this.drawSpectrum();
- requestAnimationFrame(() => this.animate());
- }
- }
- // Initialize game
- const game = new PhantomRadio();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment