Lunar Client

Lunar Client with draggable title bars, crosshair editor, selectable stats (OS, CPU, FPS, Ping), autosave, glow, etc.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Lunar Client
// @namespace    https://deadshot.io/
// @version      1.0
// @description  Lunar Client with draggable title bars, crosshair editor, selectable stats (OS, CPU, FPS, Ping), autosave, glow, etc.
// @author       Aguy123
// @match        *://*/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function(){
'use strict';
if(window.lunarClientLoaded) return;
window.lunarClientLoaded = true;

const d=document, L=localStorage;

// --- Default state ---
const defaultState = {
  style:'Circle', overlay:'Dot', size:60, glow:10, color:'#00eaff',
  enabled:true, editorX:null, editorY:null, uiX:null, uiY:null
};
let state = JSON.parse(L.getItem('lc_state')) || defaultState;
function saveState(){ L.setItem('lc_state', JSON.stringify(state)); }

// --- Canvas for Crosshair ---
const canvas=d.createElement('canvas');
canvas.style=`position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:2147483646;`;
d.body.appendChild(canvas);
const ctx=canvas.getContext('2d');
function resizeCanvas(){canvas.width=innerWidth;canvas.height=innerHeight;}
addEventListener('resize',resizeCanvas);resizeCanvas();

// --- Primary UI ---
const ui=d.createElement('div');
ui.style=`
  position:fixed;top:${state.uiY ?? 10}px;left:${state.uiX ?? 10}px;
  background:rgba(10,10,15,0.95);color:#00eaff;font-family:sans-serif;
  border-radius:10px;padding:6px;min-width:160px;max-width:160px;z-index:2147483647;position:relative;
`;
ui.innerHTML=`
  <div id="uiTitle" style="text-align:center;cursor:grab;margin-bottom:5px;font-weight:bold;">Lunar Client</div>
  <button id="btnEditor" style="width:100%;margin-bottom:3px;padding:4px;font-size:13px;">Crosshair Editor</button>
  <button id="btnStats" style="width:100%;margin-bottom:3px;padding:4px;font-size:13px;">Show Stats</button>
  <button id="btnStatsSettings" style="width:100%;margin-bottom:3px;padding:4px;font-size:13px;">Stats Settings</button>
  <button id="btnHide" style="width:100%;padding:4px;font-size:13px;">Hide UI</button>
`;
d.body.appendChild(ui);

// --- Crosshair Editor ---
const editor=d.createElement('div');
editor.style=`
  position:fixed;top:${state.editorY ?? 10}px;left:${state.editorX ?? (innerWidth-240)}px;
  background:rgba(20,20,30,0.95);color:#00eaff;font-family:sans-serif;
  border-radius:10px;padding:10px;min-width:220px;z-index:2147483647;
  cursor:default;display:none;
`;
editor.innerHTML=`
  <div id="editorTitle" style="text-align:center;cursor:grab;margin-bottom:6px;font-weight:bold;">Crosshair Editor</div>
  <div><label>Enabled:</label> <input type="checkbox" id="chToggle"></div>
  <div><label>Style:</label>
    <select id="chStyle">
      <option>None</option><option>Square</option><option>Circle</option>
      <option>Triangle</option><option>Pentagon</option><option>Four Corners</option>
    </select>
  </div>
  <div><label>Overlay:</label>
    <select id="chOverlay">
      <option>Dot</option><option>Plus</option><option>Cross</option><option>None</option>
    </select>
  </div>
  <div><label>Size: <span id="sizeVal">${state.size}</span></label><input type="range" id="chSize" min="5" max="200" style="width:100%;"></div>
  <div><label>Glow: <span id="glowVal">${state.glow}</span></label><input type="range" id="chGlow" min="0" max="50" style="width:100%;"></div>
  <div><label>Color:</label> <input type="color" id="chColor"></div>
  <button id="btnReset" style="width:100%;padding:5px;margin-top:5px;">Reset</button>
`;
d.body.appendChild(editor);

// --- Stats Panel ---
const stats=d.createElement('div');
stats.style=`
  position:fixed;right:10px;top:10px;background:rgba(0,0,0,0.6);color:#00eaff;
  padding:4px 6px;border-radius:6px;font-family:monospace;font-size:14px;
  z-index:2147483647;display:none;white-space:pre;
`;
stats.innerText='Stats:\n';
d.body.appendChild(stats);

