LOBBY READY

Select Game Mode

Solo Arena Match

Fight bots locally to test out settings and warm up spreads.

Cloud Multiplayer

Private Lobby?
Cloud Standby

Locker Character Vault

Classic Cerulean Equip Outfit Case Drops Wheel Roulette ROLL Spin Roulette (50 🪙) Renegade Battle Pass Track TIER 1 DROP: Starter AllowanceClaim +50 🪙 TIER 2 DROP: Veteran VoucherClaim +100 🪙 TIER 3 DROP: Champion CacheClaim +200 🪙 Redeem Codes Verify Drop Key HEALTH: 100 ELIMS: 0 AR RIFLE PUMP SHOTGUN WALL RAMP const socket_protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const socket_base = ${socket_protocol}//${window.location.host}; let scene, camera, renderer, localPreview, socket; let moveF=false, moveB=false, moveL=false, moveR=false, yaw=0, pitch=0, activeGame=false, isLocalMode=false; let pVel=new THREE.Vector3(), pPos=new THREE.Vector3(0,1.8,0), activeSlot=1, hp=100, elims=0; const structures=[], opponents={}, botTargets=[]; let secondsPlayed = parseInt(localStorage.getItem('fn_time') || "0"); let userCoins = parseInt(localStorage.getItem('fn_coins') || "100"); let ownedSkins = JSON.parse(localStorage.getItem('fn_skins') || "[true,false,false,false,false]"); let activeSkinId = parseInt(localStorage.getItem('fn_active_skin') || "0"); let browsingSkinId = 0, milestoneAwarded = (secondsPlayed >= 60); const skinColors=[0x0078ff, 0xff2e2e, 0xffaa00, 0x8c00ff, 0x10b981]; const skinNames=["Classic Cerulean", "Crimson Fury", "Midas Gold", "Cosmic Roulette", "Playtime Chrono"]; const skinPrices=; const loggedRedemptions = JSON.parse(localStorage.getItem('fn_codes') || "{}"); let username = "PLAYER_" + Math.floor(1000 + Math.random()*9000); async function loadCloudProfile() { try { let res = await fetch(/api/profile?user=${username}); let d = await res.json(); if(d && d.coins !== undefined) { userCoins = d.coins; ownedSkins = d.skins; activeSkinId = d.activeSkin; document.getElementById('coin-count').innerText = userCoins; triggerAddictiveToast(SYNCED: ${username}); } } catch(e) {} } async function saveCloudProfile() { try { await fetch(/api/profile, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user: username, coins: userCoins, skins: ownedSkins, activeSkin: activeSkinId }) }); } catch(e){} } document.getElementById('username-field').value = username; document.getElementById('username-field').onchange = function() { username = this.value.trim().toUpperCase() || "PLAYER"; loadCloudProfile(); }; window.onload = () => { loadCloudProfile(); }; function routeToScreen(id) { document.querySelectorAll('.screen').forEach(s => s.classList.remove('active')); document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); document.getElementById(id).classList.add('active'); } document.getElementById('nav-play').onclick = function() { routeToScreen('screen-play'); this.classList.add('active'); }; document.getElementById('nav-locker').onclick = function() { routeToScreen('screen-locker'); this.classList.add('active'); renderLockerGrid(); }; document.getElementById('nav-gamble').onclick = function() { routeToScreen('screen-gamble'); this.classList.add('active'); }; document.getElementById('nav-pass').onclick = function() { routeToScreen('screen-pass'); this.classList.add('active'); updatePassButtons(); }; document.getElementById('nav-rewards').onclick = function() { routeToScreen('screen-rewards'); this.classList.add('active'); }; setInterval(() => { secondsPlayed++; document.getElementById('tracked-time').innerText = Math.floor(secondsPlayed/60) + "m " + (secondsPlayed%60) + "s"; if(secondsPlayed >= 60 && !milestoneAwarded) { milestoneAwarded = true; ownedSkins = true; saveCloudProfile(); triggerAddictiveToast("⏱️ REWARD TRACK: PLAYTIME CHRONO UNLOCKED!"); } if(secondsPlayed % 5 === 0) commitSaveMetricsToLocalStorage(); }, 1000); function commitSaveMetricsToLocalStorage() { localStorage.setItem('fn_time', secondsPlayed); localStorage.setItem('fn_coins', userCoins); localStorage.setItem('fn_skins', JSON.stringify(ownedSkins)); localStorage.setItem('fn_active_skin', activeSkinId); document.getElementById('coin-count').innerText = userCoins; } document.getElementById('btn-spin').onclick = () => { if(userCoins < 50) return alert("Need 50 V-Coins."); userCoins -= 50; commitSaveMetricsToLocalStorage(); let wheel = document.getElementById('wheel-display'); wheel.style.transform = rotate(${Math.floor(1440 + Math.random() * 2880)}deg); wheel.innerText = "ROLLING"; setTimeout(() => { if(Math.random() > 0.4) { ownedSkins=true; wheel.innerText="WIN!"; triggerAddictiveToast("🎰 COSMIC ROULETTE UNLOCKED!"); } else { let w = Math.floor(25+Math.random()*65); userCoins+=w; wheel.innerText=+${w}; } saveCloudProfile(); document.getElementById('coin-count').innerText = userCoins; }, 3500); }; function renderLockerGrid() { const grid = document.getElementById('locker-grid-box'); grid.innerHTML = ""; skinNames.forEach((name, i) => { let n = document.createElement('div'); n.className = "item-node tile item-tile" + (browsingSkinId===i?" active":""); let label = ownedSkins[i] ? "OWNED" : ${skinPrices[i]} 🪙; if(i===4 && !ownedSkins[i]) label = "PLAY 60s"; n.innerHTML =

