Bunrec-com / script.js
BadassBitchBunny's picture
🐳 16/06 - 16:42 - in the hidden room, we should offer something special. a discount code for etsy https://www.softbitestudio.etsy.com but there needs to be something else. build a 3 card tarot layout
4b61a38 verified
Raw
History Blame Contribute Delete
26.3 kB
// ============================================
// BUNNY RECKLESS — Warren Explorer Scripts
// ============================================
// Initialize Lucide Icons
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
initParticles();
initSplash();
initDoNotPress();
initCopyCode();
initTarot();
});
// ---- Splash Screen ----
function initSplash() {
const splash = document.getElementById('splash');
const enterBtn = document.getElementById('enter-btn');
const mainContent = document.getElementById('main-content');
enterBtn.addEventListener('click', () => {
splash.style.opacity = '0';
splash.style.transform = 'scale(1.1)';
splash.style.filter = 'blur(10px)';
setTimeout(() => {
splash.style.display = 'none';
mainContent.style.opacity = '1';
}, 600);
});
}
// ---- Discovery System ----
const discovered = new Set();
const ROOMS = ['burrow', 'network', 'studio', 'lab', 'coven'];
const ROOM_NAMES = {
burrow: 'The Burrow',
network: 'The Network',
studio: 'The Studio',
lab: 'The Lab',
coven: 'The Coven',
vault: 'The Vault 🔒'
};
const ROOM_COLORS = {
burrow: '#ff2d95',
network: '#00e5ff',
studio: '#b44dff',
lab: '#22c55e',
coven: '#eab308',
vault: '#ef4444'
};
const ROOM_ICONS = {
burrow: '🏠',
network: '🌐',
studio: '🎨',
lab: '🧪',
coven: '✨',
vault: '🔓'
};
function toggleRoom(roomId) {
const content = document.getElementById(`content-${roomId}`);
const door = document.querySelector(`.room-door[data-room="${roomId}"]`);
const chevron = door.querySelector('.door-chevron');
if (content.classList.contains('open')) {
// Close
content.classList.remove('open');
door.classList.remove('opened');
chevron.classList.remove('rotated');
} else {
// Open
content.classList.add('open');
door.classList.add('opened');
chevron.classList.add('rotated');
// Track discovery
if (!discovered.has(roomId)) {
discovered.add(roomId);
updateProgress();
showToast(`Discovered ${ROOM_NAMES[roomId]}!`, ROOM_COLORS[roomId], ROOM_ICONS[roomId]);
// Check if all rooms discovered
if (discovered.size === ROOMS.length) {
setTimeout(() => {
unlockVault();
}, 1500);
}
}
// Scroll to room
setTimeout(() => {
document.getElementById(`room-${roomId}`).scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}, 200);
}
}
function updateProgress() {
const count = discovered.size;
document.getElementById('discovered-count').textContent = count;
// Update progress fill
const percentage = (count / ROOMS.length) * 100;
document.getElementById('progress-fill').style.width = `${percentage}%`;
// Update dots
discovered.forEach(room => {
const dot = document.querySelector(`.progress-dot[data-room="${room}"]`);
if (dot) dot.classList.add('discovered');
});
}
function unlockVault() {
const vault = document.getElementById('room-vault');
vault.classList.remove('hidden');
vault.classList.add('reveal-animate');
// Update vault dot
const vaultDot = document.querySelector('.progress-dot.secret');
if (vaultDot) vaultDot.classList.add('discovered');
// Flash effect
document.body.classList.add('flash-effect');
setTimeout(() => document.body.classList.remove('flash-effect'), 1000);
showToast('SECRET ROOM UNLOCKED! 🔓', '#ef4444', '💀');
// Initialize tarot cards
setTimeout(() => {
initTarot();
}, 100);
// Scroll to vault
setTimeout(() => {
vault.scrollIntoView({ behavior: 'smooth', block: 'start' });
}, 800);
}
// ---- Toast System ----
function showToast(message, color = '#ff2d95', icon = '✨') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
toast.className = 'toast';
toast.innerHTML = `
<span class="text-lg">${icon}</span>
<span class="text-sm font-medium" style="color: ${color}">${message}</span>
`;
toast.style.borderLeftColor = color;
toast.style.borderLeftWidth = '3px';
container.appendChild(toast);
setTimeout(() => {
toast.classList.add('toast-exit');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// ---- DO NOT PRESS Button ----
function initDoNotPress() {
const btn = document.getElementById('do-not-press-btn');
if (!btn) return;
btn.addEventListener('click', (e) => {
e.preventDefault();
// If vault already visible, just scroll
const vault = document.getElementById('room-vault');
if (!vault.classList.contains('hidden')) {
vault.scrollIntoView({ behavior: 'smooth' });
return;
}
// Open the lab room first if not open
const labContent = document.getElementById('content-lab');
if (!labContent.classList.contains('open')) {
toggleRoom('lab');
}
// Unlock vault
setTimeout(() => {
unlockVault();
}, 500);
});
}
// ---- Copy Discount Code ----
function initCopyCode() {
const btn = document.getElementById('copy-code-btn');
if (!btn) return;
btn.addEventListener('click', () => {
navigator.clipboard.writeText('BITEBACK').then(() => {
const span = btn.querySelector('span');
const originalText = span.textContent;
span.textContent = 'Copied!';
btn.style.borderColor = 'rgba(255, 45, 149, 0.6)';
setTimeout(() => {
span.textContent = originalText;
btn.style.borderColor = '';
}, 2000);
showToast('Discount code copied! 🎉', '#ff2d95', '📋');
}).catch(() => {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = 'BITEBACK';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
const span = btn.querySelector('span');
span.textContent = 'Copied!';
setTimeout(() => { span.textContent = 'Copy'; }, 2000);
});
});
}
// ---- Particle System ----
function initParticles() {
const canvas = document.getElementById('particles');
const ctx = canvas.getContext('2d');
let width, height;
const particles = [];
const PARTICLE_COUNT = 40;
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
resize();
window.addEventListener('resize', resize);
class Particle {
constructor() {
this.reset();
}
reset() {
this.x = Math.random() * width;
this.y = Math.random() * height;
this.size = Math.random() * 2 + 0.5;
this.speedX = (Math.random() - 0.5) * 0.3;
this.speedY = (Math.random() - 0.5) * 0.3;
this.opacity = Math.random() * 0.4 + 0.1;
this.color = Math.random() > 0.5 ? '#ff2d95' : '#00e5ff';
}
update() {
this.x += this.speedX;
this.y += this.speedY;
if (this.x < 0 || this.x > width) this.speedX *= -1;
if (this.y < 0 || this.y > height) this.speedY *= -1;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.globalAlpha = this.opacity;
ctx.fill();
ctx.globalAlpha = 1;
}
}
for (let i = 0; i < PARTICLE_COUNT; i++) {
particles.push(new Particle());
}
function animate() {
ctx.clearRect(0, 0, width, height);
particles.forEach(p => {
p.update();
p.draw();
});
// Draw connections
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 150) {
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.strokeStyle = particles[i].color;
ctx.globalAlpha = 0.05 * (1 - dist / 150);
ctx.lineWidth = 0.5;
ctx.stroke();
ctx.globalAlpha = 1;
}
}
}
requestAnimationFrame(animate);
}
animate();
}
// ---- Keyboard Easter Egg ----
let konamiCode = [];
const KONAMI = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
document.addEventListener('keydown', (e) => {
konamiCode.push(e.key);
konamiCode = konamiCode.slice(-10);
if (konamiCode.join(',') === KONAMI.join(',')) {
const vault = document.getElementById('room-vault');
if (vault.classList.contains('hidden')) {
unlockVault();
} else {
vault.scrollIntoView({ behavior: 'smooth' });
}
showToast('KONAMI CODE ACTIVATED! 🎮', '#eab308', '🎮');
}
});
// ---- Scroll-triggered door glow ----
const observerOptions = {
threshold: 0.2,
rootMargin: '0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const door = entry.target.querySelector('.room-door');
if (door && !door.classList.contains('opened')) {
door.style.animation = 'none';
door.offsetHeight; // Trigger reflow
}
}
});
}, observerOptions);
document.querySelectorAll('[id^="room-"]').forEach(room => {
observer.observe(room);
});
// ---- Subscribe Button ----
document.addEventListener('click', (e) => {
if (e.target.closest('button') && e.target.closest('button').textContent.includes('Subscribe')) {
const input = e.target.closest('div').querySelector('input[type="email"]');
if (input && input.value && input.value.includes('@')) {
showToast('Welcome to the Fluff Coven! ✐꩙', '#ff2d95', '🐰');
input.value = '';
input.placeholder = 'You\'re in! 🎉';
} else {
showToast('Please enter a valid email', '#ef4444', '⚠️');
input.focus();
}
}
});
// ---- Tarot Cards ----
const TAROT_PRIZES = [
{
name: 'The Moon',
symbol: '☽',
prize: 'discount',
title: '20% Off Etsy',
description: 'A discount for the misfits & dreamers',
code: 'BITEBACK',
link: 'https://softbitestudio.etsy.com/',
linkText: 'Visit Shop →',
color: '#ff2d95',
revealIcon: '🌙'
},
{
name: 'The Star',
symbol: '★',
prize: 'download',
title: 'Free Digital Download',
description: 'A gift from the warren, just for you',
code: null,
link: 'https://softbitestudio.etsy.com/',
linkText: 'Claim Your Gift →',
color: '#00e5ff',
revealIcon: '⭐'
},
{
name: 'The Tower',
symbol: '⚡',
prize: 'secret',
title: 'Secret Code',
description: 'Whisper this to unlock hidden paths',
code: 'FLUFFN FANGS',
link: null,
linkText: null,
color: '#b44dff',
revealIcon: '🔓'
}
];
let shuffledCards = [];
let tarotInitialized = false;
let fortuneDrawn = false;
function initTarot() {
if (tarotInitialized) return;
tarotInitialized = true;
const container = document.getElementById('tarot-cards');
const sealedDiv = document.getElementById('fortune-sealed');
if (!container) return;
// Check localStorage
const savedFortune = localStorage.getItem('warren_fortune');
if (savedFortune) {
fortuneDrawn = true;
try {
const fortune = JSON.parse(savedFortune);
renderCards(container, true);
sealedDiv.classList.remove('hidden');
showPreviousFortune(fortune);
} catch (e) {
localStorage.removeItem('warren_fortune');
fortuneDrawn = false;
}
}
if (!fortuneDrawn) {
shuffledCards = [...TAROT_PRIZES].sort(() => Math.random() - 0.5);
renderCards(container, false);
container.addEventListener('click', (e) => {
const card = e.target.closest('.tarot-card');
if (card && !card.classList.contains('revealed') && !card.classList.contains('dimmed') && !fortuneDrawn) {
flipCard(parseInt(card.dataset.index));
}
});
}
}
function renderCards(container, sealed) {
const cards = sealed ? TAROT_PRIZES : shuffledCards;
container.innerHTML = cards.map((card, i) => `
<div class="tarot-card ${sealed ? 'dimmed' : ''}" data-index="${i}" style="animation-delay: ${i * 0.3}s">
<div class="tarot-card-inner">
<div class="tarot-card-front">
<div class="tarot-card-inner-border"></div>
<div class="tarot-card-symbol">${card.symbol}</div>
<div class="tarot-card-divider">⟡</div>
<div class="tarot-card-label">${card.name}</div>
</div>
<div class="tarot-card-back" style="border: 1px solid ${card.color}40">
<div class="tarot-prize-icon">${card.revealIcon}</div>
<div class="tarot-prize-title" style="color: ${card.color}">${card.title}</div>
<div class="tarot-prize-desc">${card.description}</div>
${card.code ? `<div class="tarot-prize-code" onclick="event.stopPropagation(); copyTarotCode('${card.code}')">${card.code}</div>` : ''}
${card.link ? `<a href="${card.link}" target="_blank" class="tarot-prize-link" onclick="event.stopPropagation()">${card.linkText}</a>` : ''}
</div>
</div>
</div>
`).join('');
}
function flipCard(index) {
if (fortuneDrawn) return;
fortuneDrawn = true;
const card = document.querySelector(`.tarot-card[data-index="${index}"]`);
const prize = shuffledCards[index];
// Add flash overlay
const flash = document.createElement('div');
flash.className = 'tarot-flash';
card.appendChild(flash);
// Flip the card
card.classList.add('revealed');
card.style.animation = 'none';
// Particle burst
setTimeout(() => {
createParticleBurst(card);
}, 250);
// Dim other cards
document.querySelectorAll('.tarot-card').forEach((c, i) => {
if (i !== index) {
c.classList.add('dimmed');
c.style.animation = 'none';
}
});
// Save to localStorage
localStorage.setItem('warren_fortune', JSON.stringify({
...prize,
timestamp: Date.now()
}));
// Toast
showToast(`Fortune revealed: ${prize.title}!`, prize.color, prize.revealIcon);
// Clean up flash
setTimeout(() => flash.remove(), 800);
}
function createParticleBurst(element) {
const rect = element.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const colors = ['#ff2d95', '#b44dff', '#00e5ff', '#eab308', '#ffffff'];
for (let i = 0; i < 35; i++) {
const particle = document.createElement('div');
particle.className = 'tarot-particle';
particle.style.left = centerX + 'px';
particle.style.top = centerY + 'px';
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * 140 + 30;
const dx = Math.cos(angle) * distance;
const dy = Math.sin(angle) * distance;
particle.style.setProperty('--dx', dx + 'px');
particle.style.setProperty('--dy', dy + 'px');
particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
const size = Math.random() * 5 + 2;
particle.style.width = size + 'px';
particle.style.height = size + 'px';
particle.style.animationDuration = (Math.random() * 0.5 + 0.7) + 's';
document.body.appendChild(particle);
particle.addEventListener('animationend', () => particle.remove());
}
}
function copyTarotCode(code) {
navigator.clipboard.writeText(code).then(() => {
showToast(`Code "${code}" copied! 🎉`, '#ff2d95', '📋');
}).catch(() => {
const textArea = document.createElement('textarea');
textArea.value = code;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showToast(`Code "${code}" copied! 🎉`, '#ff2d95', '📋');
});
}
function showPreviousFortune(fortune) {
const prevFortune = document.getElementById('tarot-prev-fortune');
if (!prevFortune) return;
prevFortune.innerHTML = `
<div style="font-size: 1.5rem; margin-bottom: 0.4rem;">${fortune.revealIcon}</div>
<div style="color: ${fortune.color}; font-family: 'Syne', sans-serif; font-weight: 700; margin-bottom: 0.3rem;">${fortune.title}</div>
<div style="font-size: 0.7rem; color: #6b6b8a; margin-bottom: 0.5rem;">${fortune.description}</div>
${fortune.code ? `<div style="font-family: 'Space Mono', monospace; font-size: 0.75rem; color: #ff2d95; cursor: pointer;" onclick="copyTarotCode('${fortune.code}')">${fortune.code}</div>` : ''}
${fortune.link ? `<a href="${fortune.link}" target="_blank" style="font-size: 0.65rem; color: #b44dff; text-decoration: underline;">${fortune.linkText}</a>` : ''}
`;
}
// ---- Tarot Fortune System ----
const FORTUNES = [
{
id: 'discount',
icon: '🌟',
label: 'Discount Code',
value: 'SOFTBITE15',
desc: '15% off at Soft Bite Studio on Etsy',
color: '#ff2d95'
},
{
id: 'download',
icon: '🌙',
label: 'Digital Download',
value: 'ART PACK UNLOCKED',
desc: 'A free collection of digital art & stickers',
color: '#00e5ff'
},
{
id: 'secret',
icon: '☀️',
label: 'Secret Code',
value: 'FLUFF4EVER',
desc: 'A cipher for the initiated... keep it close',
color: '#b44dff'
}
];
let tarotFlipped = false;
let shuffledFortunes = [];
function initTarot() {
const savedFortune = localStorage.getItem('warren_fortune');
if (savedFortune) {
showSealedFortune(savedFortune);
return;
}
// Shuffle fortunes randomly
shuffledFortunes = [...FORTUNES].sort(() => Math.random() - 0.5);
// Populate card backs with fortune data
shuffledFortunes.forEach((fortune, i) => {
document.getElementById(`fortune-icon-${i}`).textContent = fortune.icon;
document.getElementById(`fortune-label-${i}`).textContent = fortune.label;
const valueEl = document.getElementById(`fortune-value-${i}`);
valueEl.textContent = fortune.value;
valueEl.style.color = fortune.color;
document.getElementById(`fortune-desc-${i}`).textContent = fortune.desc;
});
}
function showSealedFortune(fortuneId) {
const fortune = FORTUNES.find(f => f.id === fortuneId);
if (!fortune) return;
const cardsContainer = document.getElementById('tarot-cards');
const sealedContainer = document.getElementById('fortune-sealed');
const etsyLink = document.getElementById('tarot-etsy-link');
if (cardsContainer) cardsContainer.classList.add('hidden');
if (sealedContainer) sealedContainer.classList.remove('hidden');
if (etsyLink) etsyLink.classList.remove('hidden');
const display = document.getElementById('sealed-fortune-display');
if (display) {
display.innerHTML = `
<div class="text-center">
<div class="text-5xl mb-4">${fortune.icon}</div>
<div class="text-xs font-mono uppercase tracking-[0.2em] mb-2" style="color: ${fortune.color}">${fortune.label}</div>
<div class="text-2xl font-display font-bold mb-2" style="color: ${fortune.color}">${fortune.value}</div>
<div class="text-warren-muted text-sm">${fortune.desc}</div>
<button onclick="copyFortuneCode('${fortune.value}')" class="mt-4 px-4 py-2 bg-warren-pink/20 border border-warren-pink/30 rounded-lg text-warren-pink text-xs font-mono hover:bg-warren-pink/30 transition-all inline-flex items-center gap-1.5">
<i data-lucide="copy" class="w-3.5 h-3.5"></i>
<span>Copy</span>
</button>
</div>
`;
lucide.createIcons();
}
}
function flipCard(index) {
if (tarotFlipped) return;
// Double-check localStorage
if (localStorage.getItem('warren_fortune')) {
showSealedFortune(localStorage.getItem('warren_fortune'));
return;
}
tarotFlipped = true;
const fortune = shuffledFortunes[index];
// Flip animation
const cards = document.querySelectorAll('.tarot-card');
cards[index].classList.add('flipped');
// Flash effect
const flash = document.createElement('div');
flash.className = 'tarot-flash';
cards[index].closest('.tarot-card-wrapper').appendChild(flash);
setTimeout(() => flash.remove(), 700);
// Particle burst
spawnTarotParticles(index);
// Save to localStorage
localStorage.setItem('warren_fortune', fortune.id);
// Dim other cards
document.querySelectorAll('.tarot-card-wrapper').forEach((wrapper, i) => {
if (i !== index) {
wrapper.style.opacity = '0.2';
wrapper.style.pointerEvents = 'none';
wrapper.style.transition = 'all 0.8s ease';
wrapper.style.filter = 'grayscale(1)';
}
});
// Show Etsy link after a delay
setTimeout(() => {
const etsyLink = document.getElementById('tarot-etsy-link');
if (etsyLink) {
etsyLink.classList.remove('hidden');
etsyLink.style.animation = 'reveal-bounce 0.5s cubic-bezier(0.16, 1, 0.3, 1)';
}
lucide.createIcons();
}, 900);
// Toast
setTimeout(() => {
showToast(`Fortune revealed: ${fortune.value}`, fortune.color, fortune.icon);
}, 600);
}
function spawnTarotParticles(cardIndex) {
const wrapper = document.querySelectorAll('.tarot-card-wrapper')[cardIndex];
const rect = wrapper.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const canvas = document.createElement('canvas');
canvas.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:100;';
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const particles = [];
const COLORS = ['#ff2d95', '#b44dff', '#00e5ff', '#ffffff', '#ffd700'];
// Main burst particles
for (let i = 0; i < 80; i++) {
const angle = (Math.PI * 2 / 80) * i + (Math.random() - 0.5) * 0.8;
const speed = Math.random() * 8 + 2;
particles.push({
x: cx,
y: cy,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
size: Math.random() * 3.5 + 1,
color: COLORS[Math.floor(Math.random() * COLORS.length)],
opacity: 1,
decay: Math.random() * 0.015 + 0.008,
gravity: 0.08
});
}
// Bigger sparkle particles
for (let i = 0; i < 15; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 4 + 1;
particles.push({
x: cx,
y: cy,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
size: Math.random() * 6 + 3,
color: '#ffffff',
opacity: 0.8,
decay: Math.random() * 0.02 + 0.015,
gravity: 0.02
});
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let alive = false;
particles.forEach(p => {
if (p.opacity <= 0) return;
alive = true;
p.x += p.vx;
p.y += p.vy;
p.vy += p.gravity;
p.vx *= 0.98;
p.opacity -= p.decay;
// Glow
ctx.beginPath();
ctx.arc(p.x, p.y, p.size * 4, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.globalAlpha = Math.max(0, p.opacity * 0.1);
ctx.fill();
// Core
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.globalAlpha = Math.max(0, p.opacity);
ctx.fill();
});
ctx.globalAlpha = 1;
if (alive) {
requestAnimationFrame(animate);
} else {
canvas.remove();
}
}
animate();
}
function copyFortuneCode(code) {
navigator.clipboard.writeText(code).then(() => {
showToast('Code copied! 🎉', '#ff2d95', '📋');
}).catch(() => {
const ta = document.createElement('textarea');
ta.value = code;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
showToast('Code copied! 🎉', '#ff2d95', '📋');
});
}