Greasy Fork is available in English.
!NOT WORKING RIGHT NOW GOING TO FIX IT SOON!
// ==UserScript==
// @name Survev.io script
// @namespace VoidBacon
// @description !NOT WORKING RIGHT NOW GOING TO FIX IT SOON!
// @version 2.7
// @author John pork
// @match *://survev.io/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
/*John pork says have fun*/
(function () {
'use strict';
const inject = document.createElement('script');
inject.textContent = `(function(){
window.__hbCandidates=[];
const _o=Object.defineProperty;
Object.defineProperty=function(obj,prop,desc){
try{
if(prop==='game'&&desc&&'value'in desc&&
(desc.value===null||desc.value===undefined||desc.value===false)&&
obj&&typeof obj==='object'&&obj!==window&&obj!==document)
window.__hbCandidates.push(obj);
}catch(e){}
return _o.call(Object,obj,prop,desc);
};
})();`;
(document.head || document.documentElement).appendChild(inject);
inject.remove();
function findRe() {
if (window.__Re?.game !== undefined) return window.__Re;
for (const c of (window.__hbCandidates || []))
try { if (c && 'audioManager' in c && 'inputBinds' in c && 'pixi' in c) return c; } catch(e) {}
return null;
}
function findMapInfo(game) {
for (const k of Object.keys(game)) {
const v = game[k];
if (!v || typeof v !== 'object') continue;
for (const sk of Object.keys(v)) {
const sv = v[sk];
if (sv && typeof sv.ZBG === 'function') {
const pool = sv.ZBG();
if (pool.length > 0 && pool[0]?.ceiling !== undefined)
return { mapKey: k, buildPoolKey: sk };
}
}
}
return null;
}
const cfg = { Esp:true, names:true, healthBars:true, lockOn:false, magnet:false, xray:false };
const KEY_W=87, KEY_A=65, KEY_S=83, KEY_D=68;
let _zoomFn=null, _mapInfo=null;
let _nearest=null;
let isPanelVisible=false, panelX=null, panelY=null;
let _fps=0, _fpsFrames=0, _fpsLast=performance.now();
let _ms=0, _msLast=performance.now();
let _magnetKeys={ w:false, a:false, s:false, d:false };
function tickXray(game) {
if (!cfg.xray) return;
if (!_mapInfo) _mapInfo = findMapInfo(game);
if (!_mapInfo) return;
const pool = game[_mapInfo.mapKey]?.[_mapInfo.buildPoolKey]?.ZBG?.() || [];
for (const b of pool) {
if (!b.active||!b.ceiling) continue;
b.ceiling.fadeAlpha = 0;
for (const img of (b.imgs||[])) if (img.isCeiling&&img.sprite) img.sprite.alpha=0;
}
}
function restoreXray(game) {
if (!_mapInfo) return;
const pool = game[_mapInfo.mapKey]?.[_mapInfo.buildPoolKey]?.ZBG?.() || [];
for (const b of pool) {
if (!b.ceiling) continue;
b.ceiling.fadeAlpha=1;
for (const img of (b.imgs||[])) if (img.isCeiling&&img.sprite) img.sprite.alpha=img.sprite.imgAlpha??1;
}
}
function getZoom(game) {
if (_zoomFn) { try { return _zoomFn(); } catch(e) { _zoomFn=null; } }
for (const k of Object.keys(game)) {
const v = game[k];
if (!v||typeof v!=='object'||typeof v.screenWidth!=='number'||v.screenWidth<100) continue;
for (const fk of Object.keys(v)) {
if (typeof v[fk]!=='function') continue;
try { const z=v[fk](); if(z>10&&z<500){_zoomFn=v[fk].bind(v);return z;} } catch(e) {}
}
}
return 30;
}
function clearMagnetKeys(input) {
if (_magnetKeys.w) { input.keys[KEY_W]=false; _magnetKeys.w=false; }
if (_magnetKeys.a) { input.keys[KEY_A]=false; _magnetKeys.a=false; }
if (_magnetKeys.s) { input.keys[KEY_S]=false; _magnetKeys.s=false; }
if (_magnetKeys.d) { input.keys[KEY_D]=false; _magnetKeys.d=false; }
}
function applyMagnet(input, dx, dy) {
const threshold = 0.3;
const len = Math.sqrt(dx*dx + dy*dy);
if (len === 0) { clearMagnetKeys(input); return; }
const nx = dx/len, ny = dy/len;
const wantW = ny < -threshold; const wantS = ny > threshold;
const wantA = nx < -threshold; const wantD = nx > threshold;
input.keys[KEY_W]=wantW; _magnetKeys.w=wantW;
input.keys[KEY_S]=wantS; _magnetKeys.s=wantS;
input.keys[KEY_A]=wantA; _magnetKeys.a=wantA;
input.keys[KEY_D]=wantD; _magnetKeys.d=wantD;
}
const modeMap = {1:'Solo',2:'Duo',3:'Trio',4:'Squad'};
function timeAgo(d) {
const s=Math.floor((Date.now()-new Date(d))/1000);
return s<60?`${s}s`:s<3600?`${Math.floor(s/60)}m`:s<86400?`${Math.floor(s/3600)}h`:`${Math.floor(s/86400)}d`;
}
function fetchStats(slug) {
if (!slug) return;
const box=document.getElementById('_hbStatsBox'), hist=document.getElementById('_hbHistoryBox');
if(box) box.innerHTML='<span style="color:#b8b4ac;font-size:10px">loading...</span>';
if(hist) hist.innerHTML='<span style="color:#b8b4ac;font-size:10px">loading...</span>';
fetch('https://api.survev.io/api/user_stats',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({slug,interval:'alltime',mapIdFilter:'-1'})})
.then(r=>r.json()).then(d=>{
if(!box) return;
if(!d||d.banned){box.innerHTML='<span style="color:#e87a7a;font-size:10px">not found</span>';return;}
const kpg=d.games>0?(d.kills/d.games).toFixed(2):'0.00';
const wPct=d.games>0?((d.wins/d.games)*100).toFixed(1):'0.0';
box.innerHTML=`<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:2px;text-align:center">${[['WINS',d.wins??0],['KILLS',d.kills??0],['GAMES',d.games??0],['K/G',kpg],['WIN%',wPct+'%']].map(([l,v])=>`<div style="padding:4px 0"><div style="font-size:15px;font-weight:700;color:#2a2a2a">${v}</div><div style="font-size:8px;color:#bbb;letter-spacing:1px;margin-top:1px">${l}</div></div>`).join('')}</div>`;
}).catch(()=>{if(box) box.innerHTML='<span style="color:#e87a7a;font-size:10px">error</span>';});
fetch('https://api.survev.io/api/match_history',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({slug,offset:0,count:10,teamModeFilter:7})})
.then(r=>r.json()).then(matches=>{
if(!hist) return;
if(!Array.isArray(matches)||!matches.length){hist.innerHTML='<span style="color:#b8b4ac;font-size:10px">no matches</span>';return;}
hist.innerHTML=`<div style="border-radius:7px;overflow:hidden;border:1px solid #e4e0d8">
<div style="display:grid;grid-template-columns:40px 40px 1fr 50px 28px;gap:4px;padding:5px 10px;background:#edeae4">${['RANK','MODE','KILLS','DMG','AGO'].map(h=>`<span style="font-size:8px;color:#b0aca4;letter-spacing:1px">${h}</span>`).join('')}</div>
${matches.map(m=>{
const rCol=m.rank===1?'#5bc470':m.rank<=3?'#e8c060':'#aaa';
const mCol=m.team_count===1?'#c8a8ff':m.team_count===2?'#80c8ff':m.team_count===3?'#80e8a0':'#ffb060';
return `<div style="display:grid;grid-template-columns:40px 40px 1fr 50px 28px;gap:4px;align-items:center;padding:7px 10px;border-bottom:1px solid #f0ede8;cursor:pointer;transition:background .1s" onmouseover="this.style.background='#f8f6f2'" onmouseout="this.style.background=''" onclick="window._hbShowMatch('${m.guid}','${slug}')">
<span style="font-size:9px;font-weight:700;color:${rCol}">${m.rank===1?'🥇':'#'+m.rank}</span>
<span style="font-size:9px;font-weight:600;color:${mCol}">${modeMap[m.team_count]||'?'}</span>
<span style="font-size:9px;color:#666">${m.kills??0}k / ${m.team_kills??0}tk</span>
<span style="font-size:9px;color:#888">${Math.round((m.damage_dealt??0)/1000*10)/10}k</span>
<span style="font-size:9px;color:#bbb;text-align:right">${timeAgo(m.end_time)}</span>
</div>`;
}).join('')}</div>`;
}).catch(()=>{if(hist) hist.innerHTML='<span style="color:#e87a7a;font-size:10px">error</span>';});
}
window._hbShowMatch = function(guid, slug) {
const detail=document.getElementById('_hbMatchDetail');
if(!detail) return;
detail.style.display='block';
detail.innerHTML='<div style="color:#b8b4ac;font-size:10px;padding:8px">loading...</div>';
fetch('https://api.survev.io/api/match_data',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({gameId:guid})})
.then(r=>r.json()).then(players=>{
if(!Array.isArray(players)||!players.length){detail.innerHTML='<span style="color:#e87a7a;font-size:10px;padding:8px">no data</span>';return;}
const rows=players.map(p=>{
const isMe=p.slug===slug;
const t=Math.round(p.time_alive??0);
const rCol=p.rank===1?'#5bc470':p.rank<=3?'#e8c060':'#aaa';
return `<div style="display:grid;grid-template-columns:28px 1fr 24px 44px 44px 38px;gap:3px;align-items:center;padding:5px 10px;border-bottom:1px solid #f0ede8;${isMe?'background:#edf8f0':''}">
<span style="font-size:9px;font-weight:700;color:${rCol}">#${p.rank}</span>
<span style="font-size:9px;font-weight:${isMe?700:400};color:${isMe?'#2a8a44':'#444'};overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${p.slug||p.username||'?'}</span>
<span style="font-size:9px;color:#888">${p.kills??0}</span>
<span style="font-size:9px;color:#888">${Math.round(p.damage_dealt??0)}</span>
<span style="font-size:9px;color:#aaa">${Math.round(p.damage_taken??0)}</span>
<span style="font-size:9px;color:#bbb">${Math.floor(t/60)}:${String(t%60).padStart(2,'0')}</span>
</div>`;
}).join('');
detail.innerHTML=`
<div style="display:flex;justify-content:space-between;align-items:center;padding:7px 10px;background:#edeae4;border-bottom:1px solid #e4e0d8">
<span style="font-size:9px;color:#888;letter-spacing:.5px">MATCH DETAIL</span>
<span style="cursor:pointer;color:#aaa;padding:2px 6px" onclick="document.getElementById('_hbMatchDetail').style.display='none'">✕</span>
</div>
<div style="display:grid;grid-template-columns:28px 1fr 24px 44px 44px 38px;gap:3px;padding:5px 10px;background:#f4f2ee">
${['#','PLAYER','K','DEALT','TAKEN','TIME'].map(h=>`<span style="font-size:8px;color:#b0aca4;letter-spacing:.5px">${h}</span>`).join('')}
</div>
<div style="max-height:200px;overflow-y:auto">${rows}</div>`;
}).catch(()=>{detail.innerHTML='<span style="color:#e87a7a;font-size:10px;padding:8px">error</span>';});
};
function startOverlay(Re) {
if (window._hbRunning) return;
window._hbRunning = true;
const canvas = document.createElement('canvas');
canvas.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998;';
document.body.appendChild(canvas);
window._hbCanvas = canvas;
const ctx = canvas.getContext('2d');
const resize = () => { canvas.width=innerWidth; canvas.height=innerHeight; };
resize();
window.addEventListener('resize', resize);
(function draw() {
window._hbRaf = requestAnimationFrame(draw);
ctx.clearRect(0, 0, canvas.width, canvas.height);
_fpsFrames++;
const now = performance.now();
const delta = now - _msLast; _msLast = now;
_ms = Math.round(_ms * 0.85 + delta * 0.15);
if (now - _fpsLast >= 1000) {
_fps=_fpsFrames; _fpsFrames=0; _fpsLast=now;
const fpsEl=document.getElementById('_hbFps');
if(fpsEl){fpsEl.textContent=`${_fps} fps`;fpsEl.style.color=_fps>=55?'#5bc470':_fps>=30?'#e8c060':'#e87a7a';}
}
const msEl=document.getElementById('_hbMs');
if(msEl){msEl.textContent=`${_ms} ms`;msEl.style.color=_ms<=16?'#5bc470':_ms<=33?'#e8c060':'#e87a7a';}
const killEl=document.querySelector('.js-ui-player-kills');
const killDisp=document.getElementById('_hbKills');
if(killEl&&killDisp) killDisp.textContent=killEl.textContent||'0';
const game=Re.game;
if (!game?.initialized) { if (cfg.magnet && Re.input) clearMagnetKeys(Re.input); return; }
const barn=game.kszNK, myId=game.FCmIQL;
if (!barn||!myId) return;
tickXray(game);
const zoom=getZoom(game), STUD=zoom*16;
const players=barn.playerPool.ZBG();
const myInfo=barn.getPlayerInfo(myId);
const myTeam=myInfo?.teamId, myGroup=myInfo?.groupId;
const me=players.find(p=>p.__id===myId);
if (!me?.active) return;
const msx=me.container.x, msy=me.container.y;
let nearestEnemy=null, nearestDist=Infinity;
for (const p of players) {
if (!p.active||p.__id===myId||p.dead||!p.container.visible) continue;
const info=barn.getPlayerInfo(p.__id);
const isMate=(myGroup>0&&info?.groupId>0&&info.groupId===myGroup)||(myTeam>0&&info?.teamId>0&&info.teamId===myTeam);
if (isMate) continue;
const d=Math.hypot(p.container.x-msx,p.container.y-msy);
if (d<nearestDist){nearestDist=d;nearestEnemy=p;}
}
_nearest=nearestEnemy?{sx:nearestEnemy.container.x,sy:nearestEnemy.container.y}:null;
if (cfg.lockOn && _nearest && Re.input) { Re.input.mousePos.x=_nearest.sx; Re.input.mousePos.y=_nearest.sy; }
if (cfg.magnet && Re.input && nearestEnemy) {
const distStuds=nearestDist/STUD;
if (distStuds<=3) {
applyMagnet(Re.input, nearestEnemy.container.x-msx, nearestEnemy.container.y-msy);
const pulse=(Date.now()%600)/600;
ctx.save(); ctx.globalAlpha=0.6+Math.sin(pulse*Math.PI*2)*0.4;
ctx.beginPath(); ctx.arc(nearestEnemy.container.x,nearestEnemy.container.y,8+Math.sin(pulse*Math.PI*2)*3,0,Math.PI*2);
ctx.strokeStyle='#ffff00'; ctx.lineWidth=2; ctx.stroke(); ctx.restore();
} else { clearMagnetKeys(Re.input); }
} else if (!cfg.magnet && Re.input) { clearMagnetKeys(Re.input); }
for (const p of players) {
if (!p.active||p.dead||!p.container.visible) continue;
const sx=p.container.x, sy=p.container.y;
const isSelf=p.__id===myId;
const info=barn.getPlayerInfo(p.__id);
const isMate=!isSelf&&((myGroup>0&&info?.groupId>0&&info.groupId===myGroup)||(myTeam>0&&info?.teamId>0&&info.teamId===myTeam));
const isLocked=cfg.lockOn&&nearestEnemy&&p.__id===nearestEnemy.__id;
const color=isSelf?'#5bc470':isMate?'#6aabff':'#ff4444';
if (cfg.healthBars && !isSelf) {
const status=barn.playerStatus?.[p.__id];
const hp=typeof status?.health==='number'?status.health:null;
if (hp !== null) {
const BAR_W=80,BAR_H=8,BAR_X=sx-BAR_W/2,BAR_Y=sy-30;
const filled=Math.max(0,Math.min(1,hp/100))*BAR_W;
const hpCol=hp>60?'#5bc470':hp>30?'#e8c060':'#e87a7a';
ctx.globalAlpha=0.75; ctx.fillStyle='rgba(0,0,0,0.55)';
ctx.beginPath(); ctx.roundRect(BAR_X-1,BAR_Y-1,BAR_W+2,BAR_H+2,3); ctx.fill();
ctx.globalAlpha=0.4; ctx.fillStyle='#333';
ctx.beginPath(); ctx.roundRect(BAR_X,BAR_Y,BAR_W,BAR_H,2); ctx.fill();
ctx.globalAlpha=0.95; ctx.fillStyle=hpCol;
if(filled>0){ctx.beginPath();ctx.roundRect(BAR_X,BAR_Y,filled,BAR_H,2);ctx.fill();}
ctx.globalAlpha=1; ctx.font='bold 9px monospace'; ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.strokeStyle='rgba(0,0,0,0.9)'; ctx.lineWidth=2.5;
ctx.strokeText(`${Math.round(hp)}`,BAR_X+BAR_W/2,BAR_Y+BAR_H/2);
ctx.fillStyle='#fff'; ctx.fillText(`${Math.round(hp)}`,BAR_X+BAR_W/2,BAR_Y+BAR_H/2);
} else if (p.downed) {
ctx.save(); ctx.globalAlpha=0.8; ctx.font='bold 10px monospace'; ctx.textAlign='center'; ctx.textBaseline='bottom';
ctx.strokeStyle='rgba(0,0,0,0.8)'; ctx.lineWidth=3; ctx.strokeText('↓DOWNED',sx,sy-24);
ctx.fillStyle='#e8c060'; ctx.fillText('↓DOWNED',sx,sy-24); ctx.restore();
}
}
if (!isSelf) {
const dist=Math.hypot(sx-msx,sy-msy);
const alpha=Math.max(0.75,1-dist/2400);
const studs=Math.round(dist/STUD);
if (cfg.Esp) {
ctx.save(); ctx.beginPath(); ctx.moveTo(msx,msy); ctx.lineTo(sx,sy);
ctx.setLineDash(isMate?[6,4]:isLocked?[3,3]:[]);
ctx.strokeStyle=isLocked?'#ff4444':color; ctx.lineWidth=isMate?2:2.5;
ctx.globalAlpha=alpha; ctx.stroke(); ctx.setLineDash([]); ctx.restore();
}
if (cfg.names) {
const name=info?.nameTruncated||info?.name||`#${p.__id}`;
const label=isLocked?`${name} ${studs} ◎`:`${name} ${studs}`;
const lx=(msx+sx)/2, ly=(msy+sy)/2;
ctx.save(); ctx.globalAlpha=Math.min(1,alpha+0.2);
ctx.font='bold 11px monospace'; ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.strokeStyle='rgba(0,0,0,0.8)'; ctx.lineWidth=3; ctx.strokeText(label,lx,ly);
ctx.fillStyle=isLocked?'#ff4444':color; ctx.fillText(label,lx,ly); ctx.restore();
}
}
}
})();
setStatus('active','#5bc470');
}
const CSS = `
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&display=swap');
#_hbPanel *,#_hbPanel *::before,#_hbPanel *::after{box-sizing:border-box;margin:0;padding:0}
#_hbPanel{position:fixed;width:340px;background:#f4f3f0;border:1px solid #dbd9d3;border-radius:12px;z-index:10000;font-family:'DM Mono','Courier New',monospace;color:#2a2a2a;box-shadow:0 0 0 1px rgba(255,255,255,.7) inset,0 8px 32px rgba(0,0,0,.14);display:none;flex-direction:column;overflow:hidden;animation:_hbIn .18s cubic-bezier(.34,1.4,.64,1)}
@keyframes _hbIn{from{opacity:0;transform:scale(.94)}to{opacity:1;transform:scale(1)}}
#_hbHeader{display:flex;align-items:center;justify-content:space-between;padding:11px 16px;background:linear-gradient(to bottom,#f0ede8,#ebe8e2);border-bottom:1px solid #dbd9d3;cursor:grab;border-radius:12px 12px 0 0;user-select:none}
#_hbHeader:active{cursor:grabbing}
._hbLogo{display:flex;align-items:center;gap:8px}
._hbDot{width:8px;height:8px;border-radius:50%;background:#ccc;box-shadow:0 0 10px #ccc4;transition:all .3s}
@keyframes _hbShimmer{0%{background-position:200% center}100%{background-position:-200% center}}
._hbLogoText{font-size:10px;font-weight:500;letter-spacing:3px;text-transform:uppercase;background:linear-gradient(90deg,#111 0%,#888 40%,#fff 50%,#888 60%,#111 100%);background-size:300% auto;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:_hbShimmer 4s linear infinite}
._hbDiscord{font-size:9px;font-weight:500;letter-spacing:1px;text-decoration:none;text-transform:uppercase;background:linear-gradient(90deg,#4752c4 0%,#7289da 40%,#b8c0ff 50%,#7289da 60%,#4752c4 100%);background-size:300% auto;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:_hbShimmer 4s linear infinite;animation-delay:.8s}
._hbBadge{font-size:9px;color:#aaa;background:#e4e0d8;padding:2px 7px;border-radius:20px;letter-spacing:1px;border:1px solid #dbd9d3}
._hbHeaderRight{display:flex;align-items:center;gap:6px}
._hbHotkey{font-size:9px;color:#bbb;background:#ece9e2;padding:3px 8px;border-radius:20px;border:1px solid #dbd9d3}
._hbHotkey span{color:#2a2a2a;font-weight:600;background:#e0ddd6;padding:1px 5px;border-radius:3px}
#_hbClose{background:none;border:none;color:#bbb;font-size:13px;cursor:pointer;padding:4px 6px;border-radius:6px;transition:all .15s}
#_hbClose:hover{color:#d04040;background:#fae0e0}
#_hbBody{padding:14px 16px;background:#f4f3f0;display:flex;flex-direction:column;gap:12px;overflow-y:auto;max-height:calc(100vh - 100px)}
._hbSectionTitle{font-size:9px;letter-spacing:2.5px;color:#b8b4ac;text-transform:uppercase;margin-bottom:7px;padding-bottom:5px;border-bottom:1px solid #e4e0d8}
._hbGrid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
._hbToggle{display:flex;align-items:center;justify-content:space-between;padding:9px 11px;background:#fff;border:1px solid #e8e4de;border-radius:7px;cursor:pointer;transition:all .15s;user-select:none}
._hbToggle:hover{background:#faf9f6;border-color:#d4d0c8}
._hbToggle.on{background:#edf8f0;border-color:#b0dfc0}
._hbToggleLbl{font-size:10px;font-weight:500;letter-spacing:.5px;color:#999;text-transform:uppercase}
._hbToggle.on ._hbToggleLbl{color:#2a8a44}
._hbPip{width:7px;height:7px;border-radius:50%;background:#ddd;flex-shrink:0;transition:all .2s}
._hbToggle.on ._hbPip{background:#5bc470;box-shadow:0 0 7px #5bc47066}
._hbToggle.red.on{background:#fdf2f2;border-color:#f0bfbf}
._hbToggle.red.on ._hbToggleLbl{color:#c04040}
._hbToggle.red.on ._hbPip{background:#e87a7a;box-shadow:0 0 7px #e87a7a66}
._hbToggle.blue.on{background:#f0f4ff;border-color:#a0c0f0}
._hbToggle.blue.on ._hbToggleLbl{color:#3060c0}
._hbToggle.blue.on ._hbPip{background:#6aabff;box-shadow:0 0 7px #6aabff66}
._hbToggle.yellow.on{background:#fffbf0;border-color:#f0d890}
._hbToggle.yellow.on ._hbToggleLbl{color:#a07010}
._hbToggle.yellow.on ._hbPip{background:#e8c060;box-shadow:0 0 7px #e8c06066}
._hbInfoRow{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#fff;border:1px solid #e8e4de;border-radius:7px}
._hbInfoLabel{font-size:9px;color:#aaa;letter-spacing:1.5px;text-transform:uppercase}
._hbInfoVal{font-size:14px;font-weight:700;color:#2a2a2a}
._hbCard{background:#fff;border:1px solid #e8e4de;border-radius:7px;padding:10px 12px}
._hbSlugRow{display:flex;gap:6px;margin-bottom:10px}
._hbSlugRow input{flex:1;background:#f4f3f0;border:1px solid #e0ddd6;border-radius:5px;padding:5px 8px;font:11px 'DM Mono',monospace;color:#2a2a2a;outline:none}
._hbSlugRow input:focus{border-color:#b0b8c8}
._hbSlugRow button{background:#2a2a2a;color:#fff;border:none;border-radius:5px;padding:5px 10px;font:10px 'DM Mono',monospace;cursor:pointer;letter-spacing:.5px;transition:all .15s}
._hbSlugRow button:hover{background:#444}
._hbNote{font-size:9px;color:#c0bab2;font-style:italic;margin-top:5px;line-height:1.4}
#_hbMatchDetail{display:none;border-radius:7px;overflow:hidden;border:1px solid #e4e0d8;background:#fff;margin-top:8px}
#_hbStatusBar{padding:7px 16px;background:linear-gradient(to top,#ebe8e2,#edeae4);border-top:1px solid #dbd9d3;display:flex;align-items:center;justify-content:space-between;border-radius:0 0 12px 12px;flex-shrink:0}
#_hbStatusText{font-size:9px;color:#c8c4bc;letter-spacing:.5px;transition:color .3s}
._hbFootRight{display:flex;align-items:center;gap:10px}
#_hbFps{font-size:9px;color:#5bc470;letter-spacing:.5px}
#_hbMs{font-size:9px;color:#5bc470;letter-spacing:.5px}
._hbVer{font-size:9px;color:#c8c4bc;letter-spacing:1px}
/* ── Setup accordion inside body ── */
._hbSetupToggle{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;background:#fff;border:1px solid #e8e4de;border-radius:7px;cursor:pointer;transition:all .15s;user-select:none}
._hbSetupToggle:hover{background:#faf9f6;border-color:#d4d0c8}
._hbSetupToggle.open{background:#f0f4ff;border-color:#a0c0f0;border-radius:7px 7px 0 0}
._hbSetupToggleLabel{font-size:10px;font-weight:500;letter-spacing:.5px;color:#999;text-transform:uppercase}
._hbSetupToggle.open ._hbSetupToggleLabel{color:#3060c0}
._hbSetupArrow{font-size:10px;color:#bbb;transition:transform .2s}
._hbSetupToggle.open ._hbSetupArrow{transform:rotate(180deg);color:#6aabff}
._hbSetupContent{display:none;background:#fff;border:1px solid #e8e4de;border-top:none;border-radius:0 0 7px 7px;padding:10px 12px;flex-direction:column;gap:8px}
._hbSetupContent.open{display:flex}
._hbSetupStep{font-size:9px;color:#555;line-height:1.7;padding:7px 9px;background:#f8f7f5;border-radius:5px;border-left:3px solid #dbd9d3}
._hbSetupStep b{color:#2a2a2a}
._hbSetupStep.warn{background:#fffbf0;border-left-color:#e8c060;color:#a07010}
._hbCodeBlock{background:#f0ede8;border:1px solid #e4e0d8;border-radius:5px;padding:7px 9px;margin-top:5px;cursor:pointer;transition:background .15s}
._hbCodeBlock:hover{background:#e8e4de}
._hbCodeBlock pre{font-size:9px;color:#2a2a2a;font-family:'DM Mono',monospace;white-space:pre-wrap;word-break:break-all;line-height:1.5;margin:0}
._hbCopyHint{font-size:8px;color:#b8b4ac;margin-top:3px;letter-spacing:.5px}
/* ── Side setup panel ── */
#_hbSidePanel{position:fixed;width:260px;background:#f4f3f0;border:1px solid #dbd9d3;border-radius:12px;z-index:10000;font-family:'DM Mono','Courier New',monospace;color:#2a2a2a;box-shadow:0 8px 32px rgba(0,0,0,.14);display:none;flex-direction:column;overflow:hidden}
#_hbSidePanel._vis{display:flex;animation:_hbIn .18s cubic-bezier(.34,1.4,.64,1)}
._hbSideHeader{padding:10px 14px;background:linear-gradient(to bottom,#f0ede8,#ebe8e2);border-bottom:1px solid #dbd9d3;border-radius:12px 12px 0 0}
._hbSideTitle{font-size:9px;font-weight:500;letter-spacing:2.5px;color:#2a2a2a;text-transform:uppercase}
._hbSideBody{padding:12px 14px;display:flex;flex-direction:column;gap:8px;overflow-y:auto;max-height:80vh}
._hbSideStep{font-size:9px;color:#555;line-height:1.7;padding:8px 10px;background:#fff;border:1px solid #e8e4de;border-radius:7px}
._hbSideStep b{color:#2a2a2a;font-weight:700}
._hbSideStep.warn{background:#fffbf0;border-color:#f0d890;color:#a07010}
._hbSideCode{background:#f0ede8;border:1px solid #e4e0d8;border-radius:5px;padding:7px 9px;margin-top:6px;cursor:pointer;transition:background .15s}
._hbSideCode:hover{background:#e8e4de}
._hbSideCode pre{font-size:9px;color:#2a2a2a;font-family:'DM Mono',monospace;white-space:pre-wrap;word-break:break-all;line-height:1.5;margin:0}
._hbSideCode ._hbCopyHint{margin-top:4px}
`;
const SNIPPET_1 = `// Run this, then when paused type in console:
// window.__Re = Re then hit Enter, then Resume ▶
// OR just use Snippet 2 if this doesn't pause
debug(getEventListeners(window).resize[0].listener);
window.dispatchEvent(new Event('resize'));`;
const SNIPPET_2 = `window.__Re = Re`;
function copyText(text, el) {
navigator.clipboard.writeText(text).then(()=>{
const hint=el.querySelector('._hbCopyHint');
if(hint){const o=hint.textContent;hint.textContent='✓ copied!';hint.style.color='#5bc470';setTimeout(()=>{hint.textContent=o;hint.style.color='';},1500);}
}).catch(()=>{});
}
function buildUI() {
if (document.getElementById('_hbPanel')) return;
const style=document.createElement('style'); style.textContent=CSS; document.head.appendChild(style);
const panel=document.createElement('div'); panel.id='_hbPanel';
panel.innerHTML=`
<div id="_hbHeader">
<div class="_hbLogo">
<div class="_hbDot" id="_hbDot"></div>
<span class="_hbLogoText">VoidBacon</span>
<span class="_hbBadge">v2.7</span>
</div>
<div class="_hbHeaderRight">
<div class="_hbHotkey"><span>ESC</span></div>
<button id="_hbClose">✕</button>
</div>
</div>
<div id="_hbBody">
<div>
<div class="_hbSectionTitle">Visuals</div>
<div class="_hbGrid">
<div class="_hbToggle on" id="_hbt-Esp"><span class="_hbToggleLbl">Esp</span><div class="_hbPip"></div></div>
<div class="_hbToggle on" id="_hbt-names"><span class="_hbToggleLbl">Names</span><div class="_hbPip"></div></div>
<div class="_hbToggle on" id="_hbt-healthBars"><span class="_hbToggleLbl">HP Bars</span><div class="_hbPip"></div></div>
</div>
<div class="_hbNote">⚠ HP bars = teammates only (server never sends enemy HP)</div>
</div>
<div>
<div class="_hbSectionTitle">Combat</div>
<div class="_hbGrid">
<div class="_hbToggle red" id="_hbt-lockOn"><span class="_hbToggleLbl">Lock-On</span><div class="_hbPip"></div></div>
<div class="_hbToggle yellow" id="_hbt-magnet"><span class="_hbToggleLbl">Magnet</span><div class="_hbPip"></div></div>
</div>
<div class="_hbNote">Lock-On: Q key | Magnet: E key (auto-moves when ≤3 studs)</div>
</div>
<div>
<div class="_hbSectionTitle">World</div>
<div class="_hbGrid">
<div class="_hbToggle blue" id="_hbt-xray"><span class="_hbToggleLbl">X-Ray</span><div class="_hbPip"></div></div>
</div>
</div>
<div>
<div class="_hbSectionTitle">This Game</div>
<div class="_hbInfoRow">
<span class="_hbInfoLabel">Kills</span>
<span class="_hbInfoVal" id="_hbKills">0</span>
</div>
</div>
<div>
<div class="_hbSectionTitle">Player Stats</div>
<div class="_hbCard">
<div class="_hbSlugRow">
<input id="_hbSlugInput" type="text" placeholder="slug e.g. fr" />
<button id="_hbStatsFetch">GO</button>
</div>
<div id="_hbStatsBox" style="text-align:center;padding:4px 0;font-size:10px;color:#aaa">enter slug above</div>
</div>
</div>
<div>
<div class="_hbSectionTitle">Recent Matches <span style="font-size:8px;color:#c0bab2;font-weight:normal;letter-spacing:0">— click row for details</span></div>
<div id="_hbHistoryBox" style="font-size:10px;color:#aaa;text-align:center;padding:4px 0">search player above</div>
<div id="_hbMatchDetail"></div>
</div>
</div>
<div id="_hbStatusBar">
<span id="_hbStatusText">waiting...</span>
<div class="_hbFootRight">
<span id="_hbFps">-- fps</span>
<span id="_hbMs">-- ms</span>
<a class="_hbDiscord" href="https://discord.gg/7WgfQc4k" target="_blank">discord</a>
<span class="_hbVer">v2.7</span>
</div>
</div>`;
document.body.appendChild(panel);
// ── Side setup panel ──────────────────────────────────────────
const sidePanel=document.createElement('div'); sidePanel.id='_hbSidePanel';
sidePanel.innerHTML=`
<div class="_hbSideHeader" style="display:flex;align-items:center;justify-content:space-between">
<div class="_hbSideTitle">⚙ Setup Guide</div>
<button id="_hbSideClose" style="background:none;border:none;color:#bbb;font-size:13px;cursor:pointer;padding:2px 6px;border-radius:5px;transition:color .15s" onmouseover="this.style.color='#d04040'" onmouseout="this.style.color='#bbb'">✕</button>
</div>
<div class="_hbSideBody">
<div class="_hbSideStep">
<b>Step 1</b> — Press <b>F12</b> to open DevTools → go to <b>Sources</b> tab → click <b>Snippets</b>.
</div>
<div class="_hbSideStep">
<b>Step 2</b> — Create these 2 snippets:<br><br>
<b>Snippet 1:</b>
<div class="_hbSideCode" id="_hbSideSnip1">
<pre>debug(getEventListeners(window).resize[0].listener);
window.dispatchEvent(new Event('resize'));</pre>
<div class="_hbCopyHint">▸ click to copy</div>
</div>
<br><b>Snippet 2:</b>
<div class="_hbSideCode" id="_hbSideSnip2">
<pre>window.__Re = Re</pre>
<div class="_hbCopyHint">▸ click to copy</div>
</div>
</div>
<div class="_hbSideStep">
<b>Step 3</b> — On the survev.io <b>loading screen</b>:<br><br>
1. Run <b>Snippet 1</b><br>
2. Run <b>Snippet 2</b><br>
3. Turn <b>OFF Snippet 1</b> so the game doesn't freeze<br><br>
Done ✓ it should be working now
</div>
<div class="_hbSideStep warn">
⚠ Do this <b>every time</b> you open survev.io. After 1 game it stays active — no need to redo unless you refresh or close the tab.
</div>
</div>`;
document.body.appendChild(sidePanel);
document.getElementById('_hbSideClose').onclick=()=>sidePanel.remove();
document.getElementById('_hbSideSnip2').onclick=function(){copyText(`window.__Re = Re`,this);};
function positionSide(){
const r=panel.getBoundingClientRect();
const sw=260, margin=10;
// flip to left if not enough room on right
if(r.right+margin+sw > window.innerWidth){
sidePanel.style.left=(r.left-margin-sw)+'px';
} else {
sidePanel.style.left=(r.right+margin)+'px';
}
sidePanel.style.top=r.top+'px';
}
window._hbPosSide=positionSide;
// snippet copy handlers are on the side panel only (set after sidePanel is built)
document.getElementById('_hbClose').onclick=e=>{e.stopPropagation();closePanel();};
for (const [id,key] of Object.entries({
'_hbt-Esp':'Esp','_hbt-names':'names','_hbt-healthBars':'healthBars',
'_hbt-lockOn':'lockOn','_hbt-magnet':'magnet','_hbt-xray':'xray'
})) document.getElementById(id).onclick=()=>flipToggle(key);
const slugInput=document.getElementById('_hbSlugInput');
document.getElementById('_hbStatsFetch').onclick=()=>fetchStats(slugInput.value.trim());
slugInput.addEventListener('keydown',e=>{if(e.key==='Enter') fetchStats(slugInput.value.trim());});
setTimeout(()=>{
const nameEl=document.getElementById('account-player-name');
if(nameEl&&!slugInput.value){
const slug=nameEl.textContent.trim().toLowerCase().replace(/\s+/g,'-');
const skip=['log-in','create-account','guest','sign-in'];
if(slug&&!skip.some(s=>slug.includes(s))){slugInput.value=slug;fetchStats(slug);}
}
},2000);
let drag=false,ox=0,oy=0;
document.getElementById('_hbHeader').onmousedown=e=>{
if(e.target.id==='_hbClose') return;
drag=true;const r=panel.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;
};
document.addEventListener('mousemove',e=>{if(!drag) return;panelX=e.clientX-ox;panelY=e.clientY-oy;panel.style.left=panelX+'px';panel.style.top=panelY+'px';panel.style.right='auto';if(window._hbPosSide)window._hbPosSide();});
document.addEventListener('mouseup',()=>drag=false);
}
function flipToggle(key) {
cfg[key]=!cfg[key];
const el=document.getElementById({Esp:'_hbt-Esp',names:'_hbt-names',healthBars:'_hbt-healthBars',lockOn:'_hbt-lockOn',magnet:'_hbt-magnet',xray:'_hbt-xray'}[key]);
if(el) el.classList.toggle('on',cfg[key]);
if(key==='xray'&&!cfg.xray){const game=findRe()?.game;if(game) restoreXray(game);}
if(key==='magnet'&&!cfg.magnet){const Re=findRe();if(Re?.input) clearMagnetKeys(Re.input);}
setStatus(`${key} ${cfg[key]?'on':'off'}`);
}
function openPanel() {
const panel=document.getElementById('_hbPanel'); if(!panel) return;
if(panelX===null){panelX=Math.round((innerWidth-340)/2);panelY=Math.round((innerHeight-600)/2);}
panel.style.left=panelX+'px';panel.style.top=panelY+'px';panel.style.right='auto';
panel.style.display='flex';isPanelVisible=true;
const sp=document.getElementById('_hbSidePanel');
if(sp){if(window._hbPosSide)window._hbPosSide();sp.classList.add('_vis');}
}
function closePanel(){
const p=document.getElementById('_hbPanel');if(p) p.style.display='none';isPanelVisible=false;
const sp=document.getElementById('_hbSidePanel');if(sp)sp.classList.remove('_vis');
}
let _st=null;
function setStatus(msg,color='#c8c4bc') {
const el=document.getElementById('_hbStatusText'),dot=document.getElementById('_hbDot');
if(!el) return;
el.style.color=color;el.textContent=msg;
if(dot){dot.style.background=color;dot.style.boxShadow=`0 0 10px ${color}44`;}
clearTimeout(_st);
if(color!=='#5bc470'&&color!=='#c8c4bc') _st=setTimeout(()=>setStatus('ready'),3000);
}
document.addEventListener('keydown',e=>{
if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA') return;
if(e.key==='Escape'){e.preventDefault();isPanelVisible?closePanel():openPanel();}
if(e.key==='q'||e.key==='Q') flipToggle('lockOn');
if(e.key==='e'||e.key==='E') flipToggle('magnet');
if(e.key==='x'||e.key==='X') flipToggle('xray');
},true);
function waitForGame() {
buildUI();openPanel();
let tries=0;
const poll=setInterval(()=>{
tries++;
const Re=findRe();
if(Re?.game?.initialized){clearInterval(poll);startOverlay(Re);return;}
if(Re?.game){setStatus('in lobby','#b8a060');return;}
if(tries%10===0) console.log(`[VoidBacon] waiting... candidates: ${window.__hbCandidates?.length}`);
if(tries>240){clearInterval(poll);setStatus('needs setup','#c05050');
console.warn('[VoidBacon] Auto-capture failed. Open SETUP GUIDE in the panel for instructions.');}
},500);
}
const obs=new MutationObserver((_,o)=>{if(document.getElementById('ui-game')){o.disconnect();waitForGame();}});
window.addEventListener('DOMContentLoaded',()=>{
if(document.getElementById('ui-game')) waitForGame();
else obs.observe(document.body,{childList:true,subtree:true});
});
})();