// --- Show UI Button ---
const showBtn=d.createElement('button');
showBtn.textContent='Show UI';
showBtn.style=`
  position:fixed;top:10px;left:10px;background:rgba(20,20,30,0.9);
  color:#00eaff;border:none;border-radius:6px;padding:4px 8px;
  font-family:sans-serif;z-index:2147483647;display:none;
`;
d.body.appendChild(showBtn);

// --- Stats Settings Panel ---
const statsSettings=d.createElement('div');
statsSettings.style=`
  position:fixed;top:50px;left:10px;background:rgba(20,20,30,0.95);color:#00eaff;
  border-radius:10px;padding:10px;min-width:200px;z-index:2147483647;display:none;
`;
statsSettings.innerHTML=`
  <h4 style="text-align:center;margin:0 0 10px;">Stats Settings</h4>
  <div><input type="checkbox" id="selOS"> OS</div>
  <div><input type="checkbox" id="selCPU"> CPU</div>
  <div><input type="checkbox" id="selFPS"> FPS</div>
  <div><input type="checkbox" id="selPing"> Ping</div>
`;
d.body.appendChild(statsSettings);

// --- Draggable by title bars ---
function makeTitleDraggable(titleEl, targetEl, saveX, saveY){
 let drag=false, offX=0, offY=0;
 const start=e=>{
   const p=e.touches?e.touches[0]:e;
   drag=true; offX=p.clientX-targetEl.offsetLeft; offY=p.clientY-targetEl.offsetTop;
   titleEl.style.cursor='grabbing';
 };
 const move=e=>{
   if(!drag) return;
   const p=e.touches?e.touches[0]:e;
   let x=p.clientX-offX, y=p.clientY-offY;
   targetEl.style.left=x+'px'; targetEl.style.top=y+'px';
   saveX(x); saveY(y);
 };
 const stop=()=>{if(drag){drag=false;titleEl.style.cursor='grab'; saveState();}};
 titleEl.addEventListener('mousedown',start); titleEl.addEventListener('touchstart',start);
 addEventListener('mousemove',move); addEventListener('touchmove',move);
 addEventListener('mouseup',stop); addEventListener('touchend',stop);
}
makeTitleDraggable(d.getElementById('uiTitle'), ui, x=>state.uiX=x, y=>state.uiY=y);
makeTitleDraggable(d.getElementById('editorTitle'), editor, x=>state.editorX=x, y=>state.editorY=y);

// --- Button actions ---
d.getElementById('btnEditor').onclick=()=>editor.style.display=editor.style.display==='none'?'block':'none';
d.getElementById('btnStats').onclick=()=>stats.style.display=stats.style.display==='none'?'block':'none';
d.getElementById('btnStatsSettings').onclick=()=>statsSettings.style.display=statsSettings.style.display==='none'?'block':'none';
d.getElementById('btnHide').onclick=()=>{
 ui.style.display='none'; editor.style.display='none'; statsSettings.style.display='none';
 showBtn.style.display='block'; stats.style.display='block';
};
showBtn.onclick=()=>{ui.style.display='block'; showBtn.style.display='none';};

// --- Crosshair Inputs ---
const chToggle=d.getElementById('chToggle'),chStyle=d.getElementById('chStyle'),
chOverlay=d.getElementById('chOverlay'),chSize=d.getElementById('chSize'),
chGlow=d.getElementById('chGlow'),chColor=d.getElementById('chColor'),
sizeVal=d.getElementById('sizeVal'),glowVal=d.getElementById('glowVal'),
btnReset=d.getElementById('btnReset');

chToggle.checked=state.enabled;chStyle.value=state.style;chOverlay.value=state.overlay;
chSize.value=state.size;chGlow.value=state.glow;chColor.value=state.color;

function update(fn){fn();saveState();drawCrosshair();}
chToggle.onchange=()=>update(()=>state.enabled=chToggle.checked);
chStyle.onchange=()=>update(()=>state.style=chStyle.value);
chOverlay.onchange=()=>update(()=>state.overlay=chOverlay.value);
chSize.oninput=()=>update(()=>{state.size=+chSize.value;sizeVal.textContent=state.size;});
chGlow.oninput=()=>update(()=>{state.glow=+chGlow.value;glowVal.textContent=state.glow;});
chColor.oninput=()=>update(()=>state.color=chColor.value);
btnReset.onclick=()=>{state={...defaultState,editorX:editor.offsetLeft,editorY:editor.offsetTop,uiX:ui.offsetLeft,uiY:ui.offsetTop};saveState();location.reload();};

