MyCraft

Create, play, watch, and customise

Archives

Saved posts, builds and screenshots. Click a card to open the full page.

Vintage Castle Build

Community favourite — redstone gates

3 days ago

Glitched Realm

A surreal dimension

1 week ago
`; const blob = new Blob([pageHtml], { type: 'text/html' }); const url = URL.createObjectURL(blob); // open in new tab; browsers may block popups if not triggered by user gesture, but this is called from clicks window.open(url, '_blank'); // revoke later to free memory setTimeout(()=>URL.revokeObjectURL(url), 60_000); } /* ---------- Navigation within single page (visual view switching) ---------- */ function setView(view) { state.view = view; document.querySelectorAll('#content > div[id^="view-"]').forEach(el => el.style.display = 'none'); const el = document.getElementById('view-'+view); if(el) el.style.display = ''; document.querySelectorAll('.nav-btn').forEach(n => n.classList.toggle('active', n.dataset.view===view)); } document.querySelectorAll('[data-view]').forEach(btn => btn.addEventListener('click', ()=>setView(btn.dataset.view))); setView(state.view); /* ---------- Wire up all "open a page" buttons/cards ---------- */ function wireOpeners() { document.querySelectorAll('.openPage, .goto').forEach(el => { el.style.cursor = 'pointer'; el.addEventListener('click', (e)=>{ // find dataset values const page = el.dataset.page || el.dataset.page; const title = el.dataset.title || el.textContent.trim().split('\\n')[0] || page; // For archive/game/video items we pass the item title in payload openGeneratedPage(page, title, { item: title }); e.stopPropagation(); }); }); } wireOpeners(); // initial /* If new elements are added dynamically, call wireOpeners() again. */ /* ---------- Music control ---------- */ const music = document.getElementById('bgMusic'); const musicToggle = document.getElementById('music-toggle'); let musicPlaying = false; musicToggle.addEventListener('click', ()=>{ if(!musicPlaying){ music.volume = 0.18; music.play().catch(()=>{ alert('Autoplay blocked. Click the music button again.') }); musicToggle.textContent='Pause Music'; } else { music.pause(); musicToggle.textContent='Play Music'; } musicPlaying = !musicPlaying; }); /* ---------- Sign up ---------- */ const signinBtn = document.getElementById('signinBtn'); const signoutBtn = document.getElementById('signoutBtn'); const displayName = document.getElementById('displayName'); const displaySub = document.getElementById('displaySub'); function refreshUserUI(){ if(state.user){ displayName.textContent = state.user.name || 'Player'; displaySub.textContent = 'Points: ' + (state.user.points ?? state.points); signoutBtn.style.display = ''; signinBtn.style.display = 'none'; document.getElementById('smallAvatar').textContent = state.user.avatarInitials || getInitials(state.user.name); document.getElementById('smallAvatar').style.background = 'linear-gradient(135deg,'+state.avatar.hair+','+state.avatar.eyes+')'; } else { displayName.textContent = 'Guest'; displaySub.textContent = 'Not signed in'; signoutBtn.style.display = 'none'; signinBtn.style.display = ''; document.getElementById('smallAvatar').textContent = '?'; document.getElementById('smallAvatar').style.background = 'linear-gradient(135deg,var(--accent1),var(--accent3))'; } } refreshUserUI(); signinBtn.addEventListener('click', ()=>{ const name = prompt("Create a display name (min 3 chars):"); if(!name || name.length<3) return alert('Name too short.'); const email = prompt("Email (optional):"); state.user = { name: name, email: email||'', points: state.points, avatarInitials: getInitials(name) }; localStorage.setItem('mycraft_user', JSON.stringify(state.user)); refreshUserUI(); alert('Welcome ' + name + '! Starter points added.'); }); signoutBtn.addEventListener('click', ()=>{ if(confirm('Sign out?')){ state.user=null; localStorage.removeItem('mycraft_user'); refreshUserUI(); }}); function getInitials(name){ return name.split(' ').map(s=>s[0]||'').slice(0,2).join('').toUpperCase(); } /* ---------- Avatar builder (unchanged functionality) ---------- */ const hairColors = ['#4b2e83','#2b8aff','#ff6b6b','#ffb86b','#2bffb8','#0d9488','#3b82f6','#f472b6']; const skinTones = ['#f5d7c3','#f0c8a8','#e4b99a','#d8a781','#b8835a','#a76b4a']; const eyeColors = ['#052a36','#0b6b3a','#1f2937','#3b82f6','#7b61ff','#333333']; const accOptions = ['transparent','#7b61ff','#ffbe0b','#00d4ff','#000000','#ffd6e0']; function makeSwatches(containerId, list, prop){ const container = document.getElementById(containerId); if(!container) return; container.innerHTML = ''; list.forEach(col=>{ const b = document.createElement('div'); b.className='swatch'; b.style.background = col; if(state.avatar[prop] === col) b.classList.add('selected'); b.addEventListener('click', ()=>{ state.avatar[prop] = col; document.querySelectorAll('#'+containerId+' .swatch').forEach(s=>s.classList.remove('selected')); b.classList.add('selected'); updateAvatarPreview(); }); container.appendChild(b); }); } makeSwatches('swatchesHair', hairColors, 'hair'); makeSwatches('swatchesSkin', skinTones, 'skin'); makeSwatches('swatchesEyes', eyeColors, 'eyes'); makeSwatches('swatchesAcc', accOptions, 'acc'); const pHead = document.getElementById('p-head'); const pEyes = document.getElementById('p-eyes'); const pMouth = document.getElementById('p-mouth'); const pAcc = document.getElementById('p-acc'); const scaleRange = document.getElementById('scaleRange'); const rotRange = document.getElementById('rotRange'); if(scaleRange) scaleRange.addEventListener('input', ()=>{ state.avatar.scale = parseFloat(scaleRange.value); updateAvatarPreview(); }); if(rotRange) rotRange.addEventListener('input', ()=>{ state.avatar.rot = parseFloat(rotRange.value); updateAvatarPreview(); }); function updateAvatarPreview(){ if(!pHead) return; pHead.style.background = state.avatar.skin; pHead.style.width = (140 * state.avatar.scale) + 'px'; pHead.style.height = (140 * state.avatar.scale) + 'px'; pHead.style.transform = 'translateX(-50%) rotate(' + state.avatar.rot + 'deg)'; pEyes.style.background = state.avatar.eyes; pEyes.style.width = (80 * state.avatar.scale) + 'px'; pEyes.style.transform = 'translateX(-50%) rotate(' + state.avatar.rot/2 + 'deg)'; pMouth.style.background = state.avatar.mouth; pMouth.style.transform = 'translateX(-50%) rotate(' + state.avatar.rot/1.5 + 'deg)'; pAcc.style.background = state.avatar.acc; pAcc.style.height = (60 * state.avatar.scale) + 'px'; if(state.avatar.acc !== 'transparent') pAcc.style.background = `linear-gradient(90deg, ${state.avatar.acc}, rgba(255,255,255,0.04))`; document.getElementById('smallAvatar').style.background = `linear-gradient(135deg, ${state.avatar.hair}, ${state.avatar.eyes})`; } updateAvatarPreview(); const avatarModal = document.getElementById('avatarModal'); function openAvatarBuilder(){ avatarModal.style.display='block'; window.scrollTo({top:document.body.scrollHeight, behavior:'smooth'}); } function closeAvatarBuilder(){ avatarModal.style.display='none'; } document.getElementById('open-avatar').addEventListener('click', openAvatarBuilder); document.getElementById('saveToProfile')?.addEventListener('click', ()=>{ if(!state.user) return alert('Sign in to save your avatar to your profile.'); state.user.avatar = state.avatar; state.user.avatarInitials = getInitials(state.user.name); localStorage.setItem('mycraft_user', JSON.stringify(state.user)); localStorage.setItem('mycraft_avatar', JSON.stringify(state.avatar)); alert('Avatar saved to profile.'); refreshUserUI(); }); document.getElementById('exportPNG')?.addEventListener('click', ()=>{ const canvas = document.createElement('canvas'); canvas.width=600; canvas.height=600; const ctx = canvas.getContext('2d'); const bg = ctx.createLinearGradient(0,0,600,600); bg.addColorStop(0,'#071a27'); bg.addColorStop(1,'#04202a'); ctx.fillStyle=bg; ctx.fillRect(0,0,600,600); ctx.save(); ctx.translate(300,200); ctx.rotate((state.avatar.rot||0)*Math.PI/180); const scale = state.avatar.scale||1; ctx.fillStyle = state.avatar.skin||'#f5d7c3'; ctx.beginPath(); ctx.arc(0,0,70*scale,0,Math.PI*2); ctx.fill(); ctx.fillStyle = state.avatar.eyes||'#052a36'; ctx.fillRect(-40*scale,18*scale,80*scale,14*scale); ctx.fillStyle = state.avatar.mouth||'#ff6b6b'; ctx.fillRect(-30*scale,45*scale,60*scale,12*scale); ctx.restore(); if(state.avatar.acc && state.avatar.acc!=='transparent'){ ctx.fillStyle = state.avatar.acc; ctx.fillRect(150,40,300,80); } ctx.fillStyle='rgba(255,255,255,0.07)'; ctx.font='24px Nunito'; ctx.fillText('MyCraft Avatar',24,580); const url = canvas.toDataURL('image/png'); const a = document.createElement('a'); a.href=url; a.download='mycraft-avatar.png'; document.body.appendChild(a); a.click(); a.remove(); }); document.getElementById('saveAvatarLocal')?.addEventListener('click', ()=>{ const data = JSON.stringify(state.avatar, null, 2); const blob = new Blob([data], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href=url; a.download='mycraft-avatar.json'; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); alert('Avatar JSON saved to your device'); }); /* ---------- Make all opener elements clickable (again, for any dynamic additions) ---------- */ document.addEventListener('click', (e)=>{ const t = e.target.closest('.openPage, .goto'); if(t) { const page = t.dataset.page; const title = t.dataset.title || (t.textContent||page).trim().split('\\n')[0]; if(page) openGeneratedPage(page, title, { item: title }); e.preventDefault(); } }); /* ---------- Small UX niceties ---------- */ window.addEventListener('keydown', (e)=>{ if(e.key==='a' && e.ctrlKey) { openAvatarBuilder(); e.preventDefault(); } if(e.key==='m' && e.ctrlKey) { musicToggle.click(); e.preventDefault(); } }); setInterval(()=>{ document.querySelectorAll('.card').forEach((c,i)=>{ c.style.transform = `translateY(${Math.sin(Date.now()/1400 + i) * 2}px)`; }); }, 400); /* ---------- If an avatar was saved previously, apply it ---------- */ if(localStorage.getItem('mycraft_avatar')) { state.avatar = JSON.parse(localStorage.getItem('mycraft_avatar')); makeSwatches('swatchesHair', hairColors, 'hair'); makeSwatches('swatchesSkin', skinTones, 'skin'); makeSwatches('swatchesEyes', eyeColors, 'eyes'); makeSwatches('swatchesAcc', accOptions, 'acc'); if(scaleRange) scaleRange.value = state.avatar.scale || 1; if(rotRange) rotRange.value = state.avatar.rot || 0; updateAvatarPreview(); }