Lunar Client

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

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==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);
})();
})();