${name}

${label}

; n.onclick = () => { browsingSkinId=i; renderLockerGrid(); document.getElementById('skin-title').innerText=name; document.getElementById('skin-ball').style.backgroundColor='#'+skinColors[i].toString(16); }; grid.appendChild(n); }); } document.getElementById('btn-equip').onclick = () => { if(ownedSkins[browsingSkinId]) { activeSkinId=browsingSkinId; saveCloudProfile(); triggerAddictiveToast(EQUIPPED: ${skinNames[browsingSkinId]}); } else if(userCoins >= skinPrices[browsingSkinId]) { userCoins-=skinPrices[browsingSkinId]; ownedSkins[browsingSkinId]=true; renderLockerGrid(); saveCloudProfile(); triggerAddictiveToast("SKIN UNLOCKED!"); } }; function updatePassButtons() { for(let i=1; i<=3; i++) { if(loggedRedemptions['bp-'+i]) { let btn = document.getElementById('bp-claim-'+i); btn.innerText = "CLAIMED"; btn.style.background = "#475569"; btn.disabled = true; } } } function executePassClaim(tier, val) { if(loggedRedemptions['bp-'+tier]) return; userCoins += val; loggedRedemptions['bp-'+tier] = true; saveCloudProfile(); updatePassButtons(); document.getElementById('coin-count').innerText = userCoins; triggerAddictiveToast(TIER CONQUERED: +${val} V-COINS); } document.getElementById('bp-claim-1').onclick = () => { executePassClaim(1, 50); }; document.getElementById('bp-claim-2').onclick = () => { executePassClaim(2, 100); }; document.getElementById('bp-claim-3').onclick = () => { executePassClaim(3, 200); }; document.getElementById('btn-promo').onclick = () => { let v = document.getElementById('promo-input').value.trim().toUpperCase(); if(loggedRedemptions[v]) return alert("Code used."); if(v==="FREE100") { userCoins+=100; loggedRedemptions[v]=true; triggerAddictiveToast("+100 V-COINS"); } else if(v==="ARENA500") { userCoins+=500; loggedRedemptions[v]=true; triggerAddictiveToast("+500 V-COINS"); } else { return alert("Invalid Code."); } saveCloudProfile(); document.getElementById('coin-count').innerText = userCoins; document.getElementById('promo-input').value = ""; }; document.getElementById('btn-solo').onclick = () => { startMatchArenaSector(true); }; // --- CLOUDFLARE MULTIPLAYER LOBBY HANDLERS --- document.getElementById('btn-host').onclick = () => { let isPrivate = document.getElementById('private-toggle').checked; let code = "ROOM" + Math.floor(1000 + Math.random() * 9000); document.getElementById('code-display').innerText = ROOM CODE: ${code}; document.getElementById('code-display').style.display = 'block'; socket = new WebSocket(${socket_base}/ws?room=${code}&private=${isPrivate}); bindSocketEvents(); }; document.getElementById('btn-join').onclick = () => { let entryCode = document.getElementById('code-input').value.trim().toUpperCase(); if(!entryCode) return alert("Enter code!"); socket = new WebSocket(${socket_base}/ws?room=${entryCode}); bindSocketEvents(); }; function bindSocketEvents() { socket.onopen = () => { document.getElementById('status').innerText="ONLINE"; startMatchArenaSector(false); }; socket.onmessage = (event) => { let d = JSON.parse(event.data); if(d.type==='reject') { alert(d.reason); location.reload(); } if(d.type==='move' && opponents.mesh) { opponents.mesh.position.set(d.p, d.p, d.p); opponents.mesh.rotation.y=d.r; } if(d.type==='build') spawnStructureMesh(d.t, d.p, d.r, false); if(d.type==='shot') renderWeaponBeamTracer(new THREE.Vector3(d.f,d.f,d.f), new THREE.Vector3(d.t,d.t,d.t), 0xec4899); if(d.type==='hit') { hp-=d.damage; document.getElementById('hp-display').innerText=hp; if(hp<=0){ hp=100; pPos.set((Math.random()-0.5)*20,1.8,(Math.random()-0.5)*20); socket.send(JSON.stringify({type:'elim'})); }} if(d.type==='elim') { elims++; document.getElementById('elims-display').innerText=elims; userCoins+=50; document.getElementById('coin-count').innerText=userCoins; saveCloudProfile(); } }; } function startMatchArenaSector(solo) { isLocalMode = solo; document.getElementById('global-nav').style.display = 'none'; document.getElementById('screen-play').classList.remove('active'); document.getElementById('hud').style.display = 'block'; document.body.requestPointerLock(); if(!scene) build3DGraphicsEngine(); if(isLocalMode) respawnTargetBot(); activeGame = true; } function build3DGraphicsEngine() { scene = new THREE.Scene(); scene.background = new THREE.Color(0x060814); camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.copy(pPos); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById('canvas').appendChild(renderer.domElement); scene.add(new THREE.AmbientLight(0xffffff, 0.5)); let light = new THREE.DirectionalLight(0x38bdf8, 0.8); light.position.set(10,25,10); scene.add(light); let floor = new THREE.Mesh(new THREE.PlaneGeometry(100,100), new THREE.MeshStandardMaterial({color:0x0f172a})); floor.rotation.x = -Math.PI/2; scene.add(floor); let grid = new THREE.GridHelper(100, 25, 0x38bdf8, 0x1e293b); grid.position.y = 0.01; scene.add(grid); localPreview = new THREE.Mesh(new THREE.BoxGeometry(4,3,0.2), new THREE.MeshBasicMaterial({color:0x38bdf8, wireframe:true, transparent:true, opacity:0.4})); scene.add(localPreview); opponents.mesh = new THREE.Mesh(new THREE.CapsuleGeometry(0.5, 1.8, 4, 8), new THREE.MeshStandardMaterial({color: skinColors[activeSkinId]})); scene.add(opponents.mesh); document.addEventListener('keydown', (e) => { if(e.code === 'Escape') { activeGame=false; document.exitPointerLock(); document.getElementById('global-nav').style.display='block'; document.getElementById('screen-play').classList.add('active'); document.getElementById('hud').style.display='none'; return; } if(!activeGame) return; if(e.code==='KeyW') moveF=true; if(e.code==='KeyS') moveB=true; if(e.code==='KeyA') moveL=true; if(e.code==='KeyD') moveR=true; if(e.code==='Digit1') { activeSlot=1; localPreview.visible=false; } if(e.code==='Digit2') { activeSlot=4; localPreview.visible=false; } if(e.code==='Digit3') { activeSlot=2; localPreview.visible=true; } if(e.code==='Digit4') { activeSlot=3; localPreview.visible=true; } document.getElementById('w1').classList.toggle('active', activeSlot===1); document.getElementById('w4').classList.toggle('active', activeSlot===4); document.getElementById('w2').classList.toggle('active', activeSlot===2); document.getElementById('w3').classList.toggle('active', activeSlot===3); }); document.addEventListener('keyup', (e) => { if(e.code==='KeyW') moveF=false; if(e.code==='KeyS') moveB=false; if(e.code==='KeyA') moveL=false; if(e.code==='KeyD') moveR=false; }); document.addEventListener('mousemove', (e) => { if (document.pointerLockElement !== document.body) return; yaw -= e.movementX0.002; pitch -= e.movementY0.002; pitch = Math.max(-1.4, Math.min(1.4, pitch)); camera.rotation.order = "YXZ"; camera.rotation.set(pitch, yaw, 0); }); document.addEventListener('mousedown', (e) => { if(document.pointerLockElement !== document.body) { if(activeGame) document.body.requestPointerLock(); return; } if(e.button===0 && activeGame) { if(activeSlot===1 || activeSlot===4) deployFirearmsEngine(); else executeBuildPlacement(); } }); window.addEventListener('resize', () => { camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); graphicsLoopEngine(); } function calculateRaycastGrid() { let v = new THREE.Vector3(); camera.getWorldDirection(v); let t = pPos.clone().add(v.multiplyScalar(5)); return { p: new THREE.Vector3(Math.round(t.x/4)*4, Math.max(0, Math.round(t.y/3)*3) + 1.5, Math.round(t.z/4)*4), r: Math.abs(Math.cos(yaw)) > Math.abs(Math.sin(yaw)) ? 0 : Math.PI/2 }; } function executeBuildPlacement() { let g = calculateRaycastGrid(); let type = activeSlot === 2 ? 'wall' : 'ramp'; spawnStructureMesh(type, [g.p.x, g.p.y, g.p.z], g.r, true); if(socket && !isLocalMode) socket.send(JSON.stringify({type:'build', t:type, p:[g.p.x, g.p.y, g.p.z], r:g.r})); } function spawnStructureMesh(t, p, r, local) { let m = new THREE.Mesh(new THREE.BoxGeometry(4, t==='wall'?3:0.2, t==='wall'?0.2:4.5), new THREE.MeshStandardMaterial({color: local?skinColors[activeSkinId]:0xec4899})); m.position.set(p, p, p); m.rotation.y = r; if(t==='ramp') m.rotation.x = -Math.PI/6; scene.add(m); structures.push(m); } function respawnTargetBot() { if(opponents.mesh) { opponents.mesh.position.set((Math.random()-0.5)*30, 0.9, (Math.random()-0.5)*30); } botTargets.vx=(Math.random()-0.5)*5; botTargets.vz=(Math.random()-0.5)*5; } function deployFirearmsEngine() { let isShotgun = (activeSlot === 4); let pellets = isShotgun ? 6 : 1; let hit = false; for(let i=0; i 0 && hits.distance < (isShotgun ? 25 : 60)) hit = true; } if(hit) { document.getElementById('hitmarker').style.display='block'; setTimeout(() => { document.getElementById('hitmarker').style.display='none'; }, 60); if(isLocalMode) { elims++; document.getElementById('elims-display').innerText=elims; userCoins+=25; saveCloudProfile(); document.getElementById('coin-count').innerText = userCoins; respawnTargetBot(); } else if(socket) { socket.send(JSON.stringify({type:'hit', damage: isShotgun ? 40 : 25})); } } } function renderWeaponBeamTracer(f, t, col) { let geo = new THREE.BufferGeometry().setFromPoints([f, t]), mat = new THREE.LineBasicMaterial({color:col}), line = new THREE.Line(geo, mat); scene.add(line); setTimeout(() => { scene.remove(line); geo.dispose(); }, 45); } function triggerAddictiveToast(txt) { let t=document.getElementById('toast-notification'); t.innerText=txt; t.classList.add('pop'); setTimeout(()=>t.classList.remove('pop'), 2200); } function graphicsLoopEngine() { if (!activeGame) return; requestAnimationFrame(graphicsLoopEngine); pVel.x -= pVel.x * 10 * 0.016; pVel.z -= pVel.z * 10 * 0.016; let cDir = new THREE.Vector3(); camera.getWorldDirection(cDir); let sDir = new THREE.Vector3().crossVectors(camera.up, cDir).normalize(); if (moveF) pVel.addScaledVector(cDir, 120 * 0.016); if (moveB) pVel.addScaledVector(cDir, -120 * 0.016); if (moveL) pVel.addScaledVector(sDir, 120 * 0.016); if (moveR) pVel.addScaledVector(sDir, -120 * 0.016); pPos.x += pVel.x * 0.016; pPos.z += pVel.z * 0.016; camera.position.copy(pPos); if (localPreview && localPreview.visible) { let g = calculateRaycastGrid(); localPreview.position.copy(g.p); localPreview.rotation.y = g.r; } if(isLocalMode) { opponents.mesh.position.x += botTargets.vx0.016; opponents.mesh.position.z += botTargets.vz0.016; if(Math.abs(opponents.mesh.position.x)>40)botTargets.vx*=-1; } else if(socket && socket.readyState === WebSocket.OPEN) { socket.send(JSON.stringify({type:'move', p:[pPos.x, pPos.y, pPos.z], r:yaw})); } renderer.render(scene, camera); }