// --- Draw Crosshair ---
function drawCrosshair(){
 ctx.clearRect(0,0,canvas.width,canvas.height);
 if(!state.enabled)return;
 ctx.save();
 ctx.translate(canvas.width/2,canvas.height/2);
 ctx.strokeStyle=ctx.fillStyle=state.color;
 ctx.lineWidth=Math.max(1,Math.min(3,state.size/50));
 if(state.glow>0){ctx.shadowColor=state.color;ctx.shadowBlur=state.glow;}
 const s=state.size, cLen=Math.min(15,s/4);
 switch(state.style){
  case'Square':ctx.strokeRect(-s/2,-s/2,s,s);break;
  case'Circle':ctx.beginPath();ctx.arc(0,0,s/2,0,2*Math.PI);ctx.stroke();break;
  case'Triangle':ctx.beginPath();ctx.moveTo(0,-s/2);ctx.lineTo(s*Math.sqrt(3)/4,s/4);ctx.lineTo(-s*Math.sqrt(3)/4,s/4);ctx.closePath();ctx.stroke();break;
  case'Pentagon':ctx.beginPath();for(let i=0;i<5;i++){let a=i*2*Math.PI/5-Math.PI/2;let x=Math.cos(a)*s/2,y=Math.sin(a)*s/2;i?ctx.lineTo(x,y):ctx.moveTo(x,y);}ctx.closePath();ctx.stroke();break;
  case'Four Corners':
    ctx.beginPath();
    [[-1,-1],[1,-1],[-1,1],[1,1]].forEach(([x,y])=>{
      let sx=s/2*x,sy=s/2*y;
      ctx.moveTo(sx,sy);ctx.lineTo(sx-cLen*x,sy);
      ctx.moveTo(sx,sy);ctx.lineTo(sx,sy-cLen*y);
    });
    ctx.stroke();break;
 }
 ctx.shadowBlur=0;ctx.lineWidth=2;
 const oLen=Math.min(s/2,12);
 switch(state.overlay){
  case'Dot':ctx.beginPath();ctx.arc(0,0,2,0,2*Math.PI);ctx.fill();break;
  case'Plus':ctx.beginPath();ctx.moveTo(-oLen,0);ctx.lineTo(oLen,0);ctx.moveTo(0,-oLen);ctx.lineTo(0,oLen);ctx.stroke();break;
  case'Cross':ctx.beginPath();ctx.moveTo(-oLen,-oLen);ctx.lineTo(oLen,oLen);ctx.moveTo(oLen,-oLen);ctx.lineTo(-oLen,oLen);ctx.stroke();break;
 }
 ctx.restore();
}
requestAnimationFrame(function loop(){drawCrosshair();requestAnimationFrame(loop);});

// --- Stats / FPS / Ping ---
let frames=[], currentFPS=0, currentPing=0, lastPing=0;

// --- Stats selection (no Server) ---
let selectedStats = JSON.parse(L.getItem('lc_selectedStats')) || {OS:true, CPU:true, FPS:true, Ping:true};
for(const key in selectedStats){
    const cb=d.getElementById('sel'+key);
    if(cb) cb.checked=selectedStats[key];
    if(cb) cb.onchange=()=>{selectedStats[key]=cb.checked; L.setItem('lc_selectedStats',JSON.stringify(selectedStats));};
}

// --- Update Stats ---
function updateStats(){
    let lines=[];
    if(selectedStats.OS) lines.push(`OS: ${navigator.platform}`);
    if(selectedStats.CPU) lines.push(`CPU: ${navigator.hardwareConcurrency||'Unknown'} cores`);
    if(selectedStats.FPS) lines.push(`FPS: ${currentFPS}`);
    if(selectedStats.Ping) lines.push(`Ping: ${currentPing} ms`);
    stats.innerText = 'Stats:\n'+lines.join('\n');
}

// --- Stats Loop ---
(async function statsLoop(){
 const now=performance.now();frames.push(now);
 if(frames.length>60)frames.shift();
 currentFPS=Math.round((frames.length-1)/(frames[frames.length-1]-frames[0])*1000);

 if(now-lastPing>2000){
   lastPing=now;
   const start=now;
   try{await fetch('https://www.google.com/generate_204',{mode:'no-cors',cache:'no-store'}); currentPing=Math.round(performance.now()-start);}catch{currentPing='∞';}
 }
 updateStats();
 requestAnimationFrame(statsLoop);
})();
})();