/* ===================== GHOST NEO-CITY SCRIPT ===================== */ /* ===== Helper ===== */ function pickRandom(arr){ return arr[Math.floor(Math.random() * arr.length)]; } /* ===== Image Viewer ===== */ const viewer = document.getElementById("viewer"); const viewerImg = document.getElementById("viewer-img"); const viewerClose = document.getElementById("viewerClose"); function openViewer(src){ if(!viewer || !viewerImg) return; viewerImg.src = src; viewer.classList.remove("hidden"); } function closeViewer(){ if(!viewer || !viewerImg) return; viewer.classList.add("hidden"); viewerImg.src = ""; } document.querySelectorAll(".terminal[data-img]").forEach(t=>{ t.addEventListener("click",()=>openViewer(t.dataset.img)); }); viewerClose && viewerClose.addEventListener("click",e=>{ e.stopPropagation(); closeViewer(); }); viewer && viewer.addEventListener("click",e=>{ if(e.target===viewer) closeViewer(); }); document.addEventListener("keydown",e=>{ if(e.key==="Escape") closeViewer(); }); /* ===== Static Viewer (fake video) ===== */ const staticViewer = document.getElementById("staticViewer"); const staticClose = document.getElementById("staticClose"); function openStatic(){ if(!staticViewer) return; staticViewer.classList.remove("hidden"); } function closeStatic(){ if(!staticViewer) return; staticViewer.classList.add("hidden"); } document.querySelectorAll(".opens-static").forEach(el=>{ el.addEventListener("click", openStatic); }); staticClose && staticClose.addEventListener("click", (e)=>{ e.stopPropagation(); closeStatic(); }); staticViewer && staticViewer.addEventListener("click", (e)=>{ if(e.target === staticViewer) closeStatic(); }); document.addEventListener("keydown", (e)=>{ if(e.key === "Escape") closeStatic(); }); /* ===== Desert cycling (Perimeter) ===== */ const desertFrames = [ "/photos/desert1.jpg", "/photos/desert2.jpg", "/photos/desert3.jpg" ]; let desertIndex = 0; const desertCam = document.getElementById("desertCam"); const desertFrameEl = document.getElementById("desertFrame"); function updateDesertUI(){ if(desertFrameEl){ desertFrameEl.textContent = (desertIndex + 1) + "/3"; } } if(desertCam){ updateDesertUI(); desertCam.addEventListener("click", ()=>{ openViewer(desertFrames[desertIndex]); desertIndex = (desertIndex + 1) % desertFrames.length; updateDesertUI(); }); } /* ===== Radio ===== */ const radioLines=[ "RADIO: weak carrier... repeat...", "CONTROL: remain indoors.", "STATIC: ... signal degraded ...", "FIELD: lights respond to movement.", "NOTICE: evacuation incomplete.", "TRANSMISSION: 03:17" ]; const radioEl=document.getElementById("radioText"); function setRadio(){ if(radioEl){ radioEl.textContent = pickRandom(radioLines); } } setRadio(); setInterval(setRadio, 22000 + Math.floor(Math.random()*10000)); /* ===== Logs ===== */ const sector = (document.body && document.body.dataset && document.body.dataset.sector) ? document.body.dataset.sector : "default"; const logPools={ residential:[ "[ HOUSING NOTICE ]\nOccupancy unknown\nTimestamp: 04:18", "[ FIELD NOTE ]\nApartments still warm\nTimestamp: 00:41" ], industrial:[ "[ PLANT STATUS ]\nPressure unstable\nTimestamp: 19:02", "[ MAINTENANCE ]\nPower draw abnormal\nTimestamp: 22:30" ], military:[ "[ DISPATCH ]\nPatrol check-in missed\nTimestamp: 06:11", "[ INTERNAL ]\nBasement access denied\nTimestamp: 03:17" ], perimeter:[ "[ SENSOR ]\nLong-range camera looping\nTimestamp: 05:44", "[ WARNING ]\nReturn path not guaranteed\nTimestamp: 03:17" ], default:[ "[ EMERGENCY ]\nEvacuation failed\nTimestamp: 03:17" ] }; const logEl = document.getElementById("logText"); if(logEl){ const logs = logPools[sector] || logPools.default; logEl.textContent = pickRandom(logs); // rare log if(Math.random() < 0.03){ logEl.textContent = "[ REDACTED ]\nYou have been here before.\nTimestamp: 00:12"; } } /* ===== Anomaly ping (no auto popups) ===== */ const anomalyPing = document.getElementById("anomalyPing"); const anomalyView = document.getElementById("anomalyView"); function showAnomalyPing(){ if(!anomalyPing) return; anomalyPing.classList.remove("hidden"); setTimeout(()=>anomalyPing.classList.add("hidden"), 2600); } const photoPool = [ "/photos/block.jpg", "/photos/factory.jpg", "/photos/room1.jpg", "/photos/room2.jpg", "/photos/army-hallway.jpg", "/photos/driving-room.jpg", "/photos/mountain1.jpg", "/photos/mountain2.jpg", "/photos/desert1.jpg", "/photos/desert2.jpg", "/photos/desert3.jpg", "/photos/bunker-door.jpg", "/photos/blur-person.jpg" ]; // Rare chance per page load if (Math.random() < 0.18) { setTimeout(showAnomalyPing, 1200 + Math.floor(Math.random()*2000)); } // Only opens if user clicks VIEW if(anomalyView){ anomalyView.addEventListener("click",(e)=>{ e.preventDefault(); openViewer(pickRandom(photoPool)); anomalyPing && anomalyPing.classList.add("hidden"); }); } /* ===== Floating anomalies: spawn inside PDA screen ===== */ (function spawnAnomalies(){ const screen = document.getElementById("pdaScreen") || document.querySelector(".pda-screen") || document.body; if(!screen) return; const COUNT = 5; const SPEED_MIN = 0.02; const SPEED_MAX = 0.06; function rand(min, max){ return min + Math.random() * (max - min); } const anomalies = []; function spawnOne(){ const el = document.createElement("div"); el.className = "anomaly"; screen.appendChild(el); const size = rand(120, 220); el.style.width = size + "px"; el.style.height = size + "px"; el.style.opacity = String(rand(0.14, 0.26)); const rect = screen.getBoundingClientRect(); let x = rand(40, Math.max(41, rect.width - size - 40)); let y = rand(40, Math.max(41, rect.height - size - 40)); let vx = rand(SPEED_MIN, SPEED_MAX) * (Math.random() < 0.5 ? -1 : 1); let vy = rand(SPEED_MIN, SPEED_MAX) * (Math.random() < 0.5 ? -1 : 1); el.style.transform = `translate(${x}px, ${y}px)`; anomalies.push({ el, x, y, vx, vy, size }); } for(let i=0;i w + a.size) a.x = -a.size; if(a.y < -a.size) a.y = h + a.size; if(a.y > h + a.size) a.y = -a.size; a.el.style.transform = `translate(${a.x}px, ${a.y}px)`; } requestAnimationFrame(tick); } tick(); })(); /* ===== Boot screen (once) + loading % ===== */ (function(){ const boot = document.getElementById("bootText"); if (!boot) return; const BOOT_KEY = "zone_boot_seen"; // already seen → remove instantly if (localStorage.getItem(BOOT_KEY)) { boot.remove(); return; } // percent counter const pctEl = boot.querySelector(".boot-pct span"); let p = 0; let timer = null; if (pctEl) { timer = setInterval(() => { const jump = (p < 75) ? 7 : (p < 95 ? 3 : 1); p = Math.min(100, p + jump); pctEl.textContent = p + "%"; if (p >= 100) clearInterval(timer); }, 220); } // remove after boot fade completes setTimeout(() => { if (timer) clearInterval(timer); boot.remove(); localStorage.setItem(BOOT_KEY, "1"); }, 6500); })();