DiepFried

Premium all-in-one mod menu for diep.io with powerful features for regular gameplay and custom games.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(У мене вже є менеджер скриптів, дайте мені встановити його!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         DiepFried
// @namespace    disinvoke.diepfried
// @version      1.0.0
// @description  Premium all-in-one mod menu for diep.io with powerful features for regular gameplay and custom games.
// @author       disinvoke
// @license      Copyright disinvoke
// @match        https://diep.io/*
// @supportURL   https://discord.gg/eWUpEjsx72
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @connect      api.diep.io
// @connect      lb.diep.io
// @run-at       document-start
// ==/UserScript==

// Copyright (c) 2026 disinvoke. All rights reserved.

(function(){
'use strict';
var W=(typeof unsafeWindow!=='undefined')?unsafeWindow:window;

(function injectPageHook(){
var hookCode='(function(){'
+'var R=window.WebSocket;'
+'var D=window.__diepWS={instance:null,packets:[],maxPkts:500,connected:false,url:null,wsCount:0,'
+'inByOp:{},outByOp:{},totalIn:0,totalOut:0,captureAll:true};'
+'function H(u,p){'
+'var s=p!==undefined?new R(u,p):new R(u);'
+'var g=u&&(u.indexOf("m28")>=0||u.indexOf("diep")>=0||u.indexOf("rivet")>=0);'
+'if(g){'
+'D.url=u;D.connected=false;D.instance=s;D.wsCount++;'
+'var os=s.send.bind(s);'
+'s.send=function(d){'
+'try{var a;'
+'if(d instanceof ArrayBuffer)a=new Uint8Array(d);'
+'else if(d instanceof Uint8Array)a=d;'
+'else if(ArrayBuffer.isView(d))a=new Uint8Array(d.buffer,d.byteOffset,d.byteLength);'
+'if(a){var op=a[0];D.totalOut++;'
+'if(!D.outByOp[op])D.outByOp[op]={count:0,sizes:[],lastSample:null};'
+'D.outByOp[op].count++;D.outByOp[op].sizes.push(a.length);'
+'if(D.outByOp[op].sizes.length>20)D.outByOp[op].sizes.shift();'
+'D.outByOp[op].lastSample=Array.from(a.slice(0,30));'
+'var pk={dir:"send",time:Date.now(),data:Array.from(a.slice(0,64)),size:a.length,op:op,wsId:D.wsCount};'
+'D.packets.push(pk);if(D.packets.length>D.maxPkts)D.packets.shift();'
+'}}catch(e){}'
+'return os(d);};'
+'s.addEventListener("message",function(ev){'
+'try{var a;'
+'if(ev.data instanceof ArrayBuffer)a=new Uint8Array(ev.data);'
+'if(a){var op=a[0];D.totalIn++;'
+'if(!D.inByOp[op])D.inByOp[op]={count:0,sizes:[],lastSample:null};'
+'D.inByOp[op].count++;D.inByOp[op].sizes.push(a.length);'
+'if(D.inByOp[op].sizes.length>20)D.inByOp[op].sizes.shift();'
+'D.inByOp[op].lastSample=Array.from(a.slice(0,60));'
+'var pk={dir:"recv",time:Date.now(),data:Array.from(a.slice(0,64)),size:a.length,op:op,wsId:D.wsCount};'
+'D.packets.push(pk);if(D.packets.length>D.maxPkts)D.packets.shift();'
+'}}catch(e){}});'
+'s.addEventListener("open",function(){D.connected=true;});'
+'s.addEventListener("close",function(){D.connected=false;D.instance=null;});'
+'}'
+'return s;}'
+'H.CONNECTING=R.CONNECTING;H.OPEN=R.OPEN;H.CLOSING=R.CLOSING;H.CLOSED=R.CLOSED;'
+'H.prototype=R.prototype;'
+'window.WebSocket=H;'
+'window.__diepWASM={exports:null};'
+'var origInstStream=WebAssembly.instantiateStreaming;'
+'if(origInstStream){WebAssembly.instantiateStreaming=function(s,i){'
+'return origInstStream.call(WebAssembly,s,i).then(function(r){'
+'try{var exp=r.instance.exports;'
+'if(exp.custom_get_player_list){'
+'window.__diepWASM.exports=exp;'
+'}}catch(e){}'
+'return r;});};}'
+'var origInst=WebAssembly.instantiate;'
+'if(origInst){WebAssembly.instantiate=function(b,i){'
+'return origInst.call(WebAssembly,b,i).then(function(r){'
+'try{var inst=r.instance||r;var exp=inst.exports;'
+'if(exp&&exp.custom_get_player_list){'
+'window.__diepWASM.exports=exp;'
+'}}catch(e){}'
+'return r;});};}'
+'window.__diepSB={texts:[],lastPush:0};'
+'var _ctm=new WeakMap();'
+'var _origFillText=CanvasRenderingContext2D.prototype.fillText;'
+'CanvasRenderingContext2D.prototype.fillText=function(t,x,y,mw){'
+'if(t&&t.length>0&&t.length<80&&this.canvas){'
+'_ctm.set(this.canvas,String(t));}'
+'return _origFillText.apply(this,arguments);};'
+'var _sbCollecting=false,_sbFrameBuf=[];'
+'var _origDrawImage=CanvasRenderingContext2D.prototype.drawImage;'
+'CanvasRenderingContext2D.prototype.drawImage=function(src){'
+'if(_sbCollecting&&src&&src.tagName==="CANVAS"){'
+'var tt=_ctm.get(src);if(tt)_sbFrameBuf.push(tt);}'
+'return _origDrawImage.apply(this,arguments);};'
+'window.__diepSB._collect=function(){'
+'if(_sbCollecting)return;'
+'_sbCollecting=true;_sbFrameBuf=[];'
+'setTimeout(function(){'
+'_sbCollecting=false;'
+'if(_sbFrameBuf.length>0){'
+'window.__diepSB.texts=_sbFrameBuf.slice();'
+'window.__diepSB.lastPush=Date.now();}'
+'},50);};'
+'})();';
var script=document.createElement('script');
script.textContent=hookCode;
(document.documentElement||document.head||document.body).appendChild(script);
script.remove();
})();

var WS={
get instance(){return W.__diepWS?W.__diepWS.instance:null;},
get packets(){return W.__diepWS?W.__diepWS.packets:[];},
get maxPkts(){return W.__diepWS?W.__diepWS.maxPkts:500;},
get connected(){return W.__diepWS?W.__diepWS.connected:false;},
get url(){return W.__diepWS?W.__diepWS.url:null;},
get inByOp(){return W.__diepWS?W.__diepWS.inByOp:{};},
get outByOp(){return W.__diepWS?W.__diepWS.outByOp:{};},
get totalIn(){return W.__diepWS?W.__diepWS.totalIn:0;},
get totalOut(){return W.__diepWS?W.__diepWS.totalOut:0;},
get captureAll(){return W.__diepWS?W.__diepWS.captureAll:true;},
set captureAll(v){if(W.__diepWS)W.__diepWS.captureAll=v;},
addListener:function(fn){_pktListeners.push(fn);},
removeListener:function(fn){_pktListeners=_pktListeners.filter(function(f){return f!==fn;});},
notify:function(pkt){for(var i=0;i<_pktListeners.length;i++){try{_pktListeners[i](pkt);}catch(e){}}}
};
var pktLogActive=false;
var _pktListeners=[];

var _sbLastParse=0;
var _sbCachedResult=null;
var _scoreRe=/^[\d,.]+[km]?$/i;
function parseScoreText(st){
st=(st||'0').replace(/,/g,'');
if(/k$/i.test(st))return Math.round(parseFloat(st)*1000);
if(/m$/i.test(st))return Math.round(parseFloat(st)*1000000);
return Math.round(parseFloat(st)||0);}
function getScoreboardFromCanvas(){
try{
var sb=W.__diepSB;
if(!sb)return _sbCachedResult;
if(sb._collect)sb._collect();
if(!sb.texts||sb.texts.length===0)return _sbCachedResult;
if(sb.lastPush<=_sbLastParse)return _sbCachedResult;
_sbLastParse=sb.lastPush;
var rawOrig=sb.texts.slice();
var sbIdx=-1;
for(var f=0;f<rawOrig.length;f++){
if(rawOrig[f].trim()==='Scoreboard'){sbIdx=f;break;}}
if(sbIdx<0)return _sbCachedResult;
var endIdx=rawOrig.length;
for(var f2=sbIdx+1;f2<rawOrig.length;f2++){
if(rawOrig[f2].trim()==='Scoreboard'){endIdx=f2;break;}}
var result=[];
var i=sbIdx+1;
while(i+2<endIdx){
var nameC=rawOrig[i].trim();
var dashC=rawOrig[i+1].trim();
var scoreC=rawOrig[i+2].trim();
if(dashC!=='-'&&dashC!==' - '){break;}
if(!_scoreRe.test(scoreC)||scoreC.length>10){break;}
if(nameC.length===0||nameC.length>30){break;}
var sc=parseScoreText(scoreC);
if(sc<=0){break;}
result.push({name:nameC,scoreText:scoreC,score:sc});
i+=3;}
result.sort(function(a,b){return b.score-a.score;});
if(result.length>=1)_sbCachedResult=result;
return _sbCachedResult;
}catch(ex){return _sbCachedResult;}}

var _directGetPlayersFn=null;
function _findDirectGetPlayersFn(){
if(_directGetPlayersFn){
try{var test=_directGetPlayersFn();if(Array.isArray(test))return _directGetPlayersFn;}
catch(e){_directGetPlayersFn=null;}}
try{
var wasmExports=W.__diepWASM&&W.__diepWASM.exports;
if(wasmExports&&typeof wasmExports.custom_get_player_list==='function'){
var fn=function(){return wasmExports.custom_get_player_list();};
var test2=fn();
if(Array.isArray(test2)){
_directGetPlayersFn=fn;
return fn;}}}catch(e){}
try{
var appEl=document.getElementById('app');
if(!appEl)return null;
var ck=Object.keys(appEl).find(function(k){return k.startsWith('__reactContainer')||k.startsWith('__reactFiber');});
if(!ck)return null;
var fiber=appEl[ck];
var queue=[fiber],visited=new Set(),iterations=0;
while(queue.length>0&&iterations<5000){
var node=queue.shift();
if(!node||visited.has(node))continue;
visited.add(node);iterations++;
if(node.memoizedProps&&typeof node.memoizedProps==='object'){
for(var pk in node.memoizedProps){
try{var pv=node.memoizedProps[pk];
if(pv&&typeof pv==='object'&&typeof pv.customGetPlayerList==='function'){
_directGetPlayersFn=pv.customGetPlayerList;
var r=_directGetPlayersFn();
return _directGetPlayersFn;}}catch(e){}}}
if(node.child)queue.push(node.child);
if(node.sibling)queue.push(node.sibling);}
}catch(e){}
return null;}

function getPlayerListDirect(){
try{
var fn=_findDirectGetPlayersFn();
if(!fn)return null;
var raw=fn();
if(!Array.isArray(raw)||raw.length===0)return null;
return raw.map(function(p){return{id:p.id,name:p.name,role:p.role,score:p.score||0,team:playerTeams[p.id]||0};});
}catch(e){
_directGetPlayersFn=null;return null;}}

var _fiberCache={node:null,lastFound:0};
function getPlayerListFromFiber(){
try{
var now=Date.now();
if(_fiberCache.node&&now-_fiberCache.lastFound<2000){
var cached=_readPlayerState(_fiberCache.node);
if(cached)return cached;_fiberCache.node=null;}
var appEl=document.getElementById('app');
if(appEl){
var ck=Object.keys(appEl).find(function(k){return k.startsWith('__reactContainer')||k.startsWith('__reactFiber');});
if(ck){
var result2=_bfsForPlayers(appEl[ck]);
if(result2)return result2;}}
return null;
}catch(e){return null;}}

function _readPlayerState(node){
var sc=node.memoizedState,si=0;
while(sc&&si<25){
var val=sc.memoizedState;
if(Array.isArray(val)&&val.length>0&&val[0]&&val[0].id!==undefined&&val[0].name!==undefined&&val[0].role!==undefined){
return val.map(function(p){return{id:p.id,name:p.name,role:p.role,score:p.score||0,team:playerTeams[p.id]||0};});}
sc=sc.next;si++;}
return null;}


function _bfsForPlayers(startFiber){
var queue=[startFiber],visited=new Set(),iterations=0;
while(queue.length>0&&iterations<3000){
var node=queue.shift();
if(!node||visited.has(node)){continue;}visited.add(node);iterations++;
var result=_readPlayerState(node);
if(result){_fiberCache.node=node;_fiberCache.lastFound=Date.now();return result;}
if(node.child)queue.push(node.child);
if(node.sibling)queue.push(node.sibling);}
return null;}

function getPlayerList(){
var directResult=getPlayerListDirect();
if(directResult&&directResult.length>0)return directResult;
var fiberResult=getPlayerListFromFiber();
if(fiberResult&&fiberResult.length>0)return fiberResult;
return getPlayerListFromScoreboard();}

var _lastDeathStats=null;
var _localKillScore={kills:0,score:0,lastChange:0};
function getDeathStatsFromFiber(){
try{
var appEl=document.getElementById('app');
if(!appEl)return null;
var ck=Object.keys(appEl).find(function(k){return k.startsWith('__reactContainer')||k.startsWith('__reactFiber');});
if(!ck)return null;
var fiber=appEl[ck];
var queue=[fiber],visited=new Set(),iterations=0;
while(queue.length>0&&iterations<3000){
var node=queue.shift();
if(!node||visited.has(node))continue;visited.add(node);iterations++;
if(node.memoizedProps&&node.memoizedProps.value&&node.memoizedProps.value.deathStats){
return node.memoizedProps.value.deathStats;}
if(node.child)queue.push(node.child);
if(node.sibling)queue.push(node.sibling);}
}catch(e){}
return null;}

function getLocalKillScoreFromFiber(){
try{
var appEl=document.getElementById('app');
if(!appEl)return null;
var ck=Object.keys(appEl).find(function(k){return k.startsWith('__reactContainer')||k.startsWith('__reactFiber');});
if(!ck)return null;
var fiber=appEl[ck];
var queue=[fiber],visited=new Set(),iterations=0;
while(queue.length>0&&iterations<2000){
var node=queue.shift();
if(!node||visited.has(node))continue;visited.add(node);iterations++;
var sc=node.memoizedState,si=0;
while(sc&&si<15){
var val=sc.memoizedState;
if(val&&typeof val==='object'&&val.kills!==undefined&&val.score!==undefined&&!Array.isArray(val)){
return{kills:val.kills,score:val.score};}
sc=sc.next;si++;}
if(node.child)queue.push(node.child);
if(node.sibling)queue.push(node.sibling);}
}catch(e){}
return null;}


function getPlayerListFromScoreboard(){
try{
var appEl=document.getElementById('app');
if(!appEl)return null;
var ck=Object.keys(appEl).find(function(k){return k.startsWith('__reactContainer')||k.startsWith('__reactFiber');});
if(!ck)return null;
var fiber=appEl[ck];
var queue=[fiber],visited=new Set(),iterations=0;
function checkArr(val){
if(Array.isArray(val)&&val.length>0&&val[0]&&typeof val[0]==='object'){
if(typeof val[0].name==='string'&&typeof val[0].score==='number'){
return val.map(function(p,idx){return{id:p.id||idx,name:p.name||'',role:'player',score:p.score||0,team:playerTeams[p.id||idx]||0};});}}
return null;}
while(queue.length>0&&iterations<5000){
var node=queue.shift();
if(!node||visited.has(node))continue;visited.add(node);iterations++;
var sc=node.memoizedState,si=0;
while(sc&&si<30){
var r=checkArr(sc.memoizedState);if(r)return r;
if(sc.queue&&sc.queue.lastRenderedState){r=checkArr(sc.queue.lastRenderedState);if(r)return r;}
sc=sc.next;si++;}
var pp=[node.pendingProps,node.memoizedProps];
for(var pi=0;pi<pp.length;pi++){
if(pp[pi]&&typeof pp[pi]==='object'){
var pkeys=Object.keys(pp[pi]);
for(var pk=0;pk<pkeys.length;pk++){
var r2=checkArr(pp[pi][pkeys[pk]]);if(r2)return r2;}}}
if(node.child)queue.push(node.child);
if(node.sibling)queue.push(node.sibling);}
}catch(e){}
return null;}

var _prevPlayerSnapshot=null;
var playerEvents=[];
var MAX_EVENTS=100;
function trackPlayerChanges(currentPlayers){
if(!currentPlayers||!Array.isArray(currentPlayers))return;
var curMap={};currentPlayers.forEach(function(p){curMap[p.id]=p;});
if(_prevPlayerSnapshot){
currentPlayers.forEach(function(p){
if(!_prevPlayerSnapshot[p.id]){
playerEvents.push({type:'join',id:p.id,name:p.name,time:Date.now()});
if(playerEvents.length>MAX_EVENTS)playerEvents.shift();
addKillFeedEntry('+ '+p.name+' joined','#4caf50');}});
Object.keys(_prevPlayerSnapshot).forEach(function(id){
id=parseInt(id);
if(!curMap[id]){
var prev=_prevPlayerSnapshot[id];
playerEvents.push({type:'leave',id:id,name:prev.name,time:Date.now()});
if(playerEvents.length>MAX_EVENTS)playerEvents.shift();
addKillFeedEntry('- '+prev.name+' left/died','#f44336');}});}
_prevPlayerSnapshot=curMap;}

var gameAPI={
ready:false,_queue:[],
init:function(){if(W.input&&W.input.execute){if(!this.ready){this.ready=true;this._flush();}return true;}return false;},
_flush:function(){while(this._queue.length>0){var it=this._queue.shift();try{if(it.t==='c')W.input.execute(it.v);else W.input.set_convar(it.n,String(it.v));}catch(e){}}},
cmd:function(c){if(!this.ready&&!this.init()){this._queue.push({t:'c',v:c});return;}try{W.input.execute(c);}catch(e){}},
set:function(n,v){if(!this.ready&&!this.init()){this._queue.push({t:'s',n:n,v:v});return;}try{W.input.set_convar(n,String(v));}catch(e){}},
get:function(n){if(!this.ready&&!this.init())return null;try{return W.input.get_convar(n);}catch(e){return null;}}
};

var TANKS={0:'Tank',1:'Twin',2:'Triplet',3:'Triple Shot',4:'Quad Tank',5:'Octo Tank',6:'Sniper',7:'Machine Gun',8:'Flank Guard',9:'Tri-Angle',10:'Destroyer',11:'Overseer',12:'Overlord',13:'Twin Flank',14:'Penta Shot',15:'Assassin',16:'Arena Closer',17:'Necromancer',18:'Triple Twin',19:'Hunter',20:'Gunner',21:'Stalker',22:'Ranger',23:'Booster',24:'Fighter',25:'Hybrid',26:'Manager',27:'Mothership',28:'Predator',29:'Sprayer',31:'Trapper',32:'Gunner Trapper',33:'Overtrapper',34:'Mega Trapper',35:'Tri-Trapper',36:'Smasher',38:'Landmine',39:'Auto Gunner',40:'Auto 5',41:'Auto 3',42:'Spread Shot',43:'Streamliner',44:'Auto Trapper',45:'Dom (Destroyer)',46:'Dom (Gunner)',47:'Dom (Trapper)',48:'Battleship',49:'Annihilator',50:'Auto Smasher',51:'Spike',52:'Factory',53:'Shotgun',54:'Skimmer',55:'Rocketeer',56:'Glider',57:'Pellet Shot',58:'Dual-Barrel',59:'Auto Tank',60:'Firework'};
var TANK_IDS=Object.keys(TANKS).map(Number).sort(function(a,b){return a-b;});
var TANK_TREE={'Base':[0,59],'Twin':[1,2,3,14,42,13,18],'Sniper':[6,15,21,22,19,28,43],'Machine Gun':[7,20,29,39,53,57,58],'Flank Guard':[8,9,23,24,4,5,40,41],'Destroyer':[10,25,49,54,55,56,60],'Overseer':[11,12,17,26,48,33,52],'Trapper':[31,32,34,35,44],'Smasher':[36,38,50,51]};
var STATS=['Health Regen','Max Health','Body Damage','Bullet Speed','Bullet Pen','Bullet Damage','Reload','Move Speed'];
var BOSSES=['FallenBooster','FallenOverlord','Guardian','Summoner','Defender'];
var BUILDS={'Glass':'00077775','Speed Glass':'00057777','Bullet Wall':'00027777','Rammer':'57700077','Smasher Ram':'AAA0000A','Speed Ram':'57700070','1M Overlord':'23077707','Anti-Ram OL':'02307777','Drone Meta':'00077757','Fighter':'02307777','Sea Serpent':'02307777','Hurricane':'02377707','Penta':'00077775','Triplet':'00077775','Spread Shot':'00067776','Sprayer':'00057777','Predator':'00077757','Ranger':'00077757','Factory':'23057770','Necro':'00077757','Hybrid':'00077775','Annihilator':'00077775','Battleship':'00077745','Trapper':'00067770','Armor Tank':'06607770','Max All':'77777777'};
var TANK_UPGRADES={0:[1,6,7,8,36,59],1:[3,4,13],3:[2,14,42],4:[5,40],6:[15,11,19,31],7:[10,20,29,53],8:[9,4,13,41],9:[23,24],10:[25,49,54,55,56,60],11:[12,17,26,33,48,52],13:[18,48],15:[22,21],19:[28,43],20:[39,32,43],31:[35,32,33,34,44],36:[38,50,51],41:[40,39],53:[57,58]};
var TANK_INFO={0:{tier:1,fov:1,b:1},1:{tier:2,fov:1,b:2},2:{tier:4,fov:1,b:3},3:{tier:3,fov:1,b:3},4:{tier:3,fov:1,b:4},5:{tier:4,fov:1,b:8},6:{tier:2,fov:0.899,b:1},7:{tier:2,fov:1,b:1},8:{tier:2,fov:1,b:2},9:{tier:3,fov:1,b:3},10:{tier:3,fov:1,b:1},11:{tier:3,fov:0.899,b:2},12:{tier:4,fov:0.899,b:4},13:{tier:3,fov:1,b:4},14:{tier:4,fov:1,b:5},15:{tier:3,fov:0.8,b:1},16:{tier:0,fov:1,b:1},17:{tier:4,fov:0.899,b:2},18:{tier:4,fov:1,b:6},19:{tier:3,fov:0.85,b:2},20:{tier:3,fov:1,b:4},21:{tier:4,fov:0.8,b:1,invis:true},22:{tier:4,fov:0.699,b:1},23:{tier:4,fov:1,b:5},24:{tier:4,fov:1,b:5},25:{tier:4,fov:1,b:2},26:{tier:4,fov:0.899,b:1,invis:true},27:{tier:0,fov:1,b:16},28:{tier:4,fov:0.85,b:3,zoom:true},29:{tier:4,fov:1,b:2},31:{tier:3,fov:0.899,b:1},32:{tier:4,fov:0.899,b:3},33:{tier:4,fov:0.899,b:3},34:{tier:4,fov:0.899,b:1},35:{tier:4,fov:0.899,b:3},36:{tier:3,fov:0.899,b:0,caps:[10,10,10,0,0,0,0,10]},38:{tier:4,fov:0.899,b:0,invis:true,caps:[10,10,10,0,0,0,0,10]},39:{tier:4,fov:1,b:4},40:{tier:4,fov:1,b:0},41:{tier:3,fov:1,b:0},42:{tier:4,fov:1,b:11},43:{tier:4,fov:0.85,b:5},44:{tier:4,fov:0.899,b:1},45:{tier:0,fov:1,b:1,caps:[0,0,0,0,0,0,0,0]},46:{tier:0,fov:1,b:3,caps:[0,0,0,0,0,0,0,0]},47:{tier:0,fov:1,b:8,caps:[0,0,0,0,0,0,0,0]},48:{tier:4,fov:0.899,b:4},49:{tier:4,fov:1,b:1},50:{tier:4,fov:0.899,b:0,caps:[10,10,10,10,10,10,10,10]},51:{tier:4,fov:0.899,b:0,caps:[10,10,10,0,0,0,0,10]},52:{tier:4,fov:0.899,b:1},53:{tier:3,fov:1,b:1},54:{tier:4,fov:0.899,b:1},55:{tier:4,fov:0.899,b:1},56:{tier:4,fov:0.899,b:1},57:{tier:4,fov:1,b:1},58:{tier:4,fov:1,b:1},59:{tier:4,fov:1,b:1},60:{tier:4,fov:1,b:1}};
var TANK_DEFAULT_CAPS=[7,7,7,7,7,7,7,7];
function getTankCaps(id){var i=TANK_INFO[id];return(i&&i.caps)?i.caps:TANK_DEFAULT_CAPS;}
var STAT_FORMULAS={hpRegen:function(P){return 0.1+0.4*P;},maxHP:function(P,L){return 50+2*(L-1)+20*P;},bodyDmgShape:function(P){return 20+4*P;},bodyDmgTank:function(P){return 30+6*P;},bulletSpd:function(P,M){return(5+4*P)*(M||1);},bulletPen:function(P,M){return(8+6*P)*(M||1);},bulletDmg:function(P,M){return(7+3*P)*(M||1);},tankAccel:function(ms,L){return 2.55*Math.pow(1.07,ms)/Math.pow(1.015,L-1);},tankMaxSpd:function(ms,L){return 10*2.55*Math.pow(1.07,ms)/Math.pow(1.015,L-1);}};
var DIEP_COLORS={border:'#555555',cannon:'#999999',tank:'#00b2e1',teamBlue:'#00b2e1',teamRed:'#f14e54',teamPurple:'#bf7ff5',teamGreen:'#00e16e',shiny:'#8aff69',square:'#ffe869',triangle:'#fc7677',pentagon:'#768dfc',crasher:'#f177dd',neutral:'#ffe869',scoreboard:'#43ff91',wall:'#bbbbbb',enemy:'#f14e54',sunchip:'#fcc376',fallen:'#c0c0c0',stats:{hpRegen:'#fcad76',maxHP:'#f943ff',bodyDmg:'#8543ff',bulletSpd:'#437fff',bulletPen:'#ffde43',bulletDmg:'#ff4343',reload:'#82ff43',moveSpd:'#43fff9'},ui:{grid:'#cdcdcd',minimapBg:'#cdcdcd',scoreBar:'#43ff91',levelBar:'#ffde43',healthBar:'#85e37d'}};
var STAT_COLORS=['#fcad76','#f943ff','#8543ff','#437fff','#ffde43','#ff4343','#82ff43','#43fff9'];

var _THEME_DEFAULTS={ren_background_color:0xCDCDCD,ren_grid_color:0x000000,ren_grid_base_alpha:0.1,ren_border_color:0x000000,ren_border_color_alpha:0.1,ren_dark_mode:'false',ren_stroke_solid_color:0x555555,ren_stroke_soft_color:'true',ren_stroke_soft_color_intensity:0.25,ren_health_fill_color:0x85E37D,ren_health_background_color:0x555555,ren_score_bar_fill_color:0x43FF91,ren_xp_bar_fill_color:0xFFDE43,ren_minimap_background_color:0xCDCDCD,ren_minimap_border_color:0x797979,ren_minimap_arrow_color:0x000000,ren_bar_background_color:0x000000,ren_shadow_color:0x000000,ren_shadows:'false',ren_shadow_alpha:0.14,ren_shadow_blur:13.125,ren_shadow_x:5.25,ren_shadow_y:5.25,ren_solid_background:'false',ren_background:'true',ren_disable_grid:'false',ren_name_outline_blend:0.3,ren_ui_scale:1};
var GAME_THEMES={"Classic":{},
"Dark":{ren_background_color:0x111111,ren_grid_color:0x444444,ren_grid_base_alpha:0.3,ren_border_color_alpha:0.3,ren_dark_mode:'true',ren_stroke_solid_color:0x333333,ren_stroke_soft_color_intensity:0.4,ren_health_background_color:0x333333,ren_minimap_background_color:0x1A1A1A,ren_minimap_border_color:0x444444,ren_minimap_arrow_color:0xCCCCCC,ren_bar_background_color:0x111111,ren_shadows:'true',ren_shadow_alpha:0.35,ren_shadow_blur:15},
"Midnight":{ren_background_color:0x0A0A20,ren_grid_color:0x1A1A50,ren_grid_base_alpha:0.12,ren_border_color:0x050510,ren_border_color_alpha:0.4,ren_dark_mode:'true',ren_stroke_solid_color:0x3A3A6A,ren_stroke_soft_color_intensity:0.3,ren_health_fill_color:0x7B68EE,ren_health_background_color:0x1A1A40,ren_score_bar_fill_color:0x9370DB,ren_xp_bar_fill_color:0xDDA0DD,ren_minimap_background_color:0x0F0F30,ren_minimap_border_color:0x4A4A8A,ren_minimap_arrow_color:0xBBBBFF,ren_bar_background_color:0x0A0A1A,ren_shadow_color:0x110033,ren_shadows:'true',ren_shadow_alpha:0.45,ren_shadow_blur:20,ren_name_outline_blend:0.5},
"Neon":{ren_background_color:0x050505,ren_grid_color:0x00FFFF,ren_grid_base_alpha:0.15,ren_border_color_alpha:0.5,ren_dark_mode:'true',ren_stroke_solid_color:0x00CCCC,ren_stroke_soft_color_intensity:0.15,ren_health_fill_color:0x00FF66,ren_health_background_color:0x003322,ren_score_bar_fill_color:0x00FFFF,ren_xp_bar_fill_color:0xFF00FF,ren_minimap_background_color:0x001111,ren_minimap_border_color:0x00FFFF,ren_minimap_arrow_color:0x00FF00,ren_bar_background_color:0x001111,ren_shadow_color:0x00FFFF,ren_shadows:'true',ren_shadow_alpha:0.5,ren_shadow_blur:25,ren_shadow_x:0,ren_shadow_y:0,ren_name_outline_blend:0.1},
"Blood":{ren_background_color:0x1A0000,ren_grid_color:0x440000,ren_grid_base_alpha:0.2,ren_border_color:0x0A0000,ren_border_color_alpha:0.4,ren_dark_mode:'true',ren_stroke_solid_color:0x660000,ren_stroke_soft_color_intensity:0.35,ren_health_fill_color:0xFF2222,ren_health_background_color:0x330000,ren_score_bar_fill_color:0xFF4444,ren_xp_bar_fill_color:0xFF8800,ren_minimap_background_color:0x1A0000,ren_minimap_border_color:0x660000,ren_minimap_arrow_color:0xFF4444,ren_bar_background_color:0x110000,ren_shadow_color:0x660000,ren_shadows:'true',ren_shadow_alpha:0.4,ren_shadow_blur:18,ren_name_outline_blend:0.4},
"Ocean":{ren_background_color:0x001525,ren_grid_color:0x1A6699,ren_grid_base_alpha:0.2,ren_border_color:0x000A18,ren_border_color_alpha:0.35,ren_dark_mode:'true',ren_stroke_solid_color:0x004488,ren_stroke_soft_color_intensity:0.3,ren_health_fill_color:0x00CED1,ren_health_background_color:0x002233,ren_score_bar_fill_color:0x1E90FF,ren_xp_bar_fill_color:0x87CEEB,ren_minimap_background_color:0x001020,ren_minimap_border_color:0x336699,ren_minimap_arrow_color:0x66CCFF,ren_bar_background_color:0x000D1A,ren_shadow_color:0x000044,ren_shadows:'true',ren_shadow_alpha:0.3,ren_shadow_blur:16},
"Forest":{ren_background_color:0x0E1A0E,ren_grid_color:0x2D6B2D,ren_grid_base_alpha:0.25,ren_border_color:0x0A140A,ren_border_color_alpha:0.3,ren_dark_mode:'true',ren_stroke_solid_color:0x2E5C2E,ren_stroke_soft_color_intensity:0.3,ren_health_fill_color:0x32CD32,ren_health_background_color:0x1A2D1A,ren_score_bar_fill_color:0x00FF7F,ren_xp_bar_fill_color:0xADFF2F,ren_minimap_background_color:0x0D180D,ren_minimap_border_color:0x2E5C2E,ren_minimap_arrow_color:0x00FF00,ren_bar_background_color:0x081008,ren_shadow_color:0x003300,ren_shadows:'true',ren_shadow_alpha:0.3,ren_shadow_blur:14},
"Cyberpunk":{ren_background_color:0x0D0015,ren_grid_color:0xFF00FF,ren_grid_base_alpha:0.12,ren_border_color:0x050008,ren_border_color_alpha:0.5,ren_dark_mode:'true',ren_stroke_solid_color:0xAA00AA,ren_stroke_soft_color_intensity:0.2,ren_health_fill_color:0xFF1493,ren_health_background_color:0x220022,ren_score_bar_fill_color:0xFCEE09,ren_xp_bar_fill_color:0xFF00FF,ren_minimap_background_color:0x110022,ren_minimap_border_color:0xFF69B4,ren_minimap_arrow_color:0xFFFF00,ren_bar_background_color:0x0A0010,ren_shadow_color:0xFF00FF,ren_shadows:'true',ren_shadow_alpha:0.45,ren_shadow_blur:22,ren_shadow_x:0,ren_shadow_y:0,ren_name_outline_blend:0.15},
"Monochrome":{ren_background_color:0xF0F0F0,ren_grid_base_alpha:0.08,ren_border_color_alpha:0.15,ren_stroke_solid_color:0x222222,ren_stroke_soft_color:'false',ren_stroke_soft_color_intensity:0.5,ren_health_fill_color:0x888888,ren_health_background_color:0x333333,ren_score_bar_fill_color:0xAAAAAA,ren_xp_bar_fill_color:0xCCCCCC,ren_minimap_background_color:0xDDDDDD,ren_minimap_border_color:0x444444,ren_bar_background_color:0x222222,ren_name_outline_blend:0.5},
"Sunset":{ren_background_color:0x1A0A15,ren_grid_color:0xFF4500,ren_grid_base_alpha:0.15,ren_border_color:0x100010,ren_border_color_alpha:0.35,ren_dark_mode:'true',ren_stroke_solid_color:0xCC3300,ren_stroke_soft_color_intensity:0.3,ren_health_fill_color:0xFF6347,ren_health_background_color:0x331111,ren_score_bar_fill_color:0xFF8C00,ren_xp_bar_fill_color:0xFFD700,ren_minimap_background_color:0x1A0A12,ren_minimap_border_color:0xCC5500,ren_minimap_arrow_color:0xFF6600,ren_bar_background_color:0x110808,ren_shadow_color:0x660000,ren_shadows:'true',ren_shadow_alpha:0.35,ren_shadow_blur:16},
"Arctic":{ren_background_color:0xE8F4F8,ren_grid_color:0x88CCDD,ren_grid_base_alpha:0.12,ren_border_color:0xAADDEE,ren_border_color_alpha:0.15,ren_stroke_solid_color:0x5599AA,ren_stroke_soft_color_intensity:0.2,ren_health_fill_color:0x00CED1,ren_health_background_color:0x88AACC,ren_score_bar_fill_color:0x87CEEB,ren_xp_bar_fill_color:0xB0E0E6,ren_minimap_background_color:0xDDEEF4,ren_minimap_border_color:0x7799AA,ren_bar_background_color:0x446688,ren_shadow_color:0x336688,ren_shadows:'true',ren_shadow_alpha:0.1,ren_shadow_blur:8,ren_shadow_x:2,ren_shadow_y:2},
"Void":{ren_background_color:0x020202,ren_grid_color:0x0A0A0A,ren_grid_base_alpha:0.05,ren_border_color_alpha:0.8,ren_dark_mode:'true',ren_stroke_solid_color:0x1A1A1A,ren_stroke_soft_color_intensity:0.6,ren_health_fill_color:0x444444,ren_health_background_color:0x111111,ren_score_bar_fill_color:0x333333,ren_xp_bar_fill_color:0x555555,ren_minimap_background_color:0x050505,ren_minimap_border_color:0x1A1A1A,ren_minimap_arrow_color:0x555555,ren_bar_background_color:0x050505,ren_name_outline_blend:0.6},
"Gold":{ren_background_color:0x0F0A00,ren_grid_color:0xFFD700,ren_grid_base_alpha:0.15,ren_border_color:0x0A0500,ren_border_color_alpha:0.4,ren_dark_mode:'true',ren_stroke_solid_color:0x8B7500,ren_stroke_soft_color_intensity:0.3,ren_health_fill_color:0xFFD700,ren_health_background_color:0x332800,ren_score_bar_fill_color:0xFFD700,ren_xp_bar_fill_color:0xFFA500,ren_minimap_background_color:0x1A1200,ren_minimap_border_color:0xB8860B,ren_minimap_arrow_color:0xFFD700,ren_bar_background_color:0x0A0800,ren_shadow_color:0x886600,ren_shadows:'true',ren_shadow_alpha:0.4,ren_shadow_blur:18,ren_name_outline_blend:0.35},
"Toxic":{ren_background_color:0x050A00,ren_grid_color:0x39FF14,ren_grid_base_alpha:0.12,ren_border_color:0x000500,ren_border_color_alpha:0.5,ren_dark_mode:'true',ren_stroke_solid_color:0x228B22,ren_stroke_soft_color_intensity:0.25,ren_health_fill_color:0x39FF14,ren_health_background_color:0x0A2200,ren_score_bar_fill_color:0x00FF00,ren_xp_bar_fill_color:0xADFF2F,ren_minimap_background_color:0x0A1500,ren_minimap_border_color:0x228B22,ren_minimap_arrow_color:0x00FF00,ren_bar_background_color:0x050A00,ren_shadow_color:0x00FF00,ren_shadows:'true',ren_shadow_alpha:0.45,ren_shadow_blur:20,ren_shadow_x:0,ren_shadow_y:0,ren_name_outline_blend:0.2},
"Stealth":{ren_background_color:0x1A1A1A,ren_grid_color:0x222222,ren_grid_base_alpha:0,ren_border_color:0x0A0A0A,ren_border_color_alpha:0.5,ren_dark_mode:'true',ren_stroke_solid_color:0x2A2A2A,ren_stroke_soft_color_intensity:0.5,ren_health_fill_color:0x4A6A4A,ren_health_background_color:0x222222,ren_score_bar_fill_color:0x4A7A4A,ren_xp_bar_fill_color:0x6A8A6A,ren_minimap_background_color:0x151515,ren_minimap_border_color:0x333333,ren_minimap_arrow_color:0x666666,ren_bar_background_color:0x0A0A0A,ren_solid_background:'true',ren_disable_grid:'true',ren_name_outline_blend:0.6},
"Tron":{ren_background_color:0x000000,ren_grid_color:0x6EE2FF,ren_grid_base_alpha:0.8,ren_border_color:0x193F4A,ren_border_color_alpha:0.6,ren_dark_mode:'true',ren_stroke_solid_color:0x2F8CA3,ren_stroke_soft_color_intensity:0.15,ren_health_fill_color:0x6EE2FF,ren_health_background_color:0x0D1E24,ren_score_bar_fill_color:0xF79D1E,ren_xp_bar_fill_color:0xD0DFE6,ren_minimap_background_color:0x020508,ren_minimap_border_color:0x6EE2FF,ren_minimap_arrow_color:0x6EE2FF,ren_bar_background_color:0x0D1820,ren_shadow_color:0x6EE2FF,ren_shadows:'true',ren_shadow_alpha:0.3,ren_shadow_blur:12,ren_shadow_x:0,ren_shadow_y:0,ren_name_outline_blend:0.15}};
function _getThemeVal(name,prop){var t=GAME_THEMES[name];return t&&t[prop]!==undefined?t[prop]:_THEME_DEFAULTS[prop];}
var _CUSTOM_THEMES={};try{var _ct=localStorage.getItem('df_custom_themes');if(_ct)_CUSTOM_THEMES=JSON.parse(_ct);}catch(e){}
function _saveCustomThemes(){try{localStorage.setItem('df_custom_themes',JSON.stringify(_CUSTOM_THEMES));}catch(e){}}
function _getEffectiveThemeVal(name,prop){if(_CUSTOM_THEMES[name]&&_CUSTOM_THEMES[name][prop]!==undefined)return _CUSTOM_THEMES[name][prop];var t=GAME_THEMES[name];if(t&&t[prop]!==undefined)return t[prop];return _THEME_DEFAULTS[prop];}
function _getHardcodedThemeVal(name,prop){var t=GAME_THEMES[name];return t&&t[prop]!==undefined?t[prop]:_THEME_DEFAULTS[prop];}
function _setCustomThemeVal(name,prop,val){if(!_CUSTOM_THEMES[name])_CUSTOM_THEMES[name]={};_CUSTOM_THEMES[name][prop]=val;}
function _resetCustomThemeProp(name,prop){if(_CUSTOM_THEMES[name]){delete _CUSTOM_THEMES[name][prop];if(Object.keys(_CUSTOM_THEMES[name]).length===0)delete _CUSTOM_THEMES[name];}}
function _resetCustomTheme(name){delete _CUSTOM_THEMES[name];}
function _exportAllThemes(){var out={};var tnames=Object.keys(GAME_THEMES);for(var i=0;i<tnames.length;i++){var tn=tnames[i];var obj={};var keys=Object.keys(_THEME_DEFAULTS);for(var j=0;j<keys.length;j++){var v=_getEffectiveThemeVal(tn,keys[j]);var dv=_THEME_DEFAULTS[keys[j]];if(v!==dv)obj[keys[j]]=v;}out[tn]=obj;}return out;}
function _downloadFile(filename,content,type){var blob=new Blob([content],{type:type||'text/plain'});var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=filename;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(a.href);}
var CROSSHAIR_SHAPES={"Tactical":'<svg width="50" height="50" viewBox="0 0 50 50"><circle cx="25" cy="25" r="2.5" fill="COLOR" stroke="black" stroke-width="1.2"/><circle cx="25" cy="25" r="8" fill="none" stroke="black" stroke-width="4"/><circle cx="25" cy="25" r="8" fill="none" stroke="COLOR" stroke-width="2.5"/><path d="M25 10 V15 M25 35 V40 M10 25 H15 M35 25 H40" stroke="black" stroke-width="5" stroke-linecap="round"/><path d="M25 10 V15 M25 35 V40 M10 25 H15 M35 25 H40" stroke="COLOR" stroke-width="3.5" stroke-linecap="round"/></svg>',"Cross":'<svg width="60" height="60" viewBox="0 0 60 60"><path d="M30 18 V42 M18 30 H42" stroke="black" stroke-width="5" stroke-linecap="square"/><path d="M30 18 V42 M18 30 H42" stroke="COLOR" stroke-width="3" stroke-linecap="square"/></svg>',"Dot":'<svg width="50" height="50" viewBox="0 0 50 50"><circle cx="25" cy="25" r="4.5" fill="black"/><circle cx="25" cy="25" r="3" fill="COLOR"/></svg>',"Chevron":'<svg width="50" height="50" viewBox="0 0 50 50"><path d="M12 35 L25 18 L38 35" fill="none" stroke="black" stroke-width="6" stroke-linejoin="miter" stroke-linecap="square"/><path d="M12 35 L25 18 L38 35" fill="none" stroke="COLOR" stroke-width="3.5" stroke-linejoin="miter" stroke-linecap="square"/></svg>',"T-Post":'<svg width="50" height="50" viewBox="0 0 50 50"><path d="M25 42 V22 M10 22 H40" stroke="black" stroke-width="6" stroke-linecap="square"/><path d="M25 42 V22 M10 22 H40" stroke="COLOR" stroke-width="3.5" stroke-linecap="square"/></svg>',"X-Sight":'<svg width="50" height="50" viewBox="0 0 50 50"><path d="M15 15 L35 35 M35 15 L15 35" stroke="black" stroke-width="5" stroke-linecap="round"/><path d="M15 15 L35 35 M35 15 L15 35" stroke="COLOR" stroke-width="3" stroke-linecap="round"/></svg>'};

var S={god:false,godPromoted:false,fastLvl:false,tankSw:false,selfDest:false,bossCon:false,newJoins:true,teamsFrozen:false,leaderArr:true,maxCap:45,infRunning:false,infInterval:null,infTeams:2,infKills:0,tankSwitchTimer:null,absMaxTimer:null,currentTankIdx:0,autoRespawn:false,autoRespawnTimer:null,killFeed:[],showKillFeed:true,infMode:'auto',hsaRunning:false,hsaInterval:null,antiAfk:false,_afkWorker:null,
mapAuto:false,mapAutoTimer:null,mapAutoPreset:'breathe',mapAutoMaxW:10000,mapAutoMaxH:10000,mapAutoMinW:1000,mapAutoMinH:1000,mapAutoInterval:100,mapAutoSpeed:5000,mapAutoDelay:0,mapAutoEasing:'sine',_mapAutoStart:0,_mapAutoCycle:0,
_autoBossTimer:null,lastBuild:null,keepBuild:false,gameTheme:'Classic',crosshairActive:false,crosshairShape:'Tactical',crosshairColor:'#00ffff',centerGuides:false,_rainbowTimer:null};

var MAP_EASINGS={
linear:{name:'Linear',desc:'Constant speed triangle wave (up then down)',fn:function(t){t=((t%1)+1)%1;return t<0.5?t*2:2-t*2;}},
sine:{name:'Sine',desc:'Smooth sinusoidal oscillation',fn:function(t){return (1-Math.cos(t*Math.PI*2))/2;}},
easeIn:{name:'Ease In',desc:'Accelerates into each peak and valley',fn:function(t){t=((t%1)+1)%1;var x=t<0.5?t*2:(1-t)*2;return x*x;}},
easeOut:{name:'Ease Out',desc:'Decelerates into each peak and valley',fn:function(t){t=((t%1)+1)%1;var x=t<0.5?t*2:(1-t)*2;return 1-(1-x)*(1-x);}},
easeInOut:{name:'Ease In-Out',desc:'Smooth acceleration and deceleration',fn:function(t){t=((t%1)+1)%1;var x=t<0.5?t*2:(1-t)*2;return x<0.5?2*x*x:1-Math.pow(-2*x+2,2)/2;}},
bounce:{name:'Bounce',desc:'Bounces at the extremes like a ball',fn:function(t){t=((t%1)+1)%1;var x=t<0.5?t*2:(1-t)*2;var n=7.5625;if(x<1/2.75)return n*x*x;if(x<2/2.75){x-=1.5/2.75;return n*x*x+0.75;}if(x<2.5/2.75){x-=2.25/2.75;return n*x*x+0.9375;}x-=2.625/2.75;return n*x*x+0.984375;}},
elastic:{name:'Elastic',desc:'Springy overshoot at the extremes',fn:function(t){t=((t%1)+1)%1;var x=t<0.5?t*2:(1-t)*2;if(x<=0)return 0;if(x>=1)return 1;return Math.pow(2,10*x-10)*Math.sin((x*10-10.75)*(2*Math.PI/3))*-0.5+0.5;}},
steps:{name:'Steps',desc:'Discrete staircase (stepped triangle)',fn:function(t){t=((t%1)+1)%1;var n=8;var x=t<0.5?t*2:2-t*2;return Math.floor(x*n)/n;}}
};
var MAP_AUTO_PRESETS={
breathe:{name:'Breathe',desc:'Uniform shrink to min then expand back to max',fn:function(t,s,ease){var v=ease(t);return{w:s.minW+(s.maxW-s.minW)*v,h:s.minH+(s.maxH-s.minH)*v};}},
rectH:{name:'Rectangle (H)',desc:'Only height changes, width stays at max',fn:function(t,s,ease){var v=ease(t);return{w:s.maxW,h:s.minH+(s.maxH-s.minH)*v};}},
rectW:{name:'Rectangle (W)',desc:'Only width changes, height stays at max',fn:function(t,s,ease){var v=ease(t);return{w:s.minW+(s.maxW-s.minW)*v,h:s.maxH};}},
altAxis:{name:'Alternating Axes',desc:'Shrink/expand height first, then width, repeating',fn:function(t,s,ease){if(t%1<0.5){var v=ease((t%1)*2);return{w:s.maxW,h:s.minH+(s.maxH-s.minH)*v};}var v2=ease(((t%1)-0.5)*2);return{w:s.minW+(s.maxW-s.minW)*v2,h:s.maxH};}},
spiral:{name:'Spiral',desc:'Width and height oscillate out of phase (rotating feel)',fn:function(t,s,ease){var wv=ease(t);var hv=ease(t+0.25);return{w:s.minW+(s.maxW-s.minW)*wv,h:s.minH+(s.maxH-s.minH)*hv};}},
heartbeat:{name:'Heartbeat',desc:'Quick pulse with a sharp shrink, quick expand, then pause',fn:function(t,s){var p=t%1;var v;if(p<0.15)v=1-p/0.15;else if(p<0.3)v=(p-0.15)/0.15;else if(p<0.4)v=1-(p-0.3)/0.1*0.5;else if(p<0.5)v=0.5+(p-0.4)/0.1*0.5;else v=1;return{w:s.minW+(s.maxW-s.minW)*v,h:s.minH+(s.maxH-s.minH)*v};}},
collapse:{name:'Collapse',desc:'Slowly shrink both axes to minimum, then snap back to max',fn:function(t,s){var p=t%1;var v=p<0.9?(1-p/0.9):1;return{w:s.minW+(s.maxW-s.minW)*v,h:s.minH+(s.maxH-s.minH)*v};}},
earthquake:{name:'Earthquake',desc:'Random jittery size changes (easing ignored)',fn:function(t,s){var seed=Math.sin(t*1000)*10000;var rw=(seed-Math.floor(seed));seed=Math.sin(t*1000+1)*10000;var rh=(seed-Math.floor(seed));return{w:s.minW+(s.maxW-s.minW)*rw,h:s.minH+(s.maxH-s.minH)*rh};}},
funnel:{name:'Funnel',desc:'Width and height move in opposite directions',fn:function(t,s,ease){var v=ease(t);return{w:s.minW+(s.maxW-s.minW)*v,h:s.maxH-(s.maxH-s.minH)*v};}},
wave:{name:'Wave',desc:'Both axes with slight phase offset',fn:function(t,s,ease){var wv=ease(t);var hv=ease(t+0.08);return{w:s.minW+(s.maxW-s.minW)*wv,h:s.minH+(s.maxH-s.minH)*hv};}},
zigzag:{name:'Zigzag',desc:'Linear zigzag between min and max (easing ignored)',fn:function(t,s){var p=t%1;var v=p<0.5?p*2:2-p*2;return{w:s.minW+(s.maxW-s.minW)*v,h:s.minH+(s.maxH-s.minH)*v};}},
squeeze:{name:'Squeeze',desc:'Alternately squeeze width then height like an accordion',fn:function(t,s,ease){var p=t*2%2;var v=ease(p);if(p<1){return{w:s.minW+(s.maxW-s.minW)*(1-v),h:s.minH+(s.maxH-s.minH)*v};}else{return{w:s.minW+(s.maxW-s.minW)*v,h:s.maxH-(s.maxH-s.minH)*v};}}}
};
function _drawEasingCurve(canvas,easingKey){
var ctx=canvas.getContext('2d');
var w=200,h=60;
var dpr=window.devicePixelRatio||1;
canvas.width=w*dpr;canvas.height=h*dpr;
canvas.style.width=w+'px';canvas.style.height=h+'px';
ctx.scale(dpr,dpr);
ctx.clearRect(0,0,w,h);
ctx.fillStyle='rgba(0,0,0,0.3)';ctx.fillRect(0,0,w,h);
ctx.strokeStyle='#333';ctx.lineWidth=0.5;
ctx.beginPath();ctx.moveTo(0,h/2);ctx.lineTo(w,h/2);ctx.stroke();
ctx.beginPath();ctx.moveTo(0,h*0.1);ctx.lineTo(w,h*0.1);ctx.stroke();
ctx.beginPath();ctx.moveTo(0,h*0.9);ctx.lineTo(w,h*0.9);ctx.stroke();
var ease=MAP_EASINGS[easingKey];if(!ease)return;
var pad=4;
ctx.strokeStyle='#4a9eff';ctx.lineWidth=2;
ctx.beginPath();
for(var i=0;i<=w-pad*2;i++){
var t=i/(w-pad*2);
var v=ease.fn(t);
var x=pad+i;
var y=h*0.9-(h*0.8)*v;
if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);}
ctx.stroke();
ctx.fillStyle='#4a9eff';ctx.font='9px sans-serif';ctx.textAlign='left';
ctx.fillText('1',2,h*0.1+9);ctx.fillText('0',2,h*0.9-2);
ctx.textAlign='right';ctx.fillText('t→',w-2,h/2-3);
}
function _mapAutoTick(){
if(!S.mapAuto)return;
var preset=MAP_AUTO_PRESETS[S.mapAutoPreset];
if(!preset)return;
var easing=MAP_EASINGS[S.mapAutoEasing];
var easeFn=easing?easing.fn:MAP_EASINGS.sine.fn;
var speed=Math.max(500,S.mapAutoSpeed);
var effectiveSpeed=(S.mapAutoDelay>0)?speed*2:speed;
var interval=Math.max(20,S.mapAutoInterval);
var now=Date.now();
if(!S._mapAutoStart)S._mapAutoStart=now;
var elapsed=(now-S._mapAutoStart)%effectiveSpeed;
var t=elapsed/effectiveSpeed;
var settings={minW:S.mapAutoMinW,minH:S.mapAutoMinH,maxW:S.mapAutoMaxW,maxH:S.mapAutoMaxH};
var result=preset.fn(t,settings,easeFn);
var w=Math.max(S.mapAutoMinW,Math.min(S.mapAutoMaxW,Math.round(result.w)));
var h=Math.max(S.mapAutoMinH,Math.min(S.mapAutoMaxH,Math.round(result.h)));
gameAPI.cmd('map_fixed_size '+w+' '+h);
var stEl=document.getElementById('mm-mast');
if(stEl)stEl.textContent=preset.name+': '+w+'x'+h;
}
function startMapAuto(){
if(S.mapAutoTimer){clearInterval(S.mapAutoTimer);}
S._mapAutoStart=Date.now();
S._mapAutoCycle=0;
S.mapAuto=true;
var delay=S.mapAutoDelay||0;
function runCycle(){
S._mapAutoStart=Date.now();
S.mapAutoTimer=setInterval(_mapAutoTick,Math.max(20,S.mapAutoInterval));
if(delay>0){setTimeout(function(){
if(!S.mapAuto)return;
clearInterval(S.mapAutoTimer);
gameAPI.cmd('map_fixed_size '+S.mapAutoMaxW+' '+S.mapAutoMaxH);
var stEl=document.getElementById('mm-mast');
if(stEl)stEl.textContent='Pausing '+delay+'ms...';
setTimeout(function(){if(S.mapAuto)runCycle();},delay);
},S.mapAutoSpeed);}}
runCycle();}
function stopMapAuto(){
S.mapAuto=false;
if(S.mapAutoTimer){clearInterval(S.mapAutoTimer);S.mapAutoTimer=null;}
var stEl=document.getElementById('mm-mast');
if(stEl)stEl.textContent='Stopped';
}
var cmdHistory=[],histIdx=-1;
var prevPlayers=null,playerScores={},playerTeams={};
var playerPresence={};

var _currentTheme=(function(){try{return localStorage.getItem('diepFriedTheme')||'diep';}catch(e){return 'diep';}})();
function setTheme(t){_currentTheme=t;try{localStorage.setItem('diepFriedTheme',t);}catch(e){}if(menuEl){menuEl.className='theme-'+t+(menuVisible?' visible':'');}}

var CSS=''
+'#diep-mod-menu.theme-midnight{'
+'--mm-bg:rgba(18,18,28,0.97);--mm-border:#4a9eff;--mm-shadow:0 0 30px rgba(74,158,255,0.3);--mm-radius:10px;--mm-bw:2px;'
+'--mm-text:#e0e0e0;--mm-text-dim:#888;--mm-text-bright:#fff;--mm-accent:#4a9eff;'
+'--mm-hdr-bg:linear-gradient(135deg,#1a1a2e,#16213e);--mm-hdr-border:#333;--mm-hdr-title:#4a9eff;'
+'--mm-tabs-bg:#111;--mm-tabs-border:#333;--mm-tab-text:#888;--mm-tab-hover-text:#ccc;--mm-tab-hover-bg:rgba(255,255,255,0.05);--mm-tab-active-text:#4a9eff;--mm-tab-active-bg:rgba(74,158,255,0.1);--mm-tab-active-border:#4a9eff;'
+'--mm-sec-border:#222;--mm-sec-title:#4a9eff;'
+'--mm-btn-bg:rgba(74,158,255,0.15);--mm-btn-border:rgba(74,158,255,0.3);--mm-btn-text:#ddd;--mm-btn-hover-bg:rgba(74,158,255,0.3);--mm-btn-radius:4px;--mm-btn-bw:1px;--mm-btn-font:inherit;--mm-btn-weight:normal;'
+'--mm-btn-active-bg:rgba(76,175,80,0.3);--mm-btn-active-border:rgba(76,175,80,0.6);--mm-btn-active-text:#4caf50;'
+'--mm-btn-danger-bg:rgba(244,67,54,0.15);--mm-btn-danger-border:rgba(244,67,54,0.3);--mm-btn-danger-text:#f44336;'
+'--mm-btn-success-bg:rgba(76,175,80,0.15);--mm-btn-success-border:rgba(76,175,80,0.3);--mm-btn-success-text:#4caf50;'
+'--mm-btn-warn-bg:rgba(255,152,0,0.15);--mm-btn-warn-border:rgba(255,152,0,0.3);--mm-btn-warn-text:#ff9800;'
+'--mm-inp-bg:rgba(0,0,0,0.3);--mm-inp-border:#333;--mm-inp-text:#ddd;'
+'--mm-info-bg:rgba(0,0,0,0.2);--mm-info-text:#888;'
+'--mm-ptbl-border:#333;--mm-ptbl-row-border:#222;--mm-ptbl-hover:rgba(74,158,255,0.1);--mm-ptbl-sel:rgba(74,158,255,0.2);--mm-ptbl-hdr-bg:rgba(0,0,0,0.3);--mm-ptbl-id:#4a9eff;--mm-ptbl-score:#888;'
+'--mm-lbl-text:#aaa;'
+'}'
+'#diep-mod-menu.theme-fried{'
+'--mm-bg:rgba(0,0,0,0.85);--mm-border:transparent;--mm-shadow:none;--mm-radius:8px;--mm-bw:0px;'
+'--mm-text:#fff5ee;--mm-text-dim:rgba(255,200,160,0.6);--mm-text-bright:#fff5ee;--mm-accent:#e8622b;'
+'--mm-hdr-bg:rgba(0,0,0,0.3);--mm-hdr-border:rgba(255,140,66,0.1);--mm-hdr-title:#ff8c42;'
+'--mm-tabs-bg:rgba(0,0,0,0.2);--mm-tabs-border:rgba(255,140,66,0.1);--mm-tab-text:rgba(255,200,160,0.6);--mm-tab-hover-text:#fff5ee;--mm-tab-hover-bg:rgba(232,98,43,0.12);--mm-tab-active-text:#fff5ee;--mm-tab-active-bg:#e8622b;--mm-tab-active-border:#e8622b;'
+'--mm-sec-border:rgba(255,140,66,0.12);--mm-sec-title:#ff8c42;'
+'--mm-btn-bg:#c05820;--mm-btn-border:rgba(0,0,0,0.4);--mm-btn-text:#fff5ee;--mm-btn-hover-bg:#d06828;--mm-btn-radius:4px;--mm-btn-bw:2px;--mm-btn-font:Ubuntu,sans-serif;--mm-btn-weight:600;'
+'--mm-btn-active-bg:#ffa726;--mm-btn-active-border:rgba(0,0,0,0.4);--mm-btn-active-text:#fff;'
+'--mm-btn-danger-bg:#d84040;--mm-btn-danger-border:rgba(0,0,0,0.4);--mm-btn-danger-text:#fff;'
+'--mm-btn-success-bg:#e0a020;--mm-btn-success-border:rgba(0,0,0,0.4);--mm-btn-success-text:#fff;'
+'--mm-btn-warn-bg:#e87840;--mm-btn-warn-border:rgba(0,0,0,0.4);--mm-btn-warn-text:#fff;'
+'--mm-inp-bg:rgba(0,0,0,0.3);--mm-inp-border:rgba(255,140,66,0.2);--mm-inp-text:#fff5ee;'
+'--mm-info-bg:rgba(0,0,0,0.2);--mm-info-text:rgba(255,200,160,0.7);'
+'--mm-ptbl-border:rgba(255,140,66,0.1);--mm-ptbl-row-border:rgba(255,140,66,0.08);--mm-ptbl-hover:rgba(232,98,43,0.18);--mm-ptbl-sel:rgba(232,98,43,0.3);--mm-ptbl-hdr-bg:rgba(0,0,0,0.3);--mm-ptbl-id:#ff8c42;--mm-ptbl-score:rgba(255,200,160,0.6);'
+'--mm-lbl-text:rgba(255,200,160,0.85);'
+'}'
+'#diep-mod-menu.theme-diep{'
+'--mm-bg:rgba(0,0,0,0.85);--mm-border:transparent;--mm-shadow:none;--mm-radius:8px;--mm-bw:0px;'
+'--mm-text:#fff;--mm-text-dim:rgba(255,255,255,0.6);--mm-text-bright:#fff;--mm-accent:#00b2e1;'
+'--mm-hdr-bg:rgba(0,0,0,0.3);--mm-hdr-border:rgba(255,255,255,0.08);--mm-hdr-title:#fff;'
+'--mm-tabs-bg:rgba(0,0,0,0.2);--mm-tabs-border:rgba(255,255,255,0.08);--mm-tab-text:rgba(255,255,255,0.6);--mm-tab-hover-text:#fff;--mm-tab-hover-bg:rgba(255,255,255,0.08);--mm-tab-active-text:#fff;--mm-tab-active-bg:#00b2e1;--mm-tab-active-border:#00b2e1;'
+'--mm-sec-border:rgba(255,255,255,0.1);--mm-sec-title:#fff;'
+'--mm-btn-bg:#768dfc;--mm-btn-border:rgba(0,0,0,0.4);--mm-btn-text:#fff;--mm-btn-hover-bg:#8a9efd;--mm-btn-radius:4px;--mm-btn-bw:2px;--mm-btn-font:Ubuntu,sans-serif;--mm-btn-weight:600;'
+'--mm-btn-active-bg:#00b2e1;--mm-btn-active-border:rgba(0,0,0,0.4);--mm-btn-active-text:#fff;'
+'--mm-btn-danger-bg:#f14e54;--mm-btn-danger-border:rgba(0,0,0,0.4);--mm-btn-danger-text:#fff;'
+'--mm-btn-success-bg:#00e16e;--mm-btn-success-border:rgba(0,0,0,0.4);--mm-btn-success-text:#fff;'
+'--mm-btn-warn-bg:#ffe869;--mm-btn-warn-border:rgba(0,0,0,0.4);--mm-btn-warn-text:#fff;'
+'--mm-inp-bg:rgba(0,0,0,0.3);--mm-inp-border:rgba(255,255,255,0.15);--mm-inp-text:#fff;'
+'--mm-info-bg:rgba(0,0,0,0.2);--mm-info-text:rgba(255,255,255,0.7);'
+'--mm-ptbl-border:rgba(255,255,255,0.08);--mm-ptbl-row-border:rgba(255,255,255,0.06);--mm-ptbl-hover:rgba(0,178,225,0.15);--mm-ptbl-sel:rgba(0,178,225,0.25);--mm-ptbl-hdr-bg:rgba(0,0,0,0.3);--mm-ptbl-id:#00b2e1;--mm-ptbl-score:rgba(255,255,255,0.6);'
+'--mm-lbl-text:rgba(255,255,255,0.85);'
+'}'
+'#diep-mod-menu{position:fixed;top:50px;left:50px;width:560px;max-height:82vh;background:var(--mm-bg);border:var(--mm-bw) solid var(--mm-border);border-radius:var(--mm-radius);color:var(--mm-text);font-family:"Segoe UI",Tahoma,sans-serif;font-size:13px;z-index:999999;display:none;flex-direction:column;box-shadow:var(--mm-shadow);user-select:none;overflow:hidden}'
+'#diep-mod-menu.visible,#diep-mod-menu.theme-midnight.visible,#diep-mod-menu.theme-diep.visible,#diep-mod-menu.theme-fried.visible{display:flex}'
+'.mm-hdr{display:flex;align-items:center;justify-content:space-between;padding:8px 14px;background:var(--mm-hdr-bg);border-bottom:1px solid var(--mm-hdr-border);cursor:move}'
+'.mm-hdr h2{margin:0;font-size:15px;color:var(--mm-hdr-title);font-weight:600;display:inline}'
+'.mm-sts{display:flex;gap:8px;align-items:center;font-size:11px}'
+'.mm-dot{width:8px;height:8px;border-radius:50%;display:inline-block}'
+'.mm-dot.on{background:#4caf50;box-shadow:0 0 4px #4caf50}.mm-dot.off{background:#f44336}'
+'.mm-tabs{display:flex;flex-wrap:wrap;background:var(--mm-tabs-bg);border-bottom:1px solid var(--mm-tabs-border);padding:4px 4px 0}'
+'.mm-tab{padding:6px 10px;cursor:pointer;color:var(--mm-tab-text);font-size:12px;border-radius:6px 6px 0 0;transition:all 0.2s;white-space:nowrap}'
+'.mm-tab:hover{color:var(--mm-tab-hover-text);background:var(--mm-tab-hover-bg)}'
+'.mm-tab.active{color:var(--mm-tab-active-text);background:var(--mm-tab-active-bg);border-bottom:2px solid var(--mm-tab-active-border)}'
+'.mm-body{padding:12px;overflow-y:auto;max-height:calc(82vh - 100px)}'
+'.mm-sec{margin-bottom:14px}'
+'.mm-sec h3{margin:0 0 8px;font-size:13px;color:var(--mm-sec-title);border-bottom:1px solid var(--mm-sec-border);padding-bottom:4px}'
+'.mm-row{display:flex;gap:6px;margin-bottom:6px;align-items:center;flex-wrap:wrap}'
+'.mm-btn{padding:6px 12px;background:var(--mm-btn-bg);border:var(--mm-btn-bw) solid var(--mm-btn-border);color:var(--mm-btn-text);border-radius:var(--mm-btn-radius);cursor:pointer;transition:all 0.15s;font-size:12px;text-align:center;font-family:var(--mm-btn-font);font-weight:var(--mm-btn-weight)}'
+'.mm-btn:hover{background:var(--mm-btn-hover-bg);color:var(--mm-text-bright)}'
+'.mm-btn.active{background:var(--mm-btn-active-bg);border-color:var(--mm-btn-active-border);color:var(--mm-btn-active-text)}'
+'.mm-btn.danger{background:var(--mm-btn-danger-bg);border-color:var(--mm-btn-danger-border);color:var(--mm-btn-danger-text)}'
+'.mm-btn.danger:hover{opacity:0.85}'
+'.mm-btn.success{background:var(--mm-btn-success-bg);border-color:var(--mm-btn-success-border);color:var(--mm-btn-success-text)}'
+'.mm-btn.success:hover{opacity:0.85}'
+'.mm-btn.warn{background:var(--mm-btn-warn-bg);border-color:var(--mm-btn-warn-border);color:var(--mm-btn-warn-text)}'
+'.mm-btn.small{padding:4px 8px;font-size:11px}'
+'.mm-inp{background:var(--mm-inp-bg);border:1px solid var(--mm-inp-border);color:var(--mm-inp-text);padding:6px 10px;border-radius:4px;font-size:12px;outline:none}'
+'.mm-inp:focus{border-color:var(--mm-accent)}'
+'.mm-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}'
+'.mm-grid4{display:grid;grid-template-columns:repeat(4,1fr);gap:4px}'
+'.mm-grid2{display:grid;grid-template-columns:repeat(2,1fr);gap:4px}'
+'.mm-info{font-size:11px;color:var(--mm-info-text);margin-bottom:10px;padding:6px 10px;background:var(--mm-info-bg);border-radius:4px}'
+'.mm-lbl{font-size:12px;color:var(--mm-lbl-text);min-width:100px}'
+'.mm-trow{display:flex;justify-content:space-between;align-items:center;padding:4px 0}'
+'.mm-ptbl{border:1px solid var(--mm-ptbl-border);border-radius:4px;overflow:hidden;margin:6px 0}'
+'.mm-ptbl .ptr{display:flex;padding:5px 8px;border-bottom:1px solid var(--mm-ptbl-row-border);cursor:pointer;transition:background 0.15s}'
+'.mm-ptbl .ptr:hover{background:var(--mm-ptbl-hover)}'
+'.mm-ptbl .ptr.sel{background:var(--mm-ptbl-sel);border-left:3px solid var(--mm-accent)}'
+'.mm-ptbl .ptr.phdr{background:var(--mm-ptbl-hdr-bg);cursor:default;font-weight:bold;font-size:11px;color:var(--mm-text-dim)}'
+'.mm-ptbl .pcol{flex:1;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}'
+'.mm-ptbl .pcol:first-child{max-width:40px;min-width:40px;color:var(--mm-ptbl-id)}'
+'.mm-ptbl .pcol:last-child{max-width:60px;min-width:60px;text-align:right;color:var(--mm-ptbl-score)}'
+'.mm-theme-btn{padding:3px 8px;font-size:10px;border-radius:4px;cursor:pointer;border:1px solid var(--mm-border);background:var(--mm-btn-bg);color:var(--mm-btn-text);transition:all 0.15s;margin-left:6px}'
+'.mm-theme-btn:hover{opacity:0.8}'
+'.theme-fried{font-family:Ubuntu,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}'
+'.theme-fried *{text-shadow:0 0 3px rgba(0,0,0,0.9),0 0 1px rgba(0,0,0,0.8);-webkit-text-stroke:0}'
+'.theme-fried .mm-hdr h2{font-size:16px;font-weight:700;text-shadow:0 0 4px rgba(0,0,0,1),0 0 1px rgba(0,0,0,0.9)}'
+'.theme-fried .mm-tab.active{border-bottom:2px solid var(--mm-tab-active-border);font-weight:700}'
+'.theme-fried .mm-tab{border-radius:4px 4px 0 0}'
+'.theme-fried .mm-dot.on{background:#ffa726;box-shadow:0 0 4px #ffa726}'
+'.theme-fried .mm-inp{text-shadow:none;font-family:Ubuntu,sans-serif;font-weight:600}'
+'.theme-fried .mm-btn{position:relative;overflow:hidden}'
+'.theme-fried .mm-btn::after{content:"";position:absolute;left:0;right:0;bottom:0;height:35%;background:rgba(0,0,0,0.1);pointer-events:none;border-radius:0 0 var(--mm-btn-radius) var(--mm-btn-radius)}'
+'.theme-fried .mm-btn:hover{filter:brightness(115%)}'
+'.theme-fried .mm-sec h3{text-shadow:0 0 8px rgba(255,140,66,0.2),0 0 3px rgba(0,0,0,0.9)}'
+'.theme-diep{font-family:Ubuntu,sans-serif}'
+'.theme-diep .mm-hdr h2{font-size:16px;font-weight:700}'
+'.theme-diep .mm-tab.active{border-bottom:2px solid var(--mm-tab-active-border);font-weight:700}'
+'.theme-diep .mm-tab{border-radius:4px 4px 0 0}'
+'.theme-diep{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}'
+'.theme-diep *{text-shadow:0 0 3px rgba(0,0,0,0.9),0 0 1px rgba(0,0,0,0.8);-webkit-text-stroke:0}'
+'.theme-diep .mm-hdr h2{text-shadow:0 0 4px rgba(0,0,0,1),0 0 1px rgba(0,0,0,0.9)}'
+'.theme-diep .mm-inp{text-shadow:none}'
+'.theme-diep .mm-btn{position:relative;overflow:hidden}'
+'.theme-diep .mm-btn::after{content:"";position:absolute;left:0;right:0;bottom:0;height:35%;background:rgba(0,0,0,0.1);pointer-events:none;border-radius:0 0 var(--mm-btn-radius) var(--mm-btn-radius)}'
+'.theme-diep .mm-btn:hover{filter:brightness(115%)}'
+'.theme-diep .mm-inp{font-family:Ubuntu,sans-serif;font-weight:600}'
;

var _stop=Event.prototype.stopPropagation,_prevent=Event.prototype.preventDefault;
function stopK(el){el.addEventListener('keydown',function(e){_stop.call(e);});el.addEventListener('keyup',function(e){_stop.call(e);});}
function flash(el,c){el.style.boxShadow='0 0 10px '+c;setTimeout(function(){el.style.boxShadow='';},400);}
function _ht(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
function getAuthToken(){try{var keys=Object.keys(W.localStorage);for(var i=0;i<keys.length;i++){var v=W.localStorage.getItem(keys[i]);if(v&&v.indexOf('eyJ')===0&&v.indexOf('.')>0)return v;}return null;}catch(e){return null;}}

var killFeedEl=null;
function initKillFeed(){
if(killFeedEl)return;
killFeedEl=document.createElement('div');
killFeedEl.style.cssText='position:fixed;top:60px;right:10px;width:250px;max-height:200px;overflow:hidden;z-index:999997;pointer-events:none;font-family:"Segoe UI",sans-serif;font-size:12px';
document.body.appendChild(killFeedEl);}
function addKillFeedEntry(msg,color){
if(!S.showKillFeed)return;
if(!killFeedEl)initKillFeed();
S.killFeed.push({msg:msg,color:color||'#fff',time:Date.now()});
if(S.killFeed.length>8)S.killFeed.shift();
renderKillFeed();}
function renderKillFeed(){
if(!killFeedEl)return;
var now=Date.now();
S.killFeed=S.killFeed.filter(function(e){return now-e.time<10000;});
killFeedEl.innerHTML=S.killFeed.map(function(e){
var age=(now-e.time)/10000;var opacity=Math.max(0,1-age);
return '<div style="padding:3px 8px;margin:2px 0;background:rgba(0,0,0,0.6);border-radius:4px;color:'+e.color+';opacity:'+opacity.toFixed(2)+';text-shadow:1px 1px 2px #000">'+_ht(e.msg)+'</div>';}).join('');}

function _simKey(type,code){var ev=new KeyboardEvent(type,{key:'',code:'',keyCode:code,which:code,bubbles:true,cancelable:true,composed:true});window.dispatchEvent(ev);document.dispatchEvent(ev);}
function holdKToLevel(ms){_simKey('keydown',75);setTimeout(function(){_simKey('keyup',75);},ms||1500);}
function allocToSeq(alloc){
var seq='';for(var i=0;i<alloc.length&&i<8;i++){var c=alloc[i];var pts=(c>='A'&&c<='F')?parseInt(c,16):(parseInt(c)||0);for(var p=0;p<pts;p++)seq+=String(i+1);}return seq;}
function applyBuild(buildStr){
if(!buildStr)buildStr='77777777';
var seq=allocToSeq(buildStr);
gameAPI.cmd('game_stats_build '+seq);}
function maxStatsDelayed(buildStr,cb){
applyBuild(buildStr);
if(cb)setTimeout(cb,100);}

function reapplySettings(){
if(S.god){if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}setTimeout(function(){gameAPI.cmd('set_invulnerability true');},200);setTimeout(function(){gameAPI.cmd('set_invulnerability true');},400);setTimeout(function(){gameAPI.cmd('set_invulnerability true');},700);}
if(S.fastLvl){gameAPI.cmd('allow_fast_leveling true');setTimeout(function(){gameAPI.cmd('allow_fast_leveling true');},200);}
if(S.tankSw)setTimeout(function(){gameAPI.cmd('allow_tank_switch true');},150);
if(S.selfDest)setTimeout(function(){gameAPI.cmd('allow_self_destruct true');},200);
if(S.bossCon)setTimeout(function(){gameAPI.cmd('allow_boss_control true');},250);
}
function toggleAutoRespawn(){
S.autoRespawn=!S.autoRespawn;
if(S.autoRespawn&&!S.autoRespawnTimer){
var _arWasAlive=false;
S.autoRespawnTimer=setInterval(function(){
try{
var ks=getLocalKillScoreFromFiber();
var alive=ks!==null;
if(!alive&&_arWasAlive){
gameAPI.cmd('game_spawn');
setTimeout(reapplySettings,500);
if(S.keepBuild&&S.lastBuild){setTimeout(function(){applyBuild(S.lastBuild);},800);}}
_arWasAlive=alive;
}catch(e){}
},1500);}
else if(!S.autoRespawn&&S.autoRespawnTimer){
clearInterval(S.autoRespawnTimer);S.autoRespawnTimer=null;}}

function absMaxLevel(statusEl,buildStr){
gameAPI.cmd('set_max_level 120');
gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;
if(buildStr)applyBuild(buildStr);
holdKToLevel(12000);
if(statusEl){setTimeout(function(){statusEl.textContent='Level 120 reached';statusEl.style.color='#0f0';},12100);}}

var TANK_CYCLE=[0,60,56,53,57,58,59,55,54,52,51,50,49,48,44,43,42,41,40,39,38,36,35,34,33,32,31,29,28,26,25,24,23,22,21,20,19,18,17,15,14,13,12,11,10,8,9,7,6,5,4,3,2,1];
var TANK_CYCLE_MAP={};for(var _tc=0;_tc<TANK_CYCLE.length;_tc++){TANK_CYCLE_MAP[TANK_CYCLE[_tc]]=_tc;}

function getCurrentTankName(){
try{
var pl=getPlayerList();
if(pl&&pl.length>0){
for(var i=0;i<pl.length;i++){
if(pl[i].tank!==undefined)return{id:pl[i].tank,name:TANKS[pl[i].tank]||null};
if(pl[i].tankId!==undefined)return{id:pl[i].tankId,name:TANKS[pl[i].tankId]||null};
if(pl[i].class!==undefined)return{id:pl[i].class,name:TANKS[pl[i].class]||null};}}
var tn=gameAPI.get('player_tank_name');if(tn&&tn!=='undefined')return{id:-1,name:tn};
var ti=gameAPI.get('player_tank');if(ti!==null&&ti!==undefined&&ti!=='undefined'){var tid=parseInt(ti);if(!isNaN(tid))return{id:tid,name:TANKS[tid]||null};}
if(S.currentTankIdx>=0&&S.currentTankIdx<TANK_CYCLE.length){
var trackId=TANK_CYCLE[S.currentTankIdx];
return{id:trackId,name:TANKS[trackId]||null,tracked:true};}
}catch(e){}
return null;}

function getTankUpgradePath(tid){if(tid===0)return[0];var q=[[0]],v={0:true};while(q.length>0){var p=q.shift(),c=p[p.length-1],ch=TANK_UPGRADES[c];if(!ch)continue;for(var i=0;i<ch.length;i++){if(v[ch[i]])continue;var np=p.concat([ch[i]]);if(ch[i]===tid)return np;v[ch[i]]=true;q.push(np);}}return null;}
function _pressBackslash(){var canvas=document.getElementById('canvas');var tgt=canvas||document;var down=new KeyboardEvent('keydown',{key:'\\',code:'Backslash',keyCode:220,which:220,bubbles:true,cancelable:true,composed:true});tgt.dispatchEvent(down);window.dispatchEvent(down);setTimeout(function(){var up=new KeyboardEvent('keyup',{key:'\\',code:'Backslash',keyCode:220,which:220,bubbles:true,cancelable:true,composed:true});tgt.dispatchEvent(up);window.dispatchEvent(up);},80);}
function switchToTank(targetId,statusEl){
if(S.tankSwitchTimer){clearInterval(S.tankSwitchTimer);S.tankSwitchTimer=null;}
gameAPI.cmd('allow_tank_switch true');S.tankSw=true;
var targetName=TANKS[targetId]||'Tank #'+targetId;
var targetCycleIdx=TANK_CYCLE_MAP[targetId];
if(targetCycleIdx===undefined){if(statusEl){statusEl.textContent=targetName+' (not in cycle)';statusEl.style.color='#f80';}return;}
var curIdx=S.currentTankIdx||0;
var stepsNeeded=(targetCycleIdx-curIdx+TANK_CYCLE.length)%TANK_CYCLE.length;
if(stepsNeeded===0){if(statusEl){statusEl.textContent=targetName+' (already selected)';statusEl.style.color='#0f0';}return;}
var stepsDone=0;
if(statusEl){statusEl.textContent='Switching to '+targetName+' ('+stepsNeeded+' steps)...';statusEl.style.color='#ff0';}
S.tankSwitchTimer=setInterval(function(){
if(stepsDone>=stepsNeeded){clearInterval(S.tankSwitchTimer);S.tankSwitchTimer=null;
S.currentTankIdx=targetCycleIdx;
if(statusEl){statusEl.textContent=targetName+' selected!';statusEl.style.color='#0f0';}return;}
_pressBackslash();
stepsDone++;
if(statusEl){statusEl.textContent='Switching... ('+stepsDone+'/'+stepsNeeded+')';}
},500);}

function createGame(name,region,pw,stEl){
stEl.textContent='Creating game...';stEl.style.color='#ff0';
var token=getAuthToken();
if(!token){stEl.textContent='Not logged in. Auth token required.';stEl.style.color='#f00';return;}
var body={name:name||'Custom Game',region:region||'atl'};
if(pw)body.password=pw;
if(typeof GM_xmlhttpRequest==='undefined'){stEl.textContent='GM_xmlhttpRequest not available';stEl.style.color='#f00';return;}
var headers={'Accept':'application/json','Content-Type':'application/json'};if(token)headers['Authorization']='Bearer '+token;
GM_xmlhttpRequest({method:'POST',url:'https://api.diep.io/sandbox/create',headers:headers,data:JSON.stringify(body),
onload:function(r){try{var d=JSON.parse(r.responseText);if(d.gameID||d.id||d.hash){var gid=d.gameID||d.id||d.hash;stEl.textContent='Game created! Joining...';stEl.style.color='#0f0';
setTimeout(function(){W.location.href='https://diep.io/#'+gid;},500);}else{stEl.textContent='Failed: '+(d.error||d.message||r.responseText);stEl.style.color='#f00';}}catch(e){stEl.textContent='Parse error: '+e.message;stEl.style.color='#f00';}},
onerror:function(){stEl.textContent='Request failed';stEl.style.color='#f00';}});}

var LOGO_URI='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAF9klEQVR42qXV/2vdVx3H8ef7nM/nc78luU2aLzW1XWu/zLVWh6tdtXMtw6pIdYpNhNHhNqwIgynSDdkPpmGwHxSGishUhhT7i+vaUmWwia2pA7FfLbRmTdfSpN+SJjc39yY3937u58t5+0MYrLQqsvMHnMc579f7vA8AA2BEhI9Afu+ONUde/saGS7tW8DAIA1vx+DBrYXMDkBv86v1vn/vTr/TEwV/oy/0bJ7+5mI0fFrF/E6OqLrd3x5o3n3xh8Au5ju64Wp7Wles2tDbr0/3FG7eOvXpJbgxsxTs+hvt/AemClmd3rD20a8+Pty9e+UBcn53xZsslyhPjKaresT/snzl79OyXDs/KqV8/pP7aFhRgWzc6NIkATB1H+yG9J/DS19cf2fnsnq91r14fz1crnuf7gDI9fpPK1KQT1B7dv6987tj5Lx9ucOo/nVQHMAeGkf4Dd0Jeob1jU/fKtWTyLSYKQ5KoibWW9q4e0iQ2UdhIvvjM7o5G/Zdvff/dS7s3rzYrcK4967EqdjTCJqenKpyRQU4CvN6H/SAiT61k+yM7+w73/+DFgvEyaXVywqhLMdYQRSHXLo4w9c+Tmh/5synqDJ1tQiEPLlTq80otFqYbMDGj+yp1XnvuXd5RRUQWLiYgbM/oY48/vePNXS/uzRo/k86VJg0ooxfOUz34E4LSuLT3GjUprr0VbdYcLUVDM4TKtMMYTDYQU2nA8E3d+8w5XlJFEbDffQj/wHVzpXl65ERaG+37xJZHA2utmy6VpXzop3xu2VXpWtHG0geXSFyumvaejGntCgxGjagaL+dJS48vlZkkzXqiq7vlsS0tfHpwNwfpA3tmHDewVb3fXbOX5czlfyTzV/se2PJoMP6vYdcxdsgsWxVgwgaN8TkKBSGNHAh4VhFQ4wmLenNinTNxw0mjSfTRoqzLZLjx5DFOW4DjYwvIb8fsFf/slROJK/V3zowEvXbEmSAvtjWHc6kW2ozMhpZwLkFip8aI2IxIdbxJ0nCkCRLGSOxAPdZ2jvJ7+37a7yOvXjOXp0+O/H1jcGXn+lWer4UcmeVLSKdK4hJD8f4ubcuEEtaVpO4k15PBBFYDSSVF6Oy15uakup4W6ZYsE/aDPTv0FDCk8vinCvkND2e+13lf1mO2hq3NSNKAeiUh0IjmbEquxQq+IW06ggCJQyWTM7QUhMDHRXWVSsi4uQMYwgyCS3LJ5vvWtWddI0ylYzGabcH6SqbNI5qPsb6ImIUcWluFeDbB5gy1qqMylap1apqRioPPm3u9St81o/rolLpYaEzModYinqfZ9gBAmnWHWKOZvKFWg9gJQdZQWGTVpSqzcyrGCPmA9ntOyWzeW962NC/OoVKtCpFRu7hIWp7DWvB8g2smYgKrWEtSa0pTIAqRJAYcGnhIM+H6nSU6vjAtp24lBy+PNKtarXp+FtXEoWEEcYTXWQALmsuivo+fhpIp+hoUDPk2oVxD8YRimxBGeHcAg+BUMU9f4mKp5C7YNcs0s3ppKoGPixVNFa02EGtUw4i01hTxBBN45FZ0U+jNa+rAU+X6lDIzz8/vymBoG0ZBKmV5i8g3Jmqq1kMRo3i9BZopGs+nmBZfVR21itKshFQvTOj05TqBhWwgMlHRNG7wl7uAbcdJGUAOv11/5Z0/3hiiFnpG0sQEvnit7ep1dxBHTryuJeRX92jxYxnmQyFVaF2exQQSN+oqUcorz48ydhcgoAeGkd+I1E8daTx3vdwhdtMmj3otaVwtITNzGHU6NzJB5bZSHo3UZDz8YkYbY/VkaYcNzo7qvidO8MJft+LZe3XRgWH09T7s7vNMPBI1SpKazd0fX1Twi1kRKw5Jict1zRU9POtra0+eIJcxldsNO3KLo++dcd/57I9oju5D5b/9pzqAkUHcAKz4yrc7X+vpiLb0rm3P+DJHPBPhd7VQfa9MaSpltupuXxzRPU9cYD+Aggj/AwDQPqy8ISmq/Aw++eC3Op8vtsafaUzOTQfFzKKpm+FQZUoPjY8z/MMS46oIslBqgH8DyVX27I668qwAAAAASUVORK5CYII=';
var FAVICON_URI='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAJjUlEQVR42rWXaYydVRnH/885533fu81d5k5nOktnukw7LZ2WYi2LIWmRVoJGSLRTQPCDCaImEBfUSDBOBiWExJgYBBVJrIAfKImkJRJIW9sRoiXYhRZaKF1mpr0znXvv3H17l3MeP5SySask+Hw6OSc5zy/Pc3L+zx/4QIyuhwIId6+JbvzxtemDP7gm/fd7Pjdvyftn/8e4kPzOIdzw8JY19f3bH+O/PPRt/v416eN3X9m1CABGRiA/7bzyQvKxcQq+uYxvHFy9asfIvQ+EmCiIts/T6fk98yaOHLh5XV98x9ad9cLICOTRo+BPDeC95Cv4xsHVq5+79b6HLctxTGUuJ6vFgmjv7tPp+T3tE4f333TlgsSnDiHHJ2HuHMINy1Zfvn3kRw/abe0dxuhARBNJEIBSLis6evt1ursnffrQa1++sj/1/CUg6JMC0HfWOBsXLlu+Y/O9D4Sj8aQOAl+QkBBE8FpNFLPnUDg3jbb2Dj119Ija/fTW00JEr//N4dnTv18LK7UYBgBGLgPT2Pn1thHIeVlQbhy8BdCXBLh/44IjX7//oeGOgaWBDgLptZoQQgDMEEKgWa+ilM+imJ1FIt2hTx06qHY9vfWUbzub/nikfOoj9wngPMR7j/v8HsY+sn8hlDbG9X0fdigEZYdRLeTgNeoQSkFrjVAkhljCAwEoz+Xk0s+uCwI/WPy3J7e+9OCgvK8vpTcJiQQxErEwLfEDuPUmH3RdHGsF2PPdt7APAEZHIcbGwMCH20Z3LrfW9gwueeH2+37R2btkecAkZK2Qh/ZdEAkwGwgiFPPn0Gq0cO7kSbj7d3I8d1CELUZXEnBsgqWARtkg0EDDBTQDk3nGdBG/a9Xx+A8ncRAAtgHyg22RB/JmptvN7z558LXNC1cMx9q7urS0HBF4HtgYCCHgeS6Kszm8/dSj5P3zeYoWzlBnko0ybKSEaTXZ+JoZguDWmb2AtOdD93WSSEdpXTqBb21KYXlhBn/9JeBvA+Sz71ZCjq6HevR1mk5li7sn3njlq4tWDsc6evq0UEoGvgdjAmTPnMHEM7/FWucwdcUN2hYlkepXNDfr0+IhS8LXgohEulNQOCGINAthWLg+YEvSHSlCKkSrFsZx49UODt5VwtlRQIwDLMcnYUbXQ/3hpMgMtGq7Jg69vLl/xcpYuqc3cOs14RuNid3PY2l+Jy2/Kg5DBj0ru5FMCQTZKoUjEm0L47CVhssSfl0j8Inb+iNIzQNKBS3KFVAyTsFAB/UFBrddYWHn/UVMrxyBlABwHoLVY8dEZpFb3zV1+OXNC5avjCU7u/TszDnR+McOrFqSI+FYcIRB9WQOzWwTsbiAEASpCNABlACkYFg2CI6DjoEoRWxNfs0AgCxU2F/YSeFKC1ddPo3HLxvB+3/7BYhHjlFmUDd2TR14efOSdVfHdHbW6FeeEgOr4nBzTThWAHIsDsclReISpZyP7KQHJcCSmJgBIYlNzaXKrIdq0UASgwGUKxBNj0wsTN0rNPb8dDsmPiQuF9rx6zcos6LW3PXO0X0j0dnj0TWds0YkYwTDsFcs43q5Qdz0YElCkG5HskcgqGu4TYZURBAEuydGjZyLaAxo1kGuDyQ6JUpF5s40Uc3l1aunsfU/1G18EmYUUL9yKePNVF+6XGVvWTUkwux5HE5HYbWn4LRy5Fd8kBRoG+xiu1qiWFxQo2LAmomYKdbnIKhpdmyQUATlAN3dkk5mDJEBp9qod8bFvotpvBkFi8Fu2x8aEna4U4Ati7hQJO1VGYbQqjK0H0BNZqhRDFgIINWl0KwZsBRoZFocn6+olnFBIO7skeQ1GFcMSzo7ZbTQTLEYloqPy75hPcQYYES7f9uajX1Rq80OyLGJomFmUmDHgu0Agcfs1Xx2wgTlCBCBhASkAis2VJvxYMUk2BKUO6NBkgDXcEgweQGIBD4jLiUUgrnSnKsBTQ/uiRyjqxeiYx4FszVEF0QR6w4RG6BZN8TMxIKgFBCymGJpCTCgfYYTITQqBuWcZjagegMCYIQdfPGSAJaFsMlkERhFJhpG/fBJ4laNqS3EuhGABEFaoMBjZsMgSawcAW0IE8cDljbBiUpYEYnEPMFKEVVrjIDBiQhBCtQvOefZYRpODqUYGgjZNhq1FsMYqL4OsGc4mMkTB4xImyCpBEwzIDBDdSRYTpfJr2uwBiAMExG1mgYwgKOYXZ9Qd3H4khVolHhvfk4SWjV400U4cQVTrEGfzULnS+flNBWCCksYTwO2BQo7HOQrlEiAiQD2NdyWoUKJueWBAw2k04KUAITAgo8FuG4cmhl07AieODSef9Nooew2ZVTcAkhALh2EMAERG0BJZhIwIQtwLMCxATBMYGClopwcjGPegIWh1RZNzwHNgDieEFypM1oe9l2sAowtEGOA57fM22JRv7F7u1gOLgYsgp7MIPDBzMRcqBM8HxLMplhHkKsQpIQVInBgoCNJFvPTPDsnOGIBqQhjekrLiRIKhQp+ftEW7M2CAEJ1Gi+Ws0JAaTbZEkhIIs8lxGyotCKPBQdNwyQIHJIMJbhVClDKM/xqA5XXp1Dcn6X6jEuOApQtTMOF9jTOjp3B9EXn/D9NgkcBkSviTQTVTb2Lov1Wccaw0QLKZntRL0mHGdEQimc9WBKIDi+EpCZYG6KIQrPMsB1CeL6DUJtAPqcpHoYJPKhTWTz0Qg6vXtJo7AVwHSG4+qR+zhOt2xev64oyFENrYYo1GEQgNCCFge03yAt1QbYnYNs1ankC+RMu7BDBTodRntNolrQu1qCOZPjx6cP42QaALgkwBmDPeqhbJqn+pRC3Dawe+Hxo3TKNao1MPgcTGJhSGbrqoVFloFGmco7RyNQwd6KFVJ+NRH+YCseqCAn2Umllv3pYv/iNg7h17yjounGY/2q1tk6CV45AYg/vy5QLy6UnhmNL2slRjYA7+yCTcVJUB8VCcM81kZwvEVnSDVmuID68gGFHjK5XiSxLHTjiHy/n+Gt3XI/KyseAMYD/VyNBF6bZJxx8Yejm5J+vvamvA6WzMFDaeA0KXGZdNwilQ/B9ATdXkUQC2gMOHQtMZoK/l3kbT/4EKDNAdGEm/CQmhkch1u6mExuKre3n3snamdO+bfUu6E70JYmrZRFoR1g9KeFmy+LoW9qdLfDM1JT+V3aW77rjKJ7ZBbijgLjuA6P5J7ZS20YgtzxL+t071LOX4Z70oshXvIaLoK5nQlH0N1o4kZvCI1MZHBsDKnj3LW0Yh6aP+IJ/A4OL4nuOxxuSAAAAAElFTkSuQmCC';
var menuEl=null,menuVisible=false,activeTab='admin';
function createMenu(){
if(menuEl)return;
var style=document.createElement('style');style.textContent=CSS;document.head.appendChild(style);
menuEl=document.createElement('div');menuEl.id='diep-mod-menu';menuEl.className='theme-'+_currentTheme;
var _themeLabels={midnight:'\u263E Midnight',diep:'\u2B23 Diep',fried:'\uD83C\uDF57 Fried'};
menuEl.innerHTML='<div class="mm-hdr"><div style="display:flex;align-items:center;gap:6px"><img src="'+LOGO_URI+'" width="24" height="24" style="border-radius:4px" /><h2>DiepFried</h2></div><div style="display:flex;align-items:center;gap:6px"><div class="mm-theme-btn" id="mm-theme-toggle" title="Switch theme">'+(_themeLabels[_currentTheme]||_themeLabels.diep)+'</div><div class="mm-sts"><span class="mm-dot" id="mm-ws-dot"></span><span>WS</span><span class="mm-dot" id="mm-api-dot"></span><span>API</span></div></div></div><div class="mm-tabs" id="mm-tabs"></div><div class="mm-body" id="mm-body"></div>';
document.body.appendChild(menuEl);
menuEl.querySelector('#mm-theme-toggle').addEventListener('click',function(){var themes=['midnight','diep','fried'];var idx=(themes.indexOf(_currentTheme)+1)%themes.length;setTheme(themes[idx]);this.textContent=_themeLabels[_currentTheme]||_themeLabels.diep;});
var tabs=['Admin','Stats','Tanks','Bosses','Server','Players','Visuals','Macros','Lobby','Packets','Console'];
var tabBar=menuEl.querySelector('#mm-tabs');
var _tabDescs={admin:'Self settings, god mode, spawning',stats:'Stat builds and level management',tanks:'Browse and switch between all tanks',bosses:'Spawn and manage bosses',server:'Server settings, map, teams',players:'Player list, kick/ban/promote',visuals:'Visual settings, colors, themes',macros:'Quick macros, scripts, presets',lobby:'Browse/create games, Infection, HSA',packets:'WebSocket packet monitor',console:'Raw command execution'};
tabs.forEach(function(t){var tab=document.createElement('div');tab.className='mm-tab'+(t.toLowerCase()===activeTab?' active':'');tab.textContent=t;tab.dataset.tab=t.toLowerCase();tab.title=_tabDescs[t.toLowerCase()]||t;tab.addEventListener('click',function(){switchTab(t.toLowerCase());});tabBar.appendChild(tab);});
makeDrag(menuEl,menuEl.querySelector('.mm-hdr'));
renderTab(activeTab);setInterval(updateSts,2000);}

function makeDrag(el,h){var dx=0,dy=0,mx=0,my=0;
h.addEventListener('mousedown',function(e){if(e.target.tagName==='INPUT'||e.target.tagName==='SELECT')return;
mx=e.clientX;my=e.clientY;
function onM(e2){dx=e2.clientX-mx;dy=e2.clientY-my;mx=e2.clientX;my=e2.clientY;el.style.top=(el.offsetTop+dy)+'px';el.style.left=(el.offsetLeft+dx)+'px';}
function onU(){document.removeEventListener('mousemove',onM);document.removeEventListener('mouseup',onU);}
document.addEventListener('mousemove',onM);document.addEventListener('mouseup',onU);});}

function updateSts(){if(!menuEl)return;
var wd=menuEl.querySelector('#mm-ws-dot'),ad=menuEl.querySelector('#mm-api-dot');
if(wd)wd.className='mm-dot '+(WS.connected?'on':'off');
if(ad){gameAPI.init();ad.className='mm-dot '+(gameAPI.ready?'on':'off');}}

function switchTab(tab){
if(activeTab==='players'&&typeof _clearAutoRef==='function')try{_clearAutoRef();}catch(e){}
if(activeTab==='packets')pktLogActive=false;
activeTab=tab;if(tab==='packets')pktLogActive=true;
menuEl.querySelectorAll('.mm-tab').forEach(function(t){t.classList.toggle('active',t.dataset.tab===tab);});
renderTab(tab);}

function renderTab(tab){var body=menuEl.querySelector('#mm-body');body.innerHTML='';
var r={admin:rAdmin,stats:rStats,tanks:rTanks,bosses:rBosses,server:rServer,players:rPlayers,visuals:rVisuals,macros:rMacros,lobby:rLobby,packets:rPackets,console:rConsole};
if(r[tab])r[tab](body);}

function rAdmin(b){
b.innerHTML='<div class="mm-info">Shortcuts: <b>Alt+G</b>=God Mode, <b>Alt+M</b>=Max Lvl+Stats, <b>Alt+T</b>=Tank Switch, <b>Insert</b>=Toggle Menu<br>Settings auto-reapply after respawn.</div>'
+'<div class="mm-sec"><h3>Self</h3><div class="mm-row">'
+'<div class="mm-btn" id="mm-god" title="Toggle invulnerability. Shortcut: Alt+G">God Mode: OFF</div>'
+'<div class="mm-btn" id="mm-maxlvl" title="Set max level cap to 45">Max Level (45)</div>'
+'<div class="mm-btn warn" id="mm-abs" title="Gradually level up to 120 (takes a few seconds)">Absolute Max (120)</div>'
+'</div><div class="mm-row">'
+'<div class="mm-btn" id="mm-fast" title="Toggle instant leveling when gaining XP">Fast Leveling: OFF</div>'
+'<div class="mm-btn danger" id="mm-sd" title="Toggle the ability to self destruct your tank">Self Destruct: OFF</div>'
+'</div><div class="mm-row">'
+'<div class="mm-btn" id="mm-tsw" title="Toggle the ability to switch tanks. Shortcut: Alt+T">Tank Switch: OFF</div>'
+'<div class="mm-btn" id="mm-bc" title="Toggle the ability to control spawned bosses">Boss Control: OFF</div>'
+'</div><div class="mm-row">'
+'<div class="mm-btn" id="mm-ar" title="Automatically respawn when you die">Auto-Respawn: OFF</div>'
+'<div class="mm-btn" id="mm-aafk" title="Prevent AFK timeout by periodically pinging server">Anti-AFK: OFF</div>'
+'</div><div id="mm-abs-st" style="font-size:11px;color:#888;margin-top:4px"></div></div>'
+'<div class="mm-sec"><h3>Level Cap</h3><div class="mm-row">'
+'<input class="mm-inp" id="mm-lcv" type="number" min="1" max="999" value="45" style="width:80px" />'
+'<div class="mm-btn" id="mm-slc" title="Set a custom level cap">Set Level Cap</div></div></div>'
+'<div class="mm-sec"><h3>Spawn</h3><div class="mm-row">'
+'<input class="mm-inp" id="mm-spn" placeholder="Player name" style="flex:1" title="Optional: enter a name to spawn as" />'
+'<div class="mm-btn success" id="mm-spnb" title="Spawn or respawn your tank">Spawn/Respawn</div></div></div>'
+'<div class="mm-sec"><h3>Team</h3><div class="mm-row">'
+'<input class="mm-inp" id="mm-jtv" type="number" min="0" value="0" style="width:80px" title="Team number (0-based)" />'
+'<div class="mm-btn" id="mm-jt" title="Join the specified team">Join Team</div></div></div>';
var g=b.querySelector('#mm-god'),fl=b.querySelector('#mm-fast'),sd=b.querySelector('#mm-sd'),ts=b.querySelector('#mm-tsw'),bc=b.querySelector('#mm-bc');
g.addEventListener('click',function(){S.god=!S.god;
if(S.god&&!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;setTimeout(function(){gameAPI.cmd('set_invulnerability true');},200);}
else{gameAPI.cmd('set_invulnerability '+(S.god?'true':'false'));}
if(!S.god)S.godPromoted=false;
this.textContent='God Mode: '+(S.god?'ON':'OFF');this.className='mm-btn'+(S.god?' active':'');flash(this,S.god?'#0f0':'#f00');});
b.querySelector('#mm-maxlvl').addEventListener('click',function(){gameAPI.cmd('set_max_level 45');flash(this,'#0f0');});
b.querySelector('#mm-abs').addEventListener('click',function(){absMaxLevel(b.querySelector('#mm-abs-st'));flash(this,'#ff0');});
fl.addEventListener('click',function(){S.fastLvl=!S.fastLvl;gameAPI.cmd('allow_fast_leveling '+(S.fastLvl?'true':'false'));
this.textContent='Fast Leveling: '+(S.fastLvl?'ON':'OFF');this.className='mm-btn'+(S.fastLvl?' active':'');});
sd.addEventListener('click',function(){S.selfDest=!S.selfDest;gameAPI.cmd('allow_self_destruct '+(S.selfDest?'true':'false'));
this.textContent='Self Destruct: '+(S.selfDest?'ENABLED':'OFF');this.className='mm-btn'+(S.selfDest?' active':'');});
ts.addEventListener('click',function(){S.tankSw=!S.tankSw;gameAPI.cmd('allow_tank_switch '+(S.tankSw?'true':'false'));
this.textContent='Tank Switch: '+(S.tankSw?'ON':'OFF');this.className='mm-btn'+(S.tankSw?' active':'');});
bc.addEventListener('click',function(){S.bossCon=!S.bossCon;gameAPI.cmd('allow_boss_control '+(S.bossCon?'true':'false'));
this.textContent='Boss Control: '+(S.bossCon?'ON':'OFF');this.className='mm-btn'+(S.bossCon?' active':'');});
b.querySelector('#mm-slc').addEventListener('click',function(){var v=parseInt(b.querySelector('#mm-lcv').value)||45;gameAPI.cmd('set_max_level '+v);S.maxCap=v;flash(this,'#0f0');});
b.querySelector('#mm-spnb').addEventListener('click',function(){var n=b.querySelector('#mm-spn').value.trim();gameAPI.cmd('game_spawn'+(n?' '+n:''));flash(this,'#0f0');});
b.querySelector('#mm-jt').addEventListener('click',function(){var v=parseInt(b.querySelector('#mm-jtv').value)||0;gameAPI.cmd('join_team '+v);flash(this,'#0f0');});
var arBtn=b.querySelector('#mm-ar');
arBtn.addEventListener('click',function(){toggleAutoRespawn();
this.textContent='Auto-Respawn: '+(S.autoRespawn?'ON':'OFF');this.className='mm-btn'+(S.autoRespawn?' active':'');flash(this,S.autoRespawn?'#0f0':'#f00');});
if(S.god){g.textContent='God Mode: ON';g.className='mm-btn active';}
if(S.fastLvl){fl.textContent='Fast Leveling: ON';fl.className='mm-btn active';}
if(S.tankSw){ts.textContent='Tank Switch: ON';ts.className='mm-btn active';}
if(S.selfDest){sd.textContent='Self Destruct: ENABLED';sd.className='mm-btn active';}
if(S.bossCon){bc.textContent='Boss Control: ON';bc.className='mm-btn active';}
if(S.autoRespawn){arBtn.textContent='Auto-Respawn: ON';arBtn.className='mm-btn active';}
var aafkBtn=b.querySelector('#mm-aafk');
aafkBtn.addEventListener('click',function(){S.antiAfk=!S.antiAfk;
if(S.antiAfk){var _afkBlob=new Blob(['setInterval(function(){postMessage("ping");},25000);'],{type:'application/javascript'});S._afkWorker=new Worker(URL.createObjectURL(_afkBlob));S._afkWorker.onmessage=function(){gameAPI.set('ren_scoreboard_names','true');};}
else{if(S._afkWorker){S._afkWorker.terminate();S._afkWorker=null;}}
this.textContent='Anti-AFK: '+(S.antiAfk?'ON':'OFF');this.className='mm-btn'+(S.antiAfk?' active':'');flash(this,S.antiAfk?'#0f0':'#f00');});
if(S.antiAfk){aafkBtn.textContent='Anti-AFK: ON';aafkBtn.className='mm-btn active';}
b.querySelectorAll('.mm-inp').forEach(stopK);}

function rStats(b){
var h='<div class="mm-info">Applies stat upgrades one at a time for reliability.</div>'
+'<div class="mm-sec"><h3>Quick Actions</h3><div class="mm-row">'
+'<div class="mm-btn success" id="mm-ms1" title="Set level 45, enable fast leveling, and max all 8 stats to 7">Max Level + Max Stats</div>'
+'<div class="mm-btn warn" id="mm-ms2" title="Incrementally level to 120 then max all stats">Lvl 120 + Max Stats</div>'
+'<div class="mm-btn danger" id="mm-ms0" title="Reset all stat upgrades to 0">Reset Stats (0)</div>'
+'</div><div id="mm-stst" style="font-size:11px;color:#888;margin-top:4px"></div></div>'
+'<div class="mm-sec"><h3>Custom Build</h3><div class="mm-row">'
+'<input class="mm-inp" id="mm-bs" placeholder="e.g. 77777777" maxlength="8" style="width:120px" title="8 digits, one per stat (0-7). Order: HP Regen, Max HP, Body DMG, Bul Spd, Pen, DMG, Reload, Move" />'
+'<div class="mm-btn" id="mm-ab" title="Apply the custom build string to your stats">Apply</div></div>'
+'<div style="font-size:10px;color:#666;margin-top:2px">8 digits: HP Regen, Max HP, Body DMG, Bul Spd, Pen, DMG, Reload, Move</div></div>'
+'<div class="mm-sec"><h3>Individual Stats</h3><div id="mm-capsn" style="font-size:10px;color:#888;margin-bottom:4px"></div>';
for(var i=0;i<STATS.length;i++){var sc=STAT_COLORS[i];h+='<div class="mm-row"><span class="mm-lbl" style="color:'+sc+'">'+STATS[i]+'</span><div class="mm-btn small" data-si="'+i+'" data-sv="max" title="Max '+STATS[i]+'">MAX</div><div class="mm-btn small" data-si="'+i+'" data-sv="0" title="Reset '+STATS[i]+' to 0">0</div></div>';}
h+='</div><div class="mm-sec"><h3>Presets</h3><div class="mm-grid">';
var bn=Object.keys(BUILDS);for(var j=0;j<bn.length;j++){h+='<div class="mm-btn small" data-bp="'+BUILDS[bn[j]]+'" title="Apply '+bn[j]+' build: '+BUILDS[bn[j]]+'">'+bn[j]+'</div>';}
h+='</div></div>';b.innerHTML=h;
var stEl=b.querySelector('#mm-stst');
b.querySelector('#mm-ms1').addEventListener('click',function(){var self=this;gameAPI.cmd('set_max_level 45');gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;stEl.textContent='Leveling + applying stats...';stEl.style.color='#ff0';var ct=getCurrentTankName();var caps=getTankCaps(ct&&ct.id>=0?ct.id:0);var mb='';for(var ci=0;ci<8;ci++){mb+=caps[ci]>9?caps[ci].toString(16).toUpperCase():String(caps[ci]);}S.lastBuild=mb;applyBuild(mb);holdKToLevel(1500);setTimeout(function(){stEl.textContent='Done! (build: '+mb+')';stEl.style.color='#0f0';flash(self,'#0f0');},1600);});
b.querySelector('#mm-ms2').addEventListener('click',function(){var self=this;stEl.textContent='Leveling to 120 + maxing stats...';stEl.style.color='#ff0';gameAPI.cmd('set_max_level 120');gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;applyBuild('77777777');holdKToLevel(12000);setTimeout(function(){stEl.textContent='Lvl 120 + Max Stats done!';stEl.style.color='#0f0';flash(self,'#ff0');},12100);});
b.querySelector('#mm-ms0').addEventListener('click',function(){gameAPI.cmd('game_stats_build 000000000000000000000000000000000');flash(this,'#f00');stEl.textContent='Stats reset';stEl.style.color='#f00';});
b.querySelector('#mm-ab').addEventListener('click',function(){var s=b.querySelector('#mm-bs').value.trim();if(s.length>0){while(s.length<8)s+='0';stEl.textContent='Applying...';stEl.style.color='#ff0';S.lastBuild=s;var self=this;maxStatsDelayed(s,function(){stEl.textContent='Done!';stEl.style.color='#0f0';flash(self,'#0f0');});}});
b.querySelectorAll('[data-si]').forEach(function(btn){btn.addEventListener('click',function(){var idx=parseInt(this.dataset.si),val=this.dataset.sv;if(val==='max'){var ct=getCurrentTankName();var caps=getTankCaps(ct&&ct.id>=0?ct.id:0);val=caps[idx];if(val===0){flash(this,'#f00');if(stEl){stEl.textContent=STATS[idx]+' not available for this tank';stEl.style.color='#f80';}return;}}else{val=parseInt(val)||0;}var hx=val>9?val.toString(16).toUpperCase():String(val);var cur=gameAPI.get('game_stats_build')||'00000000';while(cur.length<8)cur+='0';var arr=cur.split('');arr[idx]=hx;var nb=arr.join('');applyBuild(nb);flash(this,'#0f0');if(stEl){stEl.textContent=STATS[idx]+' set to '+val;stEl.style.color='#0f0';}});});
var _capsTimer=setInterval(function(){if(!b.querySelector('#mm-capsn')||!document.body.contains(b)){clearInterval(_capsTimer);return;}var ct=getCurrentTankName();if(ct&&ct.id>=0){var caps=getTankCaps(ct.id);var cn=b.querySelector('#mm-capsn');if(caps!==TANK_DEFAULT_CAPS){cn.textContent='Stat caps for '+(ct.name||'tank')+': '+caps.join('/')+' (Smasher-class has different caps)';cn.style.color='#fc8';}else{cn.textContent='';}}},1000);
b.querySelectorAll('[data-bp]').forEach(function(btn){btn.addEventListener('click',function(){var self=this;stEl.textContent='Applying...';stEl.style.color='#ff0';S.lastBuild=this.dataset.bp;maxStatsDelayed(this.dataset.bp,function(){stEl.textContent='Done!';stEl.style.color='#0f0';flash(self,'#0f0');});});});
b.querySelectorAll('.mm-inp').forEach(stopK);}

function rTanks(b){
var h='<div class="mm-info">Click a tank to auto-switch. Hover for upgrade path and stats. Color = tier.</div>'
+'<div class="mm-sec"><h3>Controls</h3><div class="mm-row">'
+'<div class="mm-btn" id="mm-ets" title="Enable the ability to switch tanks">Enable Tank Switch</div>'
+'<div class="mm-btn danger" id="mm-sts" title="Stop any ongoing tank switch sequence">Stop Switching</div>'
+'<div class="mm-btn warn" id="mm-rst" title="Respawn and reset tank position tracker to 0">Reset to Tank (respawn)</div>'
+'</div><div id="mm-tst" style="font-size:11px;color:#888;margin-top:4px"></div>'
+'<div id="mm-tcur" style="font-size:11px;color:#4a9eff;margin-top:2px">Current: Tank (tracked pos: 0)</div></div>'
+'<div class="mm-sec"><h3>Search</h3><div class="mm-row"><input class="mm-inp" id="mm-tsr" placeholder="Search tanks..." style="flex:1" /></div></div>';
var branches=Object.keys(TANK_TREE);
var _tLbl={0:'Special',1:'Base',2:'Tier 2 (L15)',3:'Tier 3 (L30)',4:'Tier 4 (L45)'};
var _tClr={0:'#f66',1:'#fff',2:'#8f8',3:'#8cf',4:'#fc8'};
for(var x=0;x<branches.length;x++){h+='<div class="mm-sec"><h3>'+branches[x]+'</h3><div class="mm-grid">';
var ids=TANK_TREE[branches[x]];for(var t=0;t<ids.length;t++){var nm=TANKS[ids[t]]||'Tank #'+ids[t];var inf=TANK_INFO[ids[t]]||{};var tip=nm+' (ID: '+ids[t]+')&#10;'+(_tLbl[inf.tier]||'?');if(inf.fov&&inf.fov!==1)tip+=' | FOV: '+inf.fov+'x';if(inf.b!==undefined)tip+=' | '+inf.b+' barrel'+(inf.b!==1?'s':'');if(inf.invis)tip+=' | Invisibility';if(inf.zoom)tip+=' | Zoom';var caps=getTankCaps(ids[t]);var capStr=caps===TANK_DEFAULT_CAPS?'':'&#10;Stat caps: '+caps.join('/');tip+=capStr;var pth=getTankUpgradePath(ids[t]);if(pth&&pth.length>1)tip+='&#10;'+pth.map(function(p){return TANKS[p]||'#'+p;}).join(' > ');var tc=_tClr[inf.tier]||'#aaa';h+='<div class="mm-btn small tbtn" data-tid="'+ids[t]+'" data-tn="'+nm.toLowerCase()+'" title="'+tip+'" style="border-left:3px solid '+tc+'">'+nm+'</div>';}
h+='</div></div>';}b.innerHTML=h;
var stEl=b.querySelector('#mm-tst');
var curEl=b.querySelector('#mm-tcur');
b.querySelector('#mm-ets').addEventListener('click',function(){gameAPI.cmd('allow_tank_switch true');S.tankSw=true;this.textContent='Enabled';this.className='mm-btn active';flash(this,'#0f0');});
b.querySelector('#mm-sts').addEventListener('click',function(){if(S.tankSwitchTimer){clearInterval(S.tankSwitchTimer);S.tankSwitchTimer=null;}stEl.textContent='Stopped';stEl.style.color='#f00';});
b.querySelector('#mm-rst').addEventListener('click',function(){
gameAPI.cmd('game_spawn');S.currentTankIdx=0;
if(curEl)curEl.textContent='Current: Tank (tracked pos: 0)';
flash(this,'#ff0');stEl.textContent='Respawned - position reset to Tank';stEl.style.color='#ff0';});
var tankUpdateTimer=setInterval(function(){
if(!curEl||!document.body.contains(curEl)){clearInterval(tankUpdateTimer);return;}
var cur=getCurrentTankName();
if(cur&&cur.name){curEl.textContent='Current: '+cur.name+(cur.tracked?' (tracked pos: '+S.currentTankIdx+')':' (detected)');}},500);
b.querySelector('#mm-tsr').addEventListener('input',function(){var q=this.value.toLowerCase();b.querySelectorAll('.tbtn').forEach(function(btn){btn.style.display=btn.dataset.tn.indexOf(q)>=0?'':'none';});});
b.querySelectorAll('.tbtn').forEach(function(btn){btn.addEventListener('click',function(){flash(this,'#4a9eff');switchToTank(parseInt(this.dataset.tid),stEl);});});
stopK(b.querySelector('#mm-tsr'));}

function rBosses(b){
var h='<div class="mm-info">Spawn and manage bosses in your custom game.</div><div class="mm-sec"><h3>Spawn</h3><div class="mm-grid">';
for(var i=0;i<BOSSES.length;i++)h+='<div class="mm-btn" data-bo="'+BOSSES[i]+'" title="Spawn '+BOSSES[i]+' (spawn_boss '+BOSSES[i]+')">'+ BOSSES[i]+'</div>';
h+='</div></div><div class="mm-sec"><h3>Custom</h3><div class="mm-row"><input class="mm-inp" id="mm-cb" placeholder="Boss name" style="flex:1" title="Type any boss name to spawn" /><div class="mm-btn success" id="mm-scb" title="Spawn the boss entered in the text field">Spawn</div></div></div>'
+'<div class="mm-sec"><h3>Multi-Spawn</h3><div class="mm-row"><select class="mm-inp" id="mm-mbs" style="flex:1" title="Select boss type to mass-spawn">';
for(var j=0;j<BOSSES.length;j++)h+='<option value="'+BOSSES[j]+'">'+BOSSES[j]+'</option>';
h+='</select><input class="mm-inp" id="mm-mbc" type="number" min="1" max="50" value="5" style="width:60px" title="Number of bosses to spawn (1-50)" /><div class="mm-btn warn" id="mm-ms" title="Spawn multiple copies of the selected boss">Spawn X</div></div></div>'
+'<div class="mm-sec"><h3>Auto Boss Spawner</h3><div class="mm-info">Periodically spawn bosses on a timer. Great for survival challenges or boss rush modes.</div>'
+'<div class="mm-row"><span class="mm-lbl">Boss:</span><select class="mm-inp" id="mm-absb" style="flex:1" title="Boss type to auto-spawn"></select></div>'
+'<div class="mm-row"><span class="mm-lbl">Every (s):</span><input class="mm-inp" id="mm-absi" type="number" value="10" min="1" style="width:60px" title="Spawn interval in seconds" /><span class="mm-lbl">Count:</span><input class="mm-inp" id="mm-absc" type="number" value="1" min="1" max="20" style="width:50px" title="Bosses per wave" /></div>'
+'<div class="mm-row"><div class="mm-btn success" id="mm-abstart" title="Start auto-spawning bosses">Start</div><div class="mm-btn danger" id="mm-abstop" title="Stop auto-spawning bosses">Stop</div></div>'
+'<div id="mm-abst" style="font-size:11px;color:#888;margin-top:4px">Stopped</div></div>'
+'<div class="mm-sec"><div class="mm-row"><div class="mm-btn danger" id="mm-kb" title="Kill all active bosses on the map">Kill All Bosses</div></div></div>';
b.innerHTML=h;
var absSel=b.querySelector('#mm-absb');
BOSSES.forEach(function(bn){var o=document.createElement('option');o.value=bn;o.textContent=bn;absSel.appendChild(o);});
b.querySelector('#mm-abstart').addEventListener('click',function(){
if(S._autoBossTimer){clearInterval(S._autoBossTimer);}
var boss=absSel.value;
var interval=Math.max(1,parseInt(b.querySelector('#mm-absi').value)||10)*1000;
var count=Math.max(1,parseInt(b.querySelector('#mm-absc').value)||1);
var waves=0;
var stEl=b.querySelector('#mm-abst');
stEl.textContent='Spawning '+count+'x '+boss+' every '+(interval/1000)+'s';stEl.style.color='#0f0';
this.className='mm-btn active';
S._autoBossTimer=setInterval(function(){
for(var k=0;k<count;k++)gameAPI.cmd('spawn_boss '+boss);
waves++;stEl.textContent='Wave #'+waves+': '+count+'x '+boss;
},interval);
for(var k=0;k<count;k++)gameAPI.cmd('spawn_boss '+boss);waves++;
flash(this,'#0f0');});
b.querySelector('#mm-abstop').addEventListener('click',function(){
if(S._autoBossTimer){clearInterval(S._autoBossTimer);S._autoBossTimer=null;}
b.querySelector('#mm-abst').textContent='Stopped';b.querySelector('#mm-abst').style.color='#888';
b.querySelector('#mm-abstart').className='mm-btn success';
flash(this,'#f00');});
b.querySelectorAll('[data-bo]').forEach(function(btn){btn.addEventListener('click',function(){gameAPI.cmd('spawn_boss '+this.dataset.bo);flash(this,'#0f0');});});
b.querySelector('#mm-scb').addEventListener('click',function(){var n=b.querySelector('#mm-cb').value.trim();if(n){gameAPI.cmd('spawn_boss '+n);flash(this,'#0f0');}});
b.querySelector('#mm-ms').addEventListener('click',function(){var boss=b.querySelector('#mm-mbs').value,cnt=parseInt(b.querySelector('#mm-mbc').value)||1;for(var k=0;k<cnt;k++)gameAPI.cmd('spawn_boss '+boss);flash(this,'#ff0');});
b.querySelector('#mm-kb').addEventListener('click',function(){gameAPI.cmd('kill_bosses');flash(this,'#f00');});
stopK(b.querySelector('#mm-cb'));}

function rServer(b){
b.innerHTML='<div class="mm-info">Configure your custom game server settings.</div>'
+'<div class="mm-sec"><h3>Joins</h3><div class="mm-row"><div class="mm-btn" id="mm-nj" title="Toggle whether new players can join the game">New Joins: ALLOWED</div></div></div>'
+'<div class="mm-sec"><h3>Teams</h3><div class="mm-row"><div class="mm-btn" id="mm-ft" title="Toggle freeze/unfreeze team assignments">Teams: UNFROZEN</div></div><div class="mm-row"><input class="mm-inp" id="mm-tcv" type="number" min="0" value="2" style="width:80px" title="Number of teams (2-8)" /><div class="mm-btn" id="mm-stc" title="Set the number of teams in the game">Set Team Count</div></div></div>'
+'<div class="mm-sec"><h3>Map</h3><div class="mm-row"><div class="mm-btn" id="mm-msb" title="Use dynamic sandbox map sizing">Dynamic (Sandbox)</div></div><div class="mm-row"><input class="mm-inp" id="mm-mw" type="number" placeholder="W" style="width:80px" title="Map width in units" /><input class="mm-inp" id="mm-mh" type="number" placeholder="H" style="width:80px" title="Map height in units" /><div class="mm-btn" id="mm-mf" title="Set a fixed map size">Set Fixed</div></div>'
+'<div class="mm-grid" style="margin-top:6px"><div class="mm-btn small mps" data-mw="1000" data-mh="1000" title="1000x1000 map">Tiny</div><div class="mm-btn small mps" data-mw="3000" data-mh="3000" title="3000x3000 map">Small</div><div class="mm-btn small mps" data-mw="5000" data-mh="5000" title="5000x5000 map">Medium</div><div class="mm-btn small mps" data-mw="10000" data-mh="10000" title="10000x10000 map">Large</div><div class="mm-btn small mps" data-mw="25000" data-mh="25000" title="25000x25000 map">Huge</div><div class="mm-btn small mps" data-mw="50000" data-mh="50000" title="50000x50000 map">Massive</div></div></div>'
+'<div class="mm-sec"><h3>Shapes</h3><div class="mm-row"><input class="mm-inp" id="mm-shc" type="number" placeholder="-1 default" style="width:120px" title="Max number of shapes on the map (-1 for default)" /><div class="mm-btn" id="mm-ssh" title="Set the maximum number of shapes on the map">Set Count</div></div></div>'
+'<div class="mm-sec"><h3>Leader Arrow</h3><div class="mm-row"><div class="mm-btn" id="mm-la" title="Toggle the arrow pointing to the game leader">Leader Arrow: ON</div></div></div>'
+'<div class="mm-sec"><h3>Level Cap</h3><div class="mm-row"><input class="mm-inp" id="mm-slcv2" type="number" min="1" max="120" value="45" style="width:80px" title="Max level cap (1-120)" /><div class="mm-btn" id="mm-slc2" title="Set the max level cap for all players">Set Level Cap</div></div></div>'
+'<div class="mm-sec"><h3>Map Automation</h3><div class="mm-info">Animate map size changes with presets. Interval controls smoothness, Speed controls animation duration.</div>'
+'<div class="mm-row"><span class="mm-lbl">Pattern:</span><select class="mm-inp" id="mm-mapreset" style="flex:1" title="Choose the map animation pattern"></select></div>'
+'<div id="mm-mapdesc" style="font-size:10px;color:#888;margin:2px 0 4px 0"></div>'
+'<div class="mm-row"><span class="mm-lbl">Easing:</span><select class="mm-inp" id="mm-maease" style="flex:1" title="Choose how the animation speed changes over time"></select></div>'
+'<div id="mm-easedesc" style="font-size:10px;color:#888;margin:2px 0 4px 0"></div>'
+'<canvas id="mm-easecv" width="200" height="60" style="border-radius:4px;margin:2px 0 6px 0;display:block"></canvas>'
+'<div class="mm-row"><span class="mm-lbl">Max W:</span><input class="mm-inp" id="mm-mamxw" type="number" value="10000" style="width:70px" title="Maximum map width" /><span class="mm-lbl">Max H:</span><input class="mm-inp" id="mm-mamxh" type="number" value="10000" style="width:70px" title="Maximum map height" /></div>'
+'<div class="mm-row"><span class="mm-lbl">Min W:</span><input class="mm-inp" id="mm-mamnw" type="number" value="1000" style="width:70px" title="Minimum map width" /><span class="mm-lbl">Min H:</span><input class="mm-inp" id="mm-mamnh" type="number" value="1000" style="width:70px" title="Minimum map height" /></div>'
+'<div class="mm-row"><span class="mm-lbl">Interval (ms):</span><input class="mm-inp" id="mm-maint" type="number" value="100" min="20" style="width:70px" title="Update interval in ms (lower = smoother, higher = choppier)" /><span class="mm-lbl">Speed (ms):</span><input class="mm-inp" id="mm-maspd" type="number" value="5000" min="500" style="width:70px" title="Full cycle duration in ms" /></div>'
+'<div class="mm-row"><span class="mm-lbl">Delay (ms):</span><input class="mm-inp" id="mm-madly" type="number" value="0" min="0" style="width:70px" title="Pause between cycles (0 = no pause)" /></div>'
+'<div class="mm-row"><div class="mm-btn success" id="mm-mastart" title="Start map size automation">Start</div><div class="mm-btn danger" id="mm-mastop" title="Stop map size automation">Stop</div><div class="mm-btn" id="mm-mareset" title="Reset map to max size">Reset Map</div></div>'
+'<div id="mm-mast" style="font-size:11px;color:#888;margin-top:4px">Stopped</div></div>'
+'<div class="mm-sec"><h3>Danger</h3><div class="mm-row"><div class="mm-btn danger" id="mm-kb2" title="Kill all active bosses on the map">Kill Bosses</div><div class="mm-btn danger" id="mm-ban" title="Block all new players from joining">Block New Joins</div></div></div>';
b.querySelector('#mm-nj').addEventListener('click',function(){S.newJoins=!S.newJoins;gameAPI.cmd('allow_new_joins '+(S.newJoins?'true':'false'));this.textContent='New Joins: '+(S.newJoins?'ALLOWED':'BLOCKED');this.className='mm-btn'+(S.newJoins?' active':' danger');});
b.querySelector('#mm-ft').addEventListener('click',function(){S.teamsFrozen=!S.teamsFrozen;gameAPI.cmd(S.teamsFrozen?'freeze_teams':'unfreeze_teams');this.textContent='Teams: '+(S.teamsFrozen?'FROZEN':'UNFROZEN');this.className='mm-btn'+(S.teamsFrozen?' danger':' active');});
b.querySelector('#mm-stc').addEventListener('click',function(){var v=parseInt(b.querySelector('#mm-tcv').value)||2;gameAPI.cmd('set_team_count '+v);flash(this,'#0f0');});
b.querySelector('#mm-msb').addEventListener('click',function(){gameAPI.cmd('map_sbx_size');flash(this,'#0f0');});
b.querySelector('#mm-mf').addEventListener('click',function(){var w=parseInt(b.querySelector('#mm-mw').value)||5000,hh=parseInt(b.querySelector('#mm-mh').value)||5000;gameAPI.cmd('map_fixed_size '+w+' '+hh);flash(this,'#0f0');});
b.querySelector('#mm-ssh').addEventListener('click',function(){var v=parseInt(b.querySelector('#mm-shc').value);if(isNaN(v))v=-1;gameAPI.cmd('set_shape_max_count '+v);flash(this,'#0f0');});
b.querySelector('#mm-la').addEventListener('click',function(){S.leaderArr=!S.leaderArr;gameAPI.cmd('show_leader_arrow '+(S.leaderArr?'true':'false'));this.textContent='Leader Arrow: '+(S.leaderArr?'ON':'OFF');this.className='mm-btn'+(S.leaderArr?' active':'');});
b.querySelector('#mm-slc2').addEventListener('click',function(){var v=parseInt(b.querySelector('#mm-slcv2').value)||45;gameAPI.cmd('set_max_level '+v);flash(this,'#0f0');});
b.querySelector('#mm-kb2').addEventListener('click',function(){gameAPI.cmd('kill_bosses');flash(this,'#f00');});
b.querySelector('#mm-ban').addEventListener('click',function(){gameAPI.cmd('allow_new_joins false');S.newJoins=false;var nj=b.querySelector('#mm-nj');if(nj){nj.textContent='New Joins: BLOCKED';nj.className='mm-btn danger';}flash(this,'#f00');});
b.querySelectorAll('.mps').forEach(function(btn){btn.addEventListener('click',function(){var w=this.dataset.mw,h=this.dataset.mh;gameAPI.cmd('map_fixed_size '+w+' '+h);flash(this,'#0f0');});});
var mapPresetSel=b.querySelector('#mm-mapreset');
var mapDescEl=b.querySelector('#mm-mapdesc');
var mapEaseSel=b.querySelector('#mm-maease');
var easeDescEl=b.querySelector('#mm-easedesc');
var easeCv=b.querySelector('#mm-easecv');
Object.keys(MAP_AUTO_PRESETS).forEach(function(k){var o=document.createElement('option');o.value=k;o.textContent=MAP_AUTO_PRESETS[k].name;if(k===S.mapAutoPreset)o.selected=true;mapPresetSel.appendChild(o);});
Object.keys(MAP_EASINGS).forEach(function(k){var o=document.createElement('option');o.value=k;o.textContent=MAP_EASINGS[k].name;if(k===S.mapAutoEasing)o.selected=true;mapEaseSel.appendChild(o);});
function updateMapDesc(){var p=MAP_AUTO_PRESETS[mapPresetSel.value];mapDescEl.textContent=p?p.desc:'';}
function updateEaseDesc(){var e=MAP_EASINGS[mapEaseSel.value];easeDescEl.textContent=e?e.desc:'';_drawEasingCurve(easeCv,mapEaseSel.value);}
updateMapDesc();updateEaseDesc();
mapPresetSel.addEventListener('change',function(){S.mapAutoPreset=this.value;updateMapDesc();});
mapEaseSel.addEventListener('change',function(){S.mapAutoEasing=this.value;updateEaseDesc();});
b.querySelector('#mm-mastart').addEventListener('click',function(){
S.mapAutoMaxW=parseInt(b.querySelector('#mm-mamxw').value)||10000;
S.mapAutoMaxH=parseInt(b.querySelector('#mm-mamxh').value)||10000;
S.mapAutoMinW=parseInt(b.querySelector('#mm-mamnw').value)||1000;
S.mapAutoMinH=parseInt(b.querySelector('#mm-mamnh').value)||1000;
S.mapAutoInterval=parseInt(b.querySelector('#mm-maint').value)||100;
S.mapAutoSpeed=parseInt(b.querySelector('#mm-maspd').value)||5000;
S.mapAutoDelay=parseInt(b.querySelector('#mm-madly').value)||0;
S.mapAutoPreset=mapPresetSel.value;
S.mapAutoEasing=mapEaseSel.value;
startMapAuto();
this.className='mm-btn active';b.querySelector('#mm-mastop').className='mm-btn danger';
b.querySelector('#mm-mast').textContent='Running: '+MAP_AUTO_PRESETS[S.mapAutoPreset].name+' ('+MAP_EASINGS[S.mapAutoEasing].name+')';b.querySelector('#mm-mast').style.color='#0f0';
flash(this,'#0f0');});
b.querySelector('#mm-mastop').addEventListener('click',function(){
stopMapAuto();
b.querySelector('#mm-mastart').className='mm-btn success';
b.querySelector('#mm-mast').textContent='Stopped';b.querySelector('#mm-mast').style.color='#888';
flash(this,'#f00');});
b.querySelector('#mm-mareset').addEventListener('click',function(){
stopMapAuto();
gameAPI.cmd('map_fixed_size '+S.mapAutoMaxW+' '+S.mapAutoMaxH);
b.querySelector('#mm-mastart').className='mm-btn success';
b.querySelector('#mm-mast').textContent='Reset to '+S.mapAutoMaxW+'x'+S.mapAutoMaxH;b.querySelector('#mm-mast').style.color='#888';
flash(this,'#0f0');});
if(S.mapAuto){b.querySelector('#mm-mastart').className='mm-btn active';b.querySelector('#mm-mast').textContent='Running: '+(MAP_AUTO_PRESETS[S.mapAutoPreset]||{}).name;b.querySelector('#mm-mast').style.color='#0f0';}
if(!S.newJoins){var nj=b.querySelector('#mm-nj');nj.textContent='New Joins: BLOCKED';nj.className='mm-btn danger';}
if(S.teamsFrozen){var ft=b.querySelector('#mm-ft');ft.textContent='Teams: FROZEN';ft.className='mm-btn danger';}
b.querySelectorAll('.mm-inp').forEach(stopK);}

function rPlayers(b){
b.innerHTML='<div class="mm-info">View and manage all players in the game. Click Refresh to load.</div>'
+'<div class="mm-sec"><h3>Player List</h3><div class="mm-row"><div class="mm-btn" id="mm-rpl" title="Refresh the player list from game data">Refresh</div><div class="mm-btn" id="mm-arpl" title="Automatically refresh player list every 2 seconds">Auto-Refresh: OFF</div><div class="mm-btn warn" id="mm-dbgpl" title="Show debug info about player detection methods">Debug</div></div>'
+'<div id="mm-pldbg" style="display:none;font-size:10px;color:#aaa;background:rgba(0,0,0,0.3);padding:6px;border-radius:4px;margin:4px 0;max-height:120px;overflow-y:auto;font-family:monospace"></div>'
+'<div class="mm-ptbl" id="mm-plist"><div class="ptr phdr"><div class="pcol">ID</div><div class="pcol">Name</div><div class="pcol">Role</div></div><div class="ptr"><div class="pcol"></div><div class="pcol" style="color:#666">Click Refresh</div><div class="pcol"></div></div></div></div>'
+'<div class="mm-sec"><h3>Selected Player</h3><div id="mm-seldisp" style="font-size:12px;color:#888;margin-bottom:6px">No player selected</div>'
+'<div id="mm-pact" style="display:none"><div class="mm-row"><div class="mm-btn small" id="mm-pk" title="Kick selected player from the game">Kick</div><div class="mm-btn small danger" id="mm-pb" title="Ban selected player from the game">Ban</div><div class="mm-btn small success" id="mm-pp" title="Promote selected player to admin">Promote</div><div class="mm-btn small warn" id="mm-pd" title="Demote selected player from admin">Demote</div></div>'
+'<div class="mm-row"><span class="mm-lbl">Set Team:</span><input class="mm-inp" id="mm-pst" type="number" min="0" value="0" style="width:60px" title="Target team number" /><div class="mm-btn small" id="mm-pstb" title="Move the selected player to this team">Set</div></div></div></div>'
+'<div class="mm-sec"><h3>Manual (by ID)</h3><div class="mm-row"><input class="mm-inp" id="mm-mpid" type="number" placeholder="Player ID" style="width:80px" title="Enter player ID number" /><div class="mm-btn small" id="mm-mk" title="Kick player by ID">Kick</div><div class="mm-btn small danger" id="mm-mb" title="Ban player by ID">Ban</div><div class="mm-btn small success" id="mm-mp" title="Promote player by ID to admin">Promote</div><div class="mm-btn small warn" id="mm-md" title="Demote player by ID from admin">Demote</div></div>'
+'<div class="mm-row"><input class="mm-inp" id="mm-mstpid" type="number" placeholder="Player ID" style="width:80px" title="Player ID to move" /><input class="mm-inp" id="mm-mstnum" type="number" placeholder="Team #" style="width:80px" title="Target team number" /><div class="mm-btn small" id="mm-mstb" title="Move a player to the specified team">Set Team</div></div></div>'
+'<div class="mm-sec"><h3>Mass Actions</h3><div class="mm-row">'
+'<div class="mm-btn small danger" id="mm-kickall" title="Kick every player from the game">Kick All</div>'
+'<div class="mm-btn small warn" id="mm-shufflet" title="Randomly reassign all players to different teams">Shuffle Teams</div>'
+'<div class="mm-btn small" id="mm-allteam" title="Move all players to the specified team">All to Team:</div>'
+'<input class="mm-inp" id="mm-alltv" type="number" min="0" value="0" style="width:50px" title="Target team for all players" />'
+'</div><div class="mm-row"><div class="mm-btn small success" id="mm-promall" title="Promote every player to admin">Promote All</div><div class="mm-btn small warn" id="mm-demall" title="Demote every player from admin">Demote All</div></div></div>'
+'<div class="mm-sec"><h3>Manual Infection</h3><div class="mm-info">Manually infect a player (move them to a specific team). Useful when auto-detection can\'t determine the killer.</div>'
+'<div class="mm-row"><input class="mm-inp" id="mm-infpid" type="number" placeholder="Player ID" style="width:80px" title="Player ID to infect" /><span class="mm-lbl">to Team:</span><input class="mm-inp" id="mm-infteam" type="number" min="0" value="0" style="width:60px" title="Target team for infection" /><div class="mm-btn small success" id="mm-infgo" title="Move the player to the specified team (manual infection)">Infect</div></div>'
+'<div class="mm-row"><div class="mm-btn small" id="mm-inf0" title="Move selected player to Team 0 (red)">Infect Sel\u2192T0</div><div class="mm-btn small" id="mm-inf1" title="Move selected player to Team 1 (blue)">Infect Sel\u2192T1</div><div class="mm-btn small" id="mm-inf2" title="Move selected player to Team 2 (purple)">Infect Sel\u2192T2</div><div class="mm-btn small" id="mm-inf3" title="Move selected player to Team 3 (green)">Infect Sel\u2192T3</div></div></div>';
var selId=null,autoRef=null;
function _clearAutoRef(){if(autoRef){clearInterval(autoRef);autoRef=null;}}
var plistEl=b.querySelector('#mm-plist'),selDisp=b.querySelector('#mm-seldisp'),actEl=b.querySelector('#mm-pact');
function renderList(){var players=getPlayerList();
var TEAM_COLORS=['#f14e54','#00b2e1','#bf7ff5','#00e16e','#ff9800','#ff5ebc','#00e5ff','#ffd700'];
var rows='<div class="ptr phdr"><div class="pcol">ID</div><div class="pcol">Name</div><div class="pcol">Team</div><div class="pcol">Role</div></div>';
if(!players||players.length===0){rows+='<div class="ptr"><div class="pcol"></div><div class="pcol" style="color:#666">'+(players===null?'No data yet':'No players')+'</div><div class="pcol"></div><div class="pcol"></div></div>';}
else{players.sort(function(a,b2){var rp={creator:0,admin:1,player:2};return(rp[a.role]||2)-(rp[b2.role]||2)||(a.id-b2.id);});
for(var i=0;i<players.length;i++){var p=players[i];var tm=playerTeams[p.id];var tc=tm!==undefined&&tm<TEAM_COLORS.length?TEAM_COLORS[tm]:'#888';rows+='<div class="ptr'+(p.id===selId?' sel':'')+'" data-pid="'+p.id+'"><div class="pcol">'+p.id+'</div><div class="pcol">'+_ht(p.name||'(unnamed)')+'</div><div class="pcol" style="color:'+tc+'">'+(tm!==undefined?'T'+tm:'?')+'</div><div class="pcol">'+(p.role&&p.role!=='player'?_ht(p.role):'')+'</div></div>';}}
plistEl.innerHTML=rows;
plistEl.querySelectorAll('.ptr:not(.phdr)').forEach(function(row){row.addEventListener('click',function(){selId=parseInt(this.dataset.pid);renderList();var sp=players?players.find(function(p){return p.id===selId;}):null;selDisp.textContent=sp?('Selected: #'+sp.id+' '+(sp.name||'(unnamed)')+' ['+sp.role+']'):('Selected: #'+selId);selDisp.style.color='#4a9eff';actEl.style.display='block';});});}
b.querySelector('#mm-rpl').addEventListener('click',function(){renderList();flash(this,'#0f0');});
b.querySelector('#mm-dbgpl').addEventListener('click',function(){
var dbgEl=b.querySelector('#mm-pldbg');dbgEl.style.display='block';
var lines=[];
lines.push('WebSocket: '+(WS.instance?('connected, url='+WS.instance.url):'NOT connected'));
lines.push('gameAPI ready: '+(gameAPI.ready?'YES':'NO'));
var pl=getPlayerList();lines.push('getPlayerList(): '+(pl?JSON.stringify(pl).substring(0,300):'NULL'));
var csb=getScoreboardFromCanvas();lines.push('Canvas scoreboard: '+(csb?csb.length+' entries':'NULL'));
if(csb&&csb.length>0){for(var j=0;j<Math.min(csb.length,5);j++){lines.push('  '+csb[j].name+': '+csb[j].score);}}
try{var cvars=['player_tank','player_tank_name','game_tank','tank_id','tank_type','ren_player_ids'];
for(var i=0;i<cvars.length;i++){var v=gameAPI.get(cvars[i]);if(v!==null&&v!==undefined)lines.push('convar '+cvars[i]+': '+v);}}catch(e){}
dbgEl.innerHTML=lines.map(function(l){return '<div>'+l.replace(/</g,'&lt;')+'</div>';}).join('');
console.log('[DiepFried] Debug Player List:',lines.join('\n'));
flash(this,'#ff0');});
b.querySelector('#mm-arpl').addEventListener('click',function(){if(autoRef){clearInterval(autoRef);autoRef=null;this.textContent='Auto-Refresh: OFF';this.className='mm-btn';}else{autoRef=setInterval(function(){if(!document.body.contains(plistEl)){clearInterval(autoRef);autoRef=null;return;}renderList();},2000);this.textContent='Auto-Refresh: ON';this.className='mm-btn active';}});
b.querySelector('#mm-pk').addEventListener('click',function(){if(selId!==null){gameAPI.cmd('kick '+selId);flash(this,'#0f0');}});
b.querySelector('#mm-pb').addEventListener('click',function(){if(selId!==null){gameAPI.cmd('ban '+selId);flash(this,'#f00');}});
b.querySelector('#mm-pp').addEventListener('click',function(){if(selId!==null){gameAPI.cmd('promote '+selId);flash(this,'#0f0');}});
b.querySelector('#mm-pd').addEventListener('click',function(){if(selId!==null){gameAPI.cmd('demote '+selId);flash(this,'#ff0');}});
b.querySelector('#mm-pstb').addEventListener('click',function(){if(selId!==null){var t=b.querySelector('#mm-pst').value;gameAPI.cmd('set_team '+selId+' '+t);flash(this,'#0f0');}});
b.querySelector('#mm-mk').addEventListener('click',function(){var id=b.querySelector('#mm-mpid').value.trim();if(id)gameAPI.cmd('kick '+id);});
b.querySelector('#mm-mb').addEventListener('click',function(){var id=b.querySelector('#mm-mpid').value.trim();if(id)gameAPI.cmd('ban '+id);});
b.querySelector('#mm-mp').addEventListener('click',function(){var id=b.querySelector('#mm-mpid').value.trim();if(id)gameAPI.cmd('promote '+id);});
b.querySelector('#mm-md').addEventListener('click',function(){var id=b.querySelector('#mm-mpid').value.trim();if(id)gameAPI.cmd('demote '+id);});
b.querySelector('#mm-mstb').addEventListener('click',function(){var pid=b.querySelector('#mm-mstpid').value.trim(),t=b.querySelector('#mm-mstnum').value.trim();if(pid&&t)gameAPI.cmd('set_team '+pid+' '+t);});
b.querySelector('#mm-kickall').addEventListener('click',function(){var pl=getPlayerList();if(!pl)return;pl.forEach(function(p){if(p.role!=='creator')gameAPI.cmd('kick '+p.id);});flash(this,'#f00');});
b.querySelector('#mm-shufflet').addEventListener('click',function(){var pl=getPlayerList();if(!pl)return;var tc=parseInt(b.querySelector('#mm-alltv').value)||2;pl.forEach(function(p){var t=Math.floor(Math.random()*tc);gameAPI.cmd('set_team '+p.id+' '+t);playerTeams[p.id]=t;});flash(this,'#ff0');});
b.querySelector('#mm-allteam').addEventListener('click',function(){var pl=getPlayerList();if(!pl)return;var t=parseInt(b.querySelector('#mm-alltv').value)||0;pl.forEach(function(p){gameAPI.cmd('set_team '+p.id+' '+t);playerTeams[p.id]=t;});flash(this,'#0f0');});
b.querySelector('#mm-promall').addEventListener('click',function(){var pl=getPlayerList();if(!pl)return;pl.forEach(function(p){if(p.role==='player')gameAPI.cmd('promote '+p.id);});flash(this,'#0f0');});
b.querySelector('#mm-demall').addEventListener('click',function(){var pl=getPlayerList();if(!pl)return;pl.forEach(function(p){if(p.role!=='creator'&&p.role!=='player')gameAPI.cmd('demote '+p.id);});flash(this,'#ff0');});
b.querySelector('#mm-infgo').addEventListener('click',function(){var pid=b.querySelector('#mm-infpid').value.trim(),t=b.querySelector('#mm-infteam').value.trim();if(pid&&t){gameAPI.cmd('set_team '+pid+' '+t);playerTeams[parseInt(pid)]=parseInt(t);S.infKills++;addKillFeedEntry('Manual: #'+pid+' -> T'+t,'#ff9800');flash(this,'#0f0');}});
[0,1,2,3].forEach(function(team){b.querySelector('#mm-inf'+team).addEventListener('click',function(){if(selId!==null){gameAPI.cmd('set_team '+selId+' '+team);playerTeams[selId]=team;S.infKills++;addKillFeedEntry('Manual: #'+selId+' -> T'+team,'#ff9800');flash(this,'#0f0');}});});
b.querySelectorAll('.mm-inp').forEach(stopK);renderList();}

function _applyGameTheme(name){
var t=GAME_THEMES[name];if(!t)return;
S.gameTheme=name;
if(S._rainbowTimer){clearInterval(S._rainbowTimer);S._rainbowTimer=null;}
var keys=Object.keys(_THEME_DEFAULTS);
var applyAll=function(){if(S.gameTheme!==name)return;for(var i=0;i<keys.length;i++){gameAPI.set(keys[i],_getEffectiveThemeVal(name,keys[i]));}};
gameAPI.set('ren_dark_mode',_getEffectiveThemeVal(name,'ren_dark_mode'));
gameAPI.set('ren_solid_background',_getEffectiveThemeVal(name,'ren_solid_background'));
gameAPI.set('ren_background',_getEffectiveThemeVal(name,'ren_background'));
gameAPI.set('ren_disable_grid',_getEffectiveThemeVal(name,'ren_disable_grid'));
applyAll();
requestAnimationFrame(function(){applyAll();requestAnimationFrame(applyAll);});
setTimeout(applyAll,200);
setTimeout(applyAll,500);
try{localStorage.setItem('df_game_theme',name);}catch(e){}
if(typeof _updateVisualsControls==='function')_updateVisualsControls();}
function _updateCrosshair(){
var el=document.getElementById('df-crosshair');if(!el)return;
if(S.crosshairActive){el.style.display='block';el.innerHTML=CROSSHAIR_SHAPES[S.crosshairShape].replace(/COLOR/g,S.crosshairColor);document.body.style.cursor='none';}
else{el.style.display='none';document.body.style.cursor='';}}
function _updateGuides(){
var el=document.getElementById('df-guides');if(!el)return;
if(S.centerGuides){el.style.display='block';el.innerHTML='<div style="position:absolute;top:50%;left:0;width:100%;height:2px;background:rgba(255,0,0,0.5)"></div><div style="position:absolute;top:0;left:50%;width:2px;height:100%;background:rgba(255,0,0,0.5)"></div>';}
else{el.style.display='none';el.innerHTML='';}}

function _updateVisualsControls(){
var b=S._visualsPanel;if(!b)return;var name=S.gameTheme||'Classic';
b.querySelectorAll('.slcv').forEach(function(sl){var v=_getEffectiveThemeVal(name,sl.dataset.cv);sl.value=v;var sp=sl.nextElementSibling;if(sp&&sp.classList.contains('slv'))sp.textContent=v;});
b.querySelectorAll('.mcc').forEach(function(cp){var v=_getEffectiveThemeVal(name,cp.dataset.cv);cp.value='#'+('000000'+v.toString(16)).slice(-6);});
b.querySelectorAll('[data-cv][data-st]').forEach(function(btn){if(btn.dataset.thm==='1'){var v=_getEffectiveThemeVal(name,btn.dataset.cv);var isOn=(v==='true'||v===true);btn.dataset.st=String(isOn);btn.textContent=isOn?'ON':'OFF';btn.className='mm-btn small'+(isOn?' active':'');}});
var ind=b.querySelector('#mm-tcustom');if(ind){var hc=_CUSTOM_THEMES[name]&&Object.keys(_CUSTOM_THEMES[name]).length>0;ind.textContent=hc?'(customized)':'';ind.style.color='#ffb347';}
b.querySelectorAll('.gtbtn').forEach(function(x){var gt=x.dataset.gt;var label=gt+(_CUSTOM_THEMES[gt]?' \u270E':'');x.textContent=label;});
}

function rVisuals(b){
S._visualsPanel=b;
var tn=S.gameTheme||'Classic';
var toggles=[{n:'ren_fps',l:'FPS Counter',d:false},{n:'ren_debug_info',l:'Debug Info',d:false},{n:'ren_dark_mode',l:'Dark Mode',d:false},{n:'ren_solid_background',l:'Solid BG',d:false},{n:'ren_health_bars',l:'Health Bars',d:true},{n:'ren_names',l:'Names',d:true},{n:'ren_scoreboard',l:'Scoreboard',d:true},{n:'ren_ui',l:'UI',d:true},{n:'ren_raw_health_values',l:'Raw HP',d:false},{n:'ren_achievements',l:'Achievements',d:true},{n:'ren_background',l:'Background',d:true},{n:'ren_player_ids',l:'Player IDs',d:false},{n:'ren_latency',l:'Latency',d:false},{n:'ren_upgrades',l:'Upgrades',d:true},{n:'ren_minimap_viewport',l:'Minimap VP',d:false},{n:'ren_xp_bar',l:'XP Bar',d:true}];
var h='<div class="mm-info">Customize the game\'s visual appearance. Changes to sliders/colors are tracked per-theme.</div>'
+'<div class="mm-sec"><h3>Overlays</h3><div class="mm-grid2">'
+'<div class="mm-btn'+(S.crosshairActive?' active':'')+'" id="mm-vcross" title="Toggle a custom crosshair overlay that follows your mouse">Crosshair: '+(S.crosshairActive?'ON':'OFF')+'</div>'
+'<div class="mm-btn'+(S.centerGuides?' active':'')+'" id="mm-vguides" title="Toggle center axis guide lines (red crosshairs at screen center)">Guides: '+(S.centerGuides?'ON':'OFF')+'</div>'
+'<div class="mm-btn'+(S.keepBuild?' active':'')+'" id="mm-vkb" title="Re-apply last used build after auto-respawn">Keep Build: '+(S.keepBuild?'ON':'OFF')+'</div>'
+'</div>'
+'<div id="mm-crossopt" style="display:'+(S.crosshairActive?'block':'none')+';margin-top:6px">'
+'<div class="mm-trow"><span class="mm-lbl">Shape</span><select id="mm-cshape" class="mm-inp" style="flex:1">';
var cnames=Object.keys(CROSSHAIR_SHAPES);for(var ci=0;ci<cnames.length;ci++){h+='<option value="'+cnames[ci]+'"'+(S.crosshairShape===cnames[ci]?' selected':'')+'>'+cnames[ci]+'</option>';}
h+='</select></div><div class="mm-trow"><span class="mm-lbl">Color</span><input type="color" id="mm-ccol" value="'+S.crosshairColor+'" /></div></div></div>'
+'<div class="mm-sec"><h3>In-Game Themes <span style="font-size:10px;color:#888">('+Object.keys(GAME_THEMES).length+')</span> <span id="mm-tcustom" style="font-size:10px"></span></h3>'
+'<div class="mm-row"><input class="mm-inp" id="mm-tsrch" placeholder="Search themes..." style="flex:1" /></div>'
+'<div id="mm-tlist" style="max-height:200px;overflow-y:auto;margin-top:6px">';
var tnames=Object.keys(GAME_THEMES);for(var ti=0;ti<tnames.length;ti++){var bgH='#'+('000000'+_getThemeVal(tnames[ti],'ren_background_color').toString(16)).slice(-6);var grH='#'+('000000'+_getThemeVal(tnames[ti],'ren_grid_color').toString(16)).slice(-6);h+='<div class="mm-btn small gtbtn'+(S.gameTheme===tnames[ti]?' active':'')+'" data-gt="'+tnames[ti]+'" style="margin:2px 0;border-left:4px solid '+grH+'" title="BG: '+bgH+' Grid: '+grH+'">'+tnames[ti]+(_CUSTOM_THEMES[tnames[ti]]?' \u270E':'')+'</div>';}
h+='</div>'
+'<div style="margin-top:6px;display:flex;gap:4px">'
+'<div class="mm-btn warn" id="mm-trst" style="flex:1" title="Reset current theme to its original hardcoded values (discard all customizations)">Reset Theme</div>'
+'<div class="mm-btn success" id="mm-tsave" style="flex:1" title="Save all theme customizations to browser storage (persists across reloads)">Save Themes</div>'
+'</div>'
+'<div class="mm-btn" id="mm-texp" style="margin-top:4px" title="Export all 16 theme definitions (with your customizations) to a .js file for hardcoding">Export All Themes</div>'
+'</div>';
h+='<div class="mm-sec"><h3>Toggles</h3>';
for(var i=0;i<toggles.length;i++){var tIsThemeProp=_THEME_DEFAULTS[toggles[i].n]!==undefined;var tVal=tIsThemeProp?_getEffectiveThemeVal(tn,toggles[i].n):toggles[i].d;var tIsOn=(tVal==='true'||tVal===true);
h+='<div class="mm-trow"><span class="mm-lbl">'+toggles[i].l+'</span><div class="mm-btn small'+(tIsOn?' active':'')+'" data-cv="'+toggles[i].n+'" data-st="'+tIsOn+'" data-thm="'+(tIsThemeProp?'1':'0')+'" title="Toggle '+toggles[i].l+' ('+toggles[i].n+')">'+(tIsOn?'ON':'OFF')+'</div>'+(tIsThemeProp?'<div class="mm-btn small tgrst" data-cv="'+toggles[i].n+'" style="margin-left:4px;padding:2px 6px;font-size:10px" title="Reset to theme default">\u21BA</div>':'')+'</div>';}
h+='</div>';
var sliders=[{n:'ren_grid_base_alpha',l:'Grid Alpha',min:0,max:1,s:0.05},{n:'ren_stroke_soft_color_intensity',l:'Stroke Int.',min:0,max:1,s:0.05},{n:'ren_ui_scale',l:'UI Scale',min:0.5,max:3,s:0.1},{n:'ren_border_color_alpha',l:'Border Alpha',min:0,max:1,s:0.05},{n:'ren_shadow_alpha',l:'Shadow Alpha',min:0,max:1,s:0.05},{n:'ren_shadow_blur',l:'Shadow Blur',min:0,max:50,s:0.5},{n:'ren_shadow_x',l:'Shadow X',min:-20,max:20,s:1},{n:'ren_shadow_y',l:'Shadow Y',min:-20,max:20,s:1},{n:'ren_name_outline_blend',l:'Name Outline',min:0,max:1,s:0.05}];
h+='<div class="mm-sec"><h3>Sliders</h3>';
for(var s=0;s<sliders.length;s++){var sv=_getEffectiveThemeVal(tn,sliders[s].n);h+='<div class="mm-trow"><span class="mm-lbl">'+sliders[s].l+'</span><input type="range" class="slcv" data-cv="'+sliders[s].n+'" min="'+sliders[s].min+'" max="'+sliders[s].max+'" step="'+sliders[s].s+'" value="'+sv+'" style="flex:1;margin:0 8px" title="'+sliders[s].l+' ('+sliders[s].n+'): range '+sliders[s].min+'-'+sliders[s].max+'" /><span class="slv" style="font-size:11px;color:#4a9eff;width:40px;text-align:right">'+sv+'</span><div class="mm-btn small slrst" data-cv="'+sliders[s].n+'" style="margin-left:4px;padding:2px 6px;font-size:10px" title="Reset to theme default">\u21BA</div></div>';}
h+='</div>';
var colors=[{n:'ren_background_color',l:'Background'},{n:'ren_grid_color',l:'Grid'},{n:'ren_border_color',l:'Border'},{n:'ren_stroke_solid_color',l:'Stroke'},{n:'ren_health_fill_color',l:'Health Fill'},{n:'ren_health_background_color',l:'Health BG'},{n:'ren_score_bar_fill_color',l:'Score Bar'},{n:'ren_xp_bar_fill_color',l:'XP Bar'},{n:'ren_minimap_background_color',l:'Minimap BG'},{n:'ren_minimap_border_color',l:'Minimap Border'},{n:'ren_shadow_color',l:'Shadow'},{n:'ren_bar_background_color',l:'Bar BG'}];
h+='<div class="mm-sec"><h3>Colors</h3>';
for(var c=0;c<colors.length;c++){var cv=_getEffectiveThemeVal(tn,colors[c].n);var hex='#'+('000000'+cv.toString(16)).slice(-6);h+='<div class="mm-trow"><span class="mm-lbl">'+colors[c].l+'</span><input type="color" class="mcc" data-cv="'+colors[c].n+'" value="'+hex+'" title="'+colors[c].l+' color ('+colors[c].n+')" /><div class="mm-btn small mcrst" data-cv="'+colors[c].n+'" style="margin-left:4px;padding:2px 6px;font-size:10px" title="Reset to theme default">\u21BA</div></div>';}
h+='</div><div class="mm-sec"><h3>Quick Presets</h3><div class="mm-grid2">'
+'<div class="mm-btn" id="mm-vpnm" title="Dark background + green grid + low alpha night theme">Night Mode</div><div class="mm-btn" id="mm-vpng" title="Hide the grid overlay completely">No Grid</div>'
+'<div class="mm-btn" id="mm-vpsh" title="Enable drop shadows on all entities">Shadows ON</div><div class="mm-btn" id="mm-vprst" title="Reset all visual settings to default values">Reset All</div></div></div>'
+'<div class="mm-sec"><h3>Net Colors (Entity Colors)</h3><div class="mm-info">Replace in-game entity colors by ID. Common IDs: 0=Team1(Red), 1=Team2(Blue), 2=Team3(Purple), 3=Team4(Green), 4=Squares, 5=Triangles, 6=Pentagons, 7=Crashers, 8=Neutral, 9=Fallen/Summoner, 10=Maze, 11=Scoreboard, 12=Barrel, 13=Smasher, 14=Dominator, 15=ArenaCloser, 17=Necro Squares</div>'
+'<div class="mm-row"><input class="mm-inp" id="mm-nci" type="number" placeholder="ID" style="width:60px" title="Entity color ID (see list above)" /><input type="color" id="mm-ncv" value="#ff0000" title="Pick a replacement color" /><div class="mm-btn" id="mm-ncs" title="Apply custom color to the specified entity ID (net_replace_color)">Set</div><div class="mm-btn warn" id="mm-ncra" title="Reset all entity colors to defaults (net_replace_colors)">Reset All Colors</div></div>'
+'<div class="mm-grid4" style="margin-top:6px">'
+'<div class="mm-btn small ncp" data-nid="0" style="border-left:3px solid #f14e54" title="Select Team 1 (Red) color ID 0">T1 Red</div>'
+'<div class="mm-btn small ncp" data-nid="1" style="border-left:3px solid #00b2e1" title="Select Team 2 (Blue) color ID 1">T2 Blue</div>'
+'<div class="mm-btn small ncp" data-nid="2" style="border-left:3px solid #bf7ff5" title="Select Team 3 (Purple) color ID 2">T3 Purple</div>'
+'<div class="mm-btn small ncp" data-nid="3" style="border-left:3px solid #00e16e" title="Select Team 4 (Green) color ID 3">T4 Green</div>'
+'<div class="mm-btn small ncp" data-nid="4" style="border-left:3px solid #ffe869" title="Select Squares color ID 4">Squares</div>'
+'<div class="mm-btn small ncp" data-nid="5" style="border-left:3px solid #fc7677" title="Select Triangles color ID 5">Triangles</div>'
+'<div class="mm-btn small ncp" data-nid="6" style="border-left:3px solid #768dfc" title="Select Pentagons color ID 6">Pentagons</div>'
+'<div class="mm-btn small ncp" data-nid="7" style="border-left:3px solid #f177dd" title="Select Crashers color ID 7">Crashers</div></div>'
+'<div id="mm-nchist" style="margin-top:6px;font-size:10px;color:#888;max-height:60px;overflow-y:auto"></div></div>';
b.innerHTML=h;
              
b.querySelector('#mm-vcross').addEventListener('click',function(){S.crosshairActive=!S.crosshairActive;this.textContent='Crosshair: '+(S.crosshairActive?'ON':'OFF');this.className='mm-btn'+(S.crosshairActive?' active':'');var opt=b.querySelector('#mm-crossopt');if(opt)opt.style.display=S.crosshairActive?'block':'none';_updateCrosshair();flash(this,S.crosshairActive?'#0f0':'#f00');});
b.querySelector('#mm-vguides').addEventListener('click',function(){S.centerGuides=!S.centerGuides;this.textContent='Guides: '+(S.centerGuides?'ON':'OFF');this.className='mm-btn'+(S.centerGuides?' active':'');_updateGuides();flash(this,S.centerGuides?'#0f0':'#f00');});
b.querySelector('#mm-vkb').addEventListener('click',function(){S.keepBuild=!S.keepBuild;this.textContent='Keep Build: '+(S.keepBuild?'ON':'OFF');this.className='mm-btn'+(S.keepBuild?' active':'');flash(this,S.keepBuild?'#0f0':'#f00');});
var cshape=b.querySelector('#mm-cshape');if(cshape)cshape.addEventListener('change',function(){S.crosshairShape=this.value;_updateCrosshair();});
var ccol=b.querySelector('#mm-ccol');if(ccol)ccol.addEventListener('input',function(){S.crosshairColor=this.value;_updateCrosshair();});
                   
b.querySelectorAll('.gtbtn').forEach(function(btn){btn.addEventListener('click',function(){_applyGameTheme(this.dataset.gt);b.querySelectorAll('.gtbtn').forEach(function(x){x.className='mm-btn small gtbtn';});this.className='mm-btn small gtbtn active';flash(this,'#0f0');});});
var tsrch=b.querySelector('#mm-tsrch');if(tsrch){tsrch.addEventListener('input',function(){var q=this.value.toLowerCase();b.querySelectorAll('.gtbtn').forEach(function(btn){btn.style.display=btn.dataset.gt.toLowerCase().indexOf(q)>=0?'':'none';});});}
                                           
b.querySelectorAll('[data-cv][data-st]').forEach(function(btn){btn.addEventListener('click',function(){var cur=this.dataset.st==='true',nxt=!cur;gameAPI.set(this.dataset.cv,String(nxt));this.dataset.st=String(nxt);this.textContent=nxt?'ON':'OFF';this.className='mm-btn small'+(nxt?' active':'');if(this.dataset.thm==='1'&&S.gameTheme){_setCustomThemeVal(S.gameTheme,this.dataset.cv,String(nxt));var ind=b.querySelector('#mm-tcustom');if(ind){ind.textContent='(customized)';ind.style.color='#ffb347';}}});});
                          
b.querySelectorAll('.tgrst').forEach(function(btn){btn.addEventListener('click',function(){var cv=this.dataset.cv;if(S.gameTheme){_resetCustomThemeProp(S.gameTheme,cv);var v=_getHardcodedThemeVal(S.gameTheme,cv);gameAPI.set(cv,v);var tg=b.querySelector('[data-cv="'+cv+'"][data-st]');if(tg){var isOn=(v==='true'||v===true);tg.dataset.st=String(isOn);tg.textContent=isOn?'ON':'OFF';tg.className='mm-btn small'+(isOn?' active':'');}}flash(this,'#0f0');});});
                                       
b.querySelectorAll('.slcv').forEach(function(sl){sl.addEventListener('input',function(){var val=parseFloat(this.value);gameAPI.set(this.dataset.cv,val);this.nextElementSibling.textContent=val;if(S.gameTheme){_setCustomThemeVal(S.gameTheme,this.dataset.cv,val);var ind=b.querySelector('#mm-tcustom');if(ind){ind.textContent='(customized)';ind.style.color='#ffb347';}}});});
                          
b.querySelectorAll('.slrst').forEach(function(btn){btn.addEventListener('click',function(){var cv=this.dataset.cv;if(S.gameTheme){_resetCustomThemeProp(S.gameTheme,cv);var v=_getHardcodedThemeVal(S.gameTheme,cv);gameAPI.set(cv,v);var sl=b.querySelector('.slcv[data-cv="'+cv+'"]');if(sl){sl.value=v;var sp=sl.nextElementSibling;if(sp&&sp.classList.contains('slv'))sp.textContent=v;}}flash(this,'#0f0');});});
                                      
b.querySelectorAll('.mcc').forEach(function(cp){cp.addEventListener('input',function(){var dec=parseInt(this.value.replace('#',''),16);gameAPI.set(this.dataset.cv,dec);if(S.gameTheme){_setCustomThemeVal(S.gameTheme,this.dataset.cv,dec);var ind=b.querySelector('#mm-tcustom');if(ind){ind.textContent='(customized)';ind.style.color='#ffb347';}}});});
                         
b.querySelectorAll('.mcrst').forEach(function(btn){btn.addEventListener('click',function(){var cv=this.dataset.cv;if(S.gameTheme){_resetCustomThemeProp(S.gameTheme,cv);var v=_getHardcodedThemeVal(S.gameTheme,cv);gameAPI.set(cv,v);var cp=b.querySelector('.mcc[data-cv="'+cv+'"]');if(cp)cp.value='#'+('000000'+v.toString(16)).slice(-6);}flash(this,'#0f0');});});
                                                                
b.querySelector('#mm-trst').addEventListener('click',function(){if(S.gameTheme){_resetCustomTheme(S.gameTheme);_saveCustomThemes();_applyGameTheme(S.gameTheme);flash(this,'#0f0');}});
                                                             
b.querySelector('#mm-tsave').addEventListener('click',function(){_saveCustomThemes();flash(this,'#0f0');});
                                                                         
b.querySelector('#mm-texp').addEventListener('click',function(){
var themes=_exportAllThemes();
var lines=['// DiepFried Theme Export — '+new Date().toISOString(),'// Paste this object as GAME_THEMES in the userscript','var GAME_THEMES={'];
var tkeys=Object.keys(themes);
for(var i=0;i<tkeys.length;i++){var tk=tkeys[i],props=themes[tk],pkeys=Object.keys(props);
if(pkeys.length===0){lines.push('"'+tk+'":{}'+((i<tkeys.length-1)?',':''));}
else{var pstrs=[];for(var j=0;j<pkeys.length;j++){var v=props[pkeys[j]];if(typeof v==='number'&&pkeys[j].indexOf('_color')>=0){pstrs.push(pkeys[j]+':0x'+('000000'+v.toString(16).toUpperCase()).slice(-6));}else if(typeof v==='string'){pstrs.push(pkeys[j]+":'"+v+"'");}else{pstrs.push(pkeys[j]+':'+v);}}
lines.push('"'+tk+'":{'+pstrs.join(',')+'}'+((i<tkeys.length-1)?',':''));}}
lines.push('};');
_downloadFile('diepfried-themes-export.js',lines.join('\n'),'text/javascript');
flash(this,'#0f0');});
                
b.querySelector('#mm-ncs').addEventListener('click',function(){var id=b.querySelector('#mm-nci').value.trim(),hex='0x'+b.querySelector('#mm-ncv').value.replace('#','');if(id){gameAPI.cmd('net_replace_color '+id+' '+hex);var hist=b.querySelector('#mm-nchist');if(hist)hist.innerHTML+='<div>Set color '+id+' = '+hex+'</div>';flash(this,'#0f0');}});
b.querySelector('#mm-ncra').addEventListener('click',function(){gameAPI.cmd('net_replace_colors');var hist=b.querySelector('#mm-nchist');if(hist)hist.innerHTML+='<div style="color:#ff0">All colors reset</div>';flash(this,'#ff0');});
b.querySelectorAll('.ncp').forEach(function(btn){btn.addEventListener('click',function(){var nid=this.dataset.nid;b.querySelector('#mm-nci').value=nid;});});
                   
b.querySelector('#mm-vpnm').addEventListener('click',function(){_applyGameTheme('Dark');b.querySelectorAll('.gtbtn').forEach(function(x){x.className='mm-btn small gtbtn';});var db=b.querySelector('[data-gt="Dark"]');if(db)db.className='mm-btn small gtbtn active';flash(this,'#0f0');});
b.querySelector('#mm-vpng').addEventListener('click',function(){gameAPI.set('ren_grid_base_alpha','0');gameAPI.set('ren_disable_grid','true');if(S.gameTheme){_setCustomThemeVal(S.gameTheme,'ren_grid_base_alpha',0);_setCustomThemeVal(S.gameTheme,'ren_disable_grid','true');}_updateVisualsControls();flash(this,'#0f0');});
b.querySelector('#mm-vpsh').addEventListener('click',function(){gameAPI.set('ren_shadows','true');gameAPI.set('ren_shadow_alpha','0.25');gameAPI.set('ren_shadow_blur','15');if(S.gameTheme){_setCustomThemeVal(S.gameTheme,'ren_shadows','true');_setCustomThemeVal(S.gameTheme,'ren_shadow_alpha',0.25);_setCustomThemeVal(S.gameTheme,'ren_shadow_blur',15);}_updateVisualsControls();flash(this,'#0f0');});
b.querySelector('#mm-vprst').addEventListener('click',function(){_applyGameTheme('Classic');b.querySelectorAll('.gtbtn').forEach(function(x){x.className='mm-btn small gtbtn';});var cb=b.querySelector('[data-gt="Classic"]');if(cb)cb.className='mm-btn small gtbtn active';flash(this,'#0f0');});
                                     
var _ind=b.querySelector('#mm-tcustom');if(_ind){var _hc=_CUSTOM_THEMES[tn]&&Object.keys(_CUSTOM_THEMES[tn]).length>0;_ind.textContent=_hc?'(customized)':'';_ind.style.color='#ffb347';}
b.querySelectorAll('.mm-inp').forEach(stopK);}

function rMacros(b){
b.innerHTML='<div class="mm-info">One-click combos, build presets, and custom scripts.</div>'
+'<div class="mm-sec"><h3>Quick Macros</h3><div class="mm-grid2">'
+'<div class="mm-btn success" id="mm-mg" title="God mode + max level 45 + fast leveling + max stats">Godmode Combo</div><div class="mm-btn" id="mm-msbx" title="Fast leveling + tank switch + self destruct + boss control">Full Sandbox</div>'
+'<div class="mm-btn warn" id="mm-mar" title="Spawn 2 of every boss type">Armageddon</div><div class="mm-btn danger" id="mm-mlk" title="Block new joins + freeze teams">Lockdown</div>'
+'<div class="mm-btn success" id="mm-mop" title="Allow joins + unfreeze + fast lvl + tank switch + self destruct">Open Server</div><div class="mm-btn" id="mm-mtn" title="Max lvl 45 + fast leveling + freeze 2 teams">Tournament</div>'
+'<div class="mm-btn warn" id="mm-mbr" title="Spawn 9 bosses: Guardians, Fallen, Summoner, Defender">Boss Rush</div><div class="mm-btn" id="mm-mti" title="Set map to 1000x1000">Tiny Map</div>'
+'<div class="mm-btn" id="mm-mhu" title="Set map to 50000x50000">Huge Map</div><div class="mm-btn danger" id="mm-mch" title="8 teams + all perms + spawn Guardians & Fallen OLs">Chaos</div>'
+'<div class="mm-btn" id="mm-mzm" title="2 teams frozen + god + max lvl + 5x Fallen Overlords">Zombie Siege</div><div class="mm-btn warn" id="mm-mdf" title="2 teams frozen + fast lvl + max lvl + 3x Defender/Guardian/FallenOverlord">Defender</div></div></div>'
+'<div class="mm-sec"><h3>Build Presets</h3><div class="mm-grid2">'
+'<div class="mm-btn" id="mm-bgc" data-bd="00077775" title="0/0/0/7/7/7/7/5 — Penta, Triplet, Spread">Glass</div><div class="mm-btn" id="mm-bsg" data-bd="00057777" title="0/0/0/5/7/7/7/7 — Fighter, Booster">Speed Glass</div>'
+'<div class="mm-btn" id="mm-bbw" data-bd="00027777" title="0/0/0/2/7/7/7/7 — Umbrella builds">Bullet Wall</div><div class="mm-btn" id="mm-brm" data-bd="57700077" title="5/7/7/0/0/0/7/7 — Booster, Annihilator">Rammer</div>'
+'<div class="mm-btn" id="mm-bsmr" data-bd="AAA0000A" title="10/10/10/0/0/0/0/10 — Spike, Landmine">Smasher Ram</div><div class="mm-btn" id="mm-bspd" data-bd="57700070" title="5/7/7/0/0/0/7/0 — Speed Ram">Speed Ram</div>'
+'<div class="mm-btn" id="mm-bol" data-bd="23077707" title="2/3/0/7/7/7/0/7 — 1M Overlord">1M Overlord</div><div class="mm-btn" id="mm-baol" data-bd="02307777" title="0/2/3/0/7/7/7/7 — Anti-Ram Overlord">Anti-Ram OL</div>'
+'<div class="mm-btn" id="mm-bdm" data-bd="00077757" title="0/0/0/7/7/7/5/7 — Factory, Necro drones">Drone Meta</div><div class="mm-btn" id="mm-bfg" data-bd="02307777" title="0/2/3/0/7/7/7/7 — Fighter, Booster">Fighter</div>'
+'<div class="mm-btn" id="mm-bss" data-bd="02307777" title="0/2/3/0/7/7/7/7 — Sea Serpent">Sea Serpent</div><div class="mm-btn" id="mm-bhur" data-bd="02377707" title="0/2/3/7/7/7/0/7 — Octo, Overlord">Hurricane</div>'
+'<div class="mm-btn" id="mm-bpnt" data-bd="00077775" title="0/0/0/7/7/7/7/5 — Penta Shot">Penta</div><div class="mm-btn" id="mm-btpl" data-bd="00077775" title="0/0/0/7/7/7/7/5 — Triplet">Triplet</div>'
+'<div class="mm-btn" id="mm-bsps" data-bd="00067776" title="0/0/0/6/7/7/7/6 — Spread Shot">Spread Shot</div><div class="mm-btn" id="mm-bspr" data-bd="00057777" title="0/0/0/5/7/7/7/7 — Sprayer">Sprayer</div>'
+'<div class="mm-btn" id="mm-bprd" data-bd="00077757" title="0/0/0/7/7/7/5/7 — Predator, Ranger">Predator</div><div class="mm-btn" id="mm-brng" data-bd="00077757" title="0/0/0/7/7/7/5/7 — Ranger, Stalker">Ranger</div>'
+'<div class="mm-btn" id="mm-bfac" data-bd="23057770" title="2/3/0/5/7/7/7/0 — Factory">Factory</div><div class="mm-btn" id="mm-bnc" data-bd="00077757" title="0/0/0/7/7/7/5/7 — Necromancer">Necro</div>'
+'<div class="mm-btn" id="mm-bhyb" data-bd="00077775" title="0/0/0/7/7/7/7/5 — Hybrid">Hybrid</div><div class="mm-btn" id="mm-bann" data-bd="00077775" title="0/0/0/7/7/7/7/5 — Annihilator">Annihilator</div>'
+'<div class="mm-btn" id="mm-bbs" data-bd="00077745" title="0/0/0/7/7/7/4/5 — Battleship">Battleship</div><div class="mm-btn" id="mm-btrp" data-bd="00067770" title="0/0/0/6/7/7/7/0 — Trappers">Trapper</div>'
+'<div class="mm-btn" id="mm-barm" data-bd="06607770" title="0/6/6/0/7/7/7/0 — Armor tank">Armor Tank</div><div class="mm-btn" id="mm-bma" data-bd="77777777" title="7/7/7/7/7/7/7/7 — All max">Max All</div></div></div>'
+'<div class="mm-sec"><h3>Visual Presets</h3><div class="mm-grid2">'
+'<div class="mm-btn" id="mm-vnm" title="Dark background + green grid + low alpha">Night Mode</div><div class="mm-btn" id="mm-vtr" title="Transparent background, no grid">Transparent</div>'
+'<div class="mm-btn" id="mm-vng" title="Hide the grid lines">No Grid</div><div class="mm-btn" id="mm-vrst" title="Reset all visual settings to game defaults">Reset Visuals</div></div></div>'
+'<div class="mm-sec"><h3>Scripts</h3>'
+'<div class="mm-row" style="margin-bottom:6px"><div class="mm-btn" id="mm-sdocs" title="View all scripting API functions and game commands">Docs</div><div class="mm-btn" id="mm-sdoccp" title="Copy full scripting docs as markdown to clipboard">Copy Docs (MD)</div></div>'
+'<div id="mm-scripts-list" style="margin-bottom:6px"></div>'
+'<textarea class="mm-inp" id="mm-scode" rows="6" placeholder="Write scripts here. Raw commands run directly.\nAPI functions: wait(ms), god(), lvl(45), boss(&quot;Guardian&quot;)\n# Comments start with # or //" style="width:100%;resize:vertical;box-sizing:border-box;margin-bottom:6px;font-family:monospace;font-size:11px"></textarea>'
+'<div id="mm-sac" style="display:none;font-size:10px;color:#4a9eff;background:rgba(0,0,0,0.3);border-radius:4px;padding:4px;margin-bottom:4px;max-height:60px;overflow-y:auto"></div>'
+'<div class="mm-row"><input class="mm-inp" id="mm-sname" placeholder="Script name" style="flex:1" />'
+'<div class="mm-btn success" id="mm-ssave" title="Save script to library">Save</div>'
+'<div class="mm-btn" id="mm-srun" title="Execute the script in the editor">Run</div>'
+'<div class="mm-btn danger" id="mm-sstop" title="Stop all running scripts and loops">Stop</div></div>'
+'<div class="mm-row" style="margin-top:4px"><div class="mm-btn" id="mm-sexport" style="font-size:10px" title="Export all saved scripts as JSON">Export All</div>'
+'<div class="mm-btn" id="mm-simport" style="font-size:10px" title="Import scripts from JSON">Import</div></div>'
+'<div id="mm-slog" style="font-size:10px;color:#888;max-height:100px;overflow-y:auto;margin-top:6px;background:rgba(0,0,0,0.2);border-radius:4px;padding:4px;display:none"></div>'
+'<div style="font-size:10px;color:var(--mm-text-dim);margin-top:4px">Click script to run. Right-click to edit. Shift-click to view code.</div></div>';
b.querySelector('#mm-mg').addEventListener('click',function(){if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}setTimeout(function(){gameAPI.cmd('set_invulnerability true');gameAPI.cmd('set_max_level 45');gameAPI.cmd('allow_fast_leveling true');applyBuild('77777777');holdKToLevel(1500);},200);S.god=true;S.fastLvl=true;flash(this,'#0f0');});
b.querySelector('#mm-msbx').addEventListener('click',function(){gameAPI.cmd('allow_fast_leveling true');gameAPI.cmd('allow_tank_switch true');gameAPI.cmd('allow_self_destruct true');gameAPI.cmd('allow_boss_control true');S.fastLvl=true;S.tankSw=true;S.selfDest=true;S.bossCon=true;flash(this,'#0f0');});
b.querySelector('#mm-mar').addEventListener('click',function(){for(var i=0;i<BOSSES.length;i++){gameAPI.cmd('spawn_boss '+BOSSES[i]);gameAPI.cmd('spawn_boss '+BOSSES[i]);}flash(this,'#ff0');});
b.querySelector('#mm-mlk').addEventListener('click',function(){gameAPI.cmd('allow_new_joins false');gameAPI.cmd('freeze_teams');S.newJoins=false;S.teamsFrozen=true;flash(this,'#f00');});
b.querySelector('#mm-mop').addEventListener('click',function(){gameAPI.cmd('allow_new_joins true');gameAPI.cmd('unfreeze_teams');gameAPI.cmd('allow_fast_leveling true');gameAPI.cmd('allow_tank_switch true');gameAPI.cmd('allow_self_destruct true');S.newJoins=true;S.teamsFrozen=false;S.fastLvl=true;S.tankSw=true;S.selfDest=true;flash(this,'#0f0');});
b.querySelector('#mm-mtn').addEventListener('click',function(){gameAPI.cmd('set_max_level 45');gameAPI.cmd('allow_fast_leveling true');gameAPI.cmd('freeze_teams');gameAPI.cmd('set_team_count 2');S.teamsFrozen=true;flash(this,'#0f0');});
b.querySelector('#mm-mbr').addEventListener('click',function(){var rush=['Guardian','Guardian','Guardian','FallenOverlord','FallenOverlord','FallenBooster','FallenBooster','Summoner','Defender'];for(var i=0;i<rush.length;i++)gameAPI.cmd('spawn_boss '+rush[i]);flash(this,'#ff0');});
b.querySelector('#mm-mti').addEventListener('click',function(){gameAPI.cmd('map_fixed_size 1000 1000');flash(this,'#0f0');});
b.querySelector('#mm-mhu').addEventListener('click',function(){gameAPI.cmd('map_fixed_size 50000 50000');flash(this,'#0f0');});
b.querySelector('#mm-mch').addEventListener('click',function(){gameAPI.cmd('set_team_count 8');gameAPI.cmd('allow_fast_leveling true');gameAPI.cmd('allow_tank_switch true');gameAPI.cmd('allow_self_destruct true');gameAPI.cmd('unfreeze_teams');for(var i=0;i<3;i++){gameAPI.cmd('spawn_boss Guardian');gameAPI.cmd('spawn_boss FallenOverlord');}flash(this,'#f00');});
b.querySelector('#mm-mzm').addEventListener('click',function(){gameAPI.cmd('set_team_count 2');gameAPI.cmd('freeze_teams');gameAPI.cmd('allow_fast_leveling true');if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}setTimeout(function(){gameAPI.cmd('set_invulnerability true');},200);S.god=true;gameAPI.cmd('set_max_level 45');for(var i=0;i<5;i++)gameAPI.cmd('spawn_boss FallenOverlord');flash(this,'#0f0');});
b.querySelector('#mm-mdf').addEventListener('click',function(){gameAPI.cmd('set_team_count 2');gameAPI.cmd('freeze_teams');gameAPI.cmd('allow_fast_leveling true');gameAPI.cmd('set_max_level 45');for(var i=0;i<3;i++){gameAPI.cmd('spawn_boss Defender');gameAPI.cmd('spawn_boss Guardian');gameAPI.cmd('spawn_boss FallenOverlord');}flash(this,'#ff0');});
b.querySelectorAll('[data-bd]').forEach(function(btn){btn.addEventListener('click',function(){var bd=this.dataset.bd;gameAPI.cmd('set_max_level 45');gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;applyBuild(bd);holdKToLevel(1500);flash(btn,'#0f0');});});
b.querySelector('#mm-vnm').addEventListener('click',function(){_applyGameTheme('Dark');flash(this,'#0f0');});
b.querySelector('#mm-vtr').addEventListener('click',function(){gameAPI.set('ren_background','false');gameAPI.set('ren_grid_base_alpha','0');gameAPI.set('ren_solid_background','false');flash(this,'#0f0');});
b.querySelector('#mm-vng').addEventListener('click',function(){gameAPI.set('ren_grid_base_alpha','0');flash(this,'#0f0');});
b.querySelector('#mm-vrst').addEventListener('click',function(){_applyGameTheme('Classic');flash(this,'#0f0');});
var sLogEl=b.querySelector('#mm-slog');_scriptLog=sLogEl;
var sCodeEl=b.querySelector('#mm-scode');var sAcEl=b.querySelector('#mm-sac');
b.querySelector('#mm-srun').addEventListener('click',function(){var code=sCodeEl.value.trim();if(!code){flash(this,'#f00');return;}sLogEl.style.display='block';sLogEl.innerHTML='';_runScript(code,function(msg,c){sLogEl.innerHTML+='<div style="color:'+(c||'#aaa')+'">'+msg+'</div>';sLogEl.scrollTop=sLogEl.scrollHeight;});flash(this,'#0f0');});
b.querySelector('#mm-sstop').addEventListener('click',function(){_stopAllLoops();_scriptRunning=false;sLogEl.innerHTML+='<div style="color:#f00">Stopped</div>';flash(this,'#f00');});
sCodeEl.addEventListener('input',function(){var lines=this.value.split('\n');var curLine=lines[lines.length-1]||'';var lastWord=curLine.trim().split(/[\s(]+/).pop()||'';if(lastWord){var tmp={value:lastWord,trim:function(){return lastWord;},focus:function(){}};_acHTML(tmp,sAcEl,_allAcNames);if(sAcEl.style.display==='block'){sAcEl.querySelectorAll('.ac-item').forEach(function(el){el.addEventListener('click',function(){var t=this.textContent.trim().split(' ')[0];var ls=sCodeEl.value.split('\n');var last=ls[ls.length-1];var idx=last.lastIndexOf(lastWord);if(idx>=0)ls[ls.length-1]=last.substring(0,idx)+t+' ';sCodeEl.value=ls.join('\n');sCodeEl.focus();sAcEl.style.display='none';});});}
}else{sAcEl.style.display='none';}});
b.querySelector('#mm-sdocs').addEventListener('click',function(){var md=_generateDocsMD();var html='<div style="font-size:11px;max-height:400px;overflow-y:auto;padding:8px;background:rgba(0,0,0,0.3);border-radius:6px;line-height:1.5">';
html+='<h4 style="color:#4a9eff;margin:0 0 8px">Script Functions</h4>';
Object.keys(SCRIPT_API).sort().forEach(function(k){var f=SCRIPT_API[k];html+='<div style="margin:2px 0"><code style="color:#4caf50">'+k+'('+(f.args||'')+')</code> <span style="color:#888"> - '+f.desc+'</span></div>';});
html+='<h4 style="color:#4a9eff;margin:8px 0">Game Commands</h4>';
ALL_CMDS.forEach(function(c){var m=CMD_META[c];html+='<div style="margin:2px 0"><code style="color:#ff0">'+c+'</code>'+(m.args?' <span style="color:#666">'+m.args+'</span>':'')+' <span style="color:#888"> - '+m.desc+'</span></div>';});
html+='</div>';sLogEl.style.display='block';sLogEl.style.maxHeight='400px';sLogEl.innerHTML=html;});
b.querySelector('#mm-sdoccp').addEventListener('click',function(){var md=_generateDocsMD().replace(/\\n/g,'\n');navigator.clipboard.writeText(md).then(function(){flash(b.querySelector('#mm-sdoccp'),'#0f0');}).catch(function(){prompt('Copy:',md);});});
function _esc(s){var d=document.createElement('div');d.textContent=s;return d.innerHTML;}
var _editIdx=-1;
function _renderScripts(){
var container=b.querySelector('#mm-scripts-list');if(!container)return;
var scripts=_getScripts();
if(scripts.length===0){container.innerHTML='<div style="font-size:11px;color:var(--mm-text-dim);padding:4px">No saved scripts yet.</div>';return;}
var h='<div class="mm-grid2">';
for(var i=0;i<scripts.length;i++){var hl=_editIdx===i?' active':'';h+='<div style="display:flex;gap:2px"><div class="mm-btn'+hl+'" data-sidx="'+i+'" style="flex:1;font-size:11px" title="'+_esc(scripts[i].cmds.join('\n'))+'">'+_esc(scripts[i].name)+' <span style="opacity:0.6;font-size:9px">('+scripts[i].cmds.length+' lines)</span></div><div class="mm-btn danger" data-sdel="'+i+'" style="width:24px;font-size:10px;padding:2px" title="Delete this script">&times;</div></div>';}
h+='</div>';container.innerHTML=h;
container.querySelectorAll('[data-sidx]').forEach(function(btn){btn.addEventListener('click',function(e){var idx=parseInt(this.dataset.sidx);var scripts=_getScripts();if(!scripts[idx])return;if(e.shiftKey){alert(scripts[idx].name+':\n\n'+scripts[idx].cmds.join('\n'));return;}sLogEl.style.display='block';sLogEl.innerHTML='';_runScript(scripts[idx].cmds.join('\n'),function(msg,c){sLogEl.innerHTML+='<div style="color:'+(c||'#aaa')+'">'+msg+'</div>';sLogEl.scrollTop=sLogEl.scrollHeight;});flash(this,'#0f0');});btn.addEventListener('contextmenu',function(e){e.preventDefault();var idx=parseInt(this.dataset.sidx);var scripts=_getScripts();if(!scripts[idx])return;_editIdx=idx;b.querySelector('#mm-sname').value=scripts[idx].name;sCodeEl.value=scripts[idx].cmds.join('\n');_renderScripts();});});
container.querySelectorAll('[data-sdel]').forEach(function(btn){btn.addEventListener('click',function(){if(!confirm('Delete this script?'))return;var idx=parseInt(this.dataset.sdel);var scripts=_getScripts();scripts.splice(idx,1);_saveScripts(scripts);if(_editIdx===idx)_editIdx=-1;else if(_editIdx>idx)_editIdx--;_renderScripts();});});}
b.querySelector('#mm-ssave').addEventListener('click',function(){var name=b.querySelector('#mm-sname').value.trim();var code=sCodeEl.value.trim();if(!name){flash(this,'#f00');return;}if(!code){flash(this,'#f00');return;}var scripts=_getScripts();if(_editIdx>=0&&_editIdx<scripts.length){scripts[_editIdx]={name:name,cmds:code.split('\n')};_editIdx=-1;}else{scripts.push({name:name,cmds:code.split('\n')});}_saveScripts(scripts);b.querySelector('#mm-sname').value='';sCodeEl.value='';_renderScripts();flash(this,'#0f0');});
b.querySelector('#mm-sexport').addEventListener('click',function(){var scripts=_getScripts();if(!scripts.length){flash(this,'#f00');return;}var json=JSON.stringify(scripts,null,2);navigator.clipboard.writeText(json).then(function(){flash(b.querySelector('#mm-sexport'),'#0f0');}).catch(function(){prompt('Copy this JSON:',json);});});
b.querySelector('#mm-simport').addEventListener('click',function(){var json=prompt('Paste exported scripts JSON:');if(!json)return;try{var imported=JSON.parse(json);if(!Array.isArray(imported))throw 0;var valid=imported.filter(function(p){return p&&typeof p.name==='string'&&Array.isArray(p.cmds);});if(!valid.length){flash(this,'#f00');return;}var scripts=_getScripts();for(var i=0;i<valid.length;i++)scripts.push(valid[i]);_saveScripts(scripts);_renderScripts();flash(this,'#0f0');}catch(e){flash(this,'#f00');}});
_renderScripts();
stopK(sCodeEl);stopK(b.querySelector('#mm-sname'));}

var _lobbyNameToId={};
var _lobbyNameToIdSource={};
function rLobby(b){
b.innerHTML='<div class="mm-info">Browse, create, join custom games + Infection mode.</div>'
+'<div class="mm-sec"><h3>Browse Custom Games</h3><div class="mm-row"><div class="mm-btn" id="mm-bref" title="Fetch the latest list of custom games from the server">Refresh</div><input class="mm-inp" id="mm-bsrc" placeholder="Search..." style="flex:1" title="Filter games by name, owner, or region" /></div><div id="mm-blist" style="max-height:200px;overflow-y:auto;font-size:11px"></div></div>'
+'<div class="mm-sec"><h3>Create Custom Game</h3>'
+'<div class="mm-row"><span class="mm-lbl">Name:</span><input class="mm-inp" id="mm-cname" placeholder="Game name" style="flex:1" title="Display name for your custom game" /></div>'
+'<div class="mm-row"><span class="mm-lbl">Region:</span><select class="mm-inp" id="mm-creg" title="Server region for the game"><option value="atl">Atlanta</option><option value="fra">Frankfurt</option><option value="sao">S\u00e3o Paulo</option><option value="sgp">Singapore</option><option value="syd">Sydney</option></select></div>'
+'<div class="mm-row"><span class="mm-lbl">Password:</span><input class="mm-inp" id="mm-cpw" placeholder="Optional" style="flex:1" title="Optional password to restrict access" /></div>'
+'<div class="mm-row"><div class="mm-btn success" id="mm-ccg" title="Create a new custom game with the settings above">Create Game</div></div><div id="mm-cst" style="font-size:11px;color:#888;margin-top:6px"></div></div>'
+'<div class="mm-sec"><h3>Join Game</h3><div class="mm-row"><input class="mm-inp" id="mm-jlink" placeholder="diep.io link, game ID, or server hash" style="flex:1" title="Paste a diep.io link, game ID, or server hash to join" /><div class="mm-btn" id="mm-jg" title="Join the specified custom game">Join</div></div></div>'
+'<div class="mm-sec"><h3>Infection Mode</h3><div class="mm-info">When a player is killed, they join the killer\'s team.<br><b>How it works:</b><br>1. Click Start to assign all players to random equal teams<br>2. Kills and deaths are detected automatically from the scoreboard<br>3. When someone dies, they get moved to the killer\'s team<br><b>Tip:</b> Works best when players have unique names.</div>'
+'<div class="mm-row"><span class="mm-lbl">Teams:</span><select class="mm-inp" id="mm-inft" title="Number of starting teams for infection mode"><option value="2">2 Teams</option><option value="3">3 Teams</option><option value="4">4 Teams</option></select></div>'
+'<div class="mm-row"><span class="mm-lbl">Mode:</span><select class="mm-inp" id="mm-infm" title="Detection mode: Auto uses kill heuristics, 2-Team flips on any death, Manual requires manual infection"><option value="auto">Auto (kill counter + heuristic)</option><option value="2t">2-Team Flip (any death switches)</option><option value="manual">Manual Only</option></select></div>'
+'<div class="mm-row"><div class="mm-btn success" id="mm-ifs" title="Start infection mode: assigns teams randomly and begins monitoring kills">Start Infection</div><div class="mm-btn danger" id="mm-ifx" title="Stop infection mode and clear all monitoring">Stop Infection</div></div><div id="mm-ifst" style="font-size:11px;color:#888;margin-top:4px"></div>'
+'<div id="mm-ifdiag" style="font-size:10px;color:#aaa;margin-top:4px;display:none"><div id="mm-iftc" style="margin-bottom:2px"></div></div>'
+'<div id="mm-iflog" style="font-size:10px;color:#888;max-height:120px;overflow-y:auto;margin-top:6px;background:rgba(0,0,0,0.2);border-radius:4px;padding:4px;display:none"></div></div>'
+'<div class="mm-sec"><h3>Highest Score = Admin</h3><div class="mm-info">The player with the highest score gets promoted to admin. When someone overtakes them, the old admin is demoted and the new #1 is promoted.<br>Polls the scoreboard every 2 seconds.</div>'
+'<div class="mm-row"><div class="mm-btn success" id="mm-hsas" title="Start HSA mode: highest scorer gets promoted to admin automatically">Start</div><div class="mm-btn danger" id="mm-hsax" title="Stop HSA mode and clear monitoring">Stop</div></div>'
+'<div id="mm-hsast" style="font-size:11px;color:#888;margin-top:4px"></div>'
+'<div id="mm-hsalog" style="font-size:10px;color:#888;max-height:100px;overflow-y:auto;margin-top:6px;background:rgba(0,0,0,0.2);border-radius:4px;padding:4px;display:none"></div></div>';
var blistEl=b.querySelector('#mm-blist'),bsrcEl=b.querySelector('#mm-bsrc'),allLobbies=[];
function renderBL(f){var filt=(f||'').toLowerCase();
var fl=allLobbies.filter(function(l){return !filt||(l.name||'').toLowerCase().indexOf(filt)>=0||(l.ownerName||'').toLowerCase().indexOf(filt)>=0||(l.region||'').toLowerCase().indexOf(filt)>=0;});
if(fl.length===0){blistEl.innerHTML='<div style="color:#666;padding:6px">No games found.</div>';return;}
var h='';fl.forEach(function(l){h+='<div style="display:flex;justify-content:space-between;align-items:center;padding:4px 6px;border-bottom:1px solid #222"><div><span style="color:#aaa">'+_ht(l.name||'Unnamed')+'</span> <span style="color:#666">by '+_ht(l.ownerName||'?')+'</span></div><div style="display:flex;gap:6px;align-items:center"><span style="color:#888">'+(l.currentPlayers||0)+'/'+(l.maxPlayers||40)+'</span><span style="color:#666">['+_ht(l.region||'')+']</span><div class="mm-btn small" data-jid="'+_ht(l.gameID||l.id||'')+'">Join</div></div></div>';});
blistEl.innerHTML=h;blistEl.querySelectorAll('[data-jid]').forEach(function(btn){btn.addEventListener('click',function(){var gid=this.dataset.jid;if(gid)W.location.href='https://diep.io/#'+gid;});});}
function fetchBL(){blistEl.innerHTML='<div style="color:#ff0;padding:6px">Loading...</div>';
if(typeof GM_xmlhttpRequest==='undefined'){blistEl.innerHTML='<div style="color:#f00;padding:6px">GM_xmlhttpRequest not available</div>';return;}
var token=getAuthToken(),headers={'Accept':'application/json'};if(token)headers['Authorization']='Bearer '+token;
GM_xmlhttpRequest({method:'GET',url:'https://api.diep.io/sandbox/list?page=1&limit=50',headers:headers,
onload:function(r){try{var d=JSON.parse(r.responseText);allLobbies=d.lobbies||d.data||d||[];if(Array.isArray(d))allLobbies=d;renderBL(bsrcEl.value);}catch(e){blistEl.innerHTML='<div style="color:#f00;padding:6px">Parse error: '+e.message+'</div>';}},
onerror:function(){blistEl.innerHTML='<div style="color:#f00;padding:6px">Request failed</div>';}});}
b.querySelector('#mm-bref').addEventListener('click',fetchBL);
bsrcEl.addEventListener('input',function(){renderBL(this.value);});fetchBL();
b.querySelector('#mm-ccg').addEventListener('click',function(){createGame(b.querySelector('#mm-cname').value.trim(),b.querySelector('#mm-creg').value,b.querySelector('#mm-cpw').value,b.querySelector('#mm-cst'));});
b.querySelector('#mm-jg').addEventListener('click',function(){var link=b.querySelector('#mm-jlink').value.trim();if(!link)return;if(link.indexOf('diep.io')>=0)W.location.href=link;else if(link.indexOf('://')>=0)W.location.href=link;else W.location.href='https://diep.io/#'+link;});
b.querySelector('#mm-ifs').addEventListener('click',function(){
if(S.infRunning)return;S.infRunning=true;S.infTeams=parseInt(b.querySelector('#mm-inft').value)||2;S.infMode=b.querySelector('#mm-infm').value||'auto';
var stEl=b.querySelector('#mm-ifst');
var logEl=b.querySelector('#mm-iflog');logEl.style.display='block';logEl.innerHTML='';
var diagEl=b.querySelector('#mm-ifdiag');diagEl.style.display='block';
var tcEl=b.querySelector('#mm-iftc');
function infLog(msg,color){var d=new Date();var ts=d.toLocaleTimeString();logEl.innerHTML+='<div style="color:'+(color||'#aaa')+'">'+ts+' '+msg+'</div>';logEl.scrollTop=logEl.scrollHeight;}
stEl.textContent='Infection ACTIVE ('+S.infTeams+' teams)';stEl.style.color='#0f0';this.className='mm-btn active';
prevPlayers=null;playerScores={};playerTeams={};playerPresence={};_prevPlayerSnapshot=null;
S.infKills=0;
infLog('Infection started with '+S.infTeams+' teams','#0f0');
var nameToId=_lobbyNameToId;
var _joinOrder=[];
var _nameToIdSource=_lobbyNameToIdSource;
infLog('Player tracking active','#0f0');
function updateJoinOrder(names){
names.forEach(function(name){
if(!name||name==='')return;
if(_joinOrder.indexOf(name)<0){
_joinOrder.push(name);
var estimatedId=_joinOrder.length;
if(!nameToId[name]||_nameToIdSource[name]!=='admin'){
nameToId[name]=estimatedId;
_nameToIdSource[name]='joinorder';
infLog('New player: '+name+' → estimated ID '+estimatedId,'#aaa');}}});}
var maxSlots=60;
playerTeams[0]=0;
var ids=[];for(var _i=1;_i<maxSlots;_i++)ids.push(_i);
for(var si=ids.length-1;si>0;si--){var sj=Math.floor(Math.random()*(si+1));var tmp=ids[si];ids[si]=ids[sj];ids[sj]=tmp;}
var assignDelay=50;
ids.forEach(function(pid,idx){
var team=idx%S.infTeams;
setTimeout(function(){if(S.infRunning)gameAPI.cmd('set_team '+pid+' '+team);},assignDelay);
playerTeams[pid]=team;
assignDelay+=30;});
infLog('Assigned IDs 1-'+(maxSlots-1)+' to '+S.infTeams+' teams (admin=T0)','#0f0');
setTimeout(function(){if(S.infRunning){gameAPI.cmd('freeze_teams');infLog('Teams frozen','#ff0');}},assignDelay+500);
function _activeTeamCount(teams,numTeams){
var tc={};for(var t=0;t<numTeams;t++)tc[t]=0;
var activeIds={};
Object.keys(nameToId).forEach(function(name){activeIds[nameToId[name]]=true;});
activeIds[0]=true;
Object.keys(teams).forEach(function(pid){
if(!activeIds[pid])return;
var t=teams[pid];if(tc[t]!==undefined)tc[t]++;});
return tc;}
function _isLastOnTeam(victimId,teams,numTeams){
var vt=teams[victimId];if(vt===undefined)return false;
var tc=_activeTeamCount(teams,numTeams);
return tc[vt]<=1;}
var _prevSBScores={};
var _sbKillQueue=[];
var _sbDeathQueue=[];
var _deathCooldown={};
var infPollCount=0;
var TEAM_COLORS=['#f14e54','#00b2e1','#bf7ff5','#00e16e','#ff9800','#ff5ebc','#00e5ff','#ffd700'];
var baselineKS=getLocalKillScoreFromFiber();
if(baselineKS){_localKillScore.kills=baselineKS.kills;_localKillScore.score=baselineKS.score;}
infLog('Mode: '+S.infMode+' | Kill baseline: '+(baselineKS?baselineKS.kills:'N/A'),'#0ff');
S.infInterval=setInterval(function(){
infPollCount++;
var otherData=getPlayerList();
var fiberNames={};
if(otherData&&otherData.length>0){
otherData.forEach(function(p){
if(p.id!==undefined&&p.name){
fiberNames[p.name]=true;
if(_nameToIdSource[p.name]!=='admin'){
nameToId[p.name]=p.id;
if(!_nameToIdSource[p.name])_nameToIdSource[p.name]='playerlist';}}});}
var hasFiberData=Object.keys(fiberNames).length>0;
var canvasSB=getScoreboardFromCanvas();
if(canvasSB&&canvasSB.length>0){
if(hasFiberData){canvasSB=canvasSB.filter(function(p){return fiberNames[p.name];});}
var sbNames=canvasSB.map(function(p){return p.name;});
updateJoinOrder(sbNames);}
if(infPollCount%5===1){
var tc={};for(var _t=0;_t<S.infTeams;_t++)tc[_t]=0;
Object.keys(playerTeams).forEach(function(pid){var t=playerTeams[pid];if(tc[t]!==undefined)tc[t]++;});
var fiberCount=Object.keys(fiberNames).length;
var sbCount=canvasSB?canvasSB.length:0;
stEl.textContent='Infection ACTIVE | INF:'+(S.infKills||0)+' | Players: '+fiberCount+' (SB: '+sbCount+')';
if(tcEl){var h='';for(var _ti=0;_ti<S.infTeams;_ti++){var c=_ti<TEAM_COLORS.length?TEAM_COLORS[_ti]:'#888';h+='<span style="color:'+c+'">T'+_ti+':'+tc[_ti]+'</span> ';}
h+='| INF:'+(S.infKills||0)+' | Players: '+fiberCount;
if(sbCount>0)h+=' (SB: '+sbCount+')';
tcEl.innerHTML=h;}}
if(S.infMode==='manual')return;
var ks=getLocalKillScoreFromFiber();
if(ks&&ks.kills>_localKillScore.kills){
var killDelta=ks.kills-_localKillScore.kills;
infLog('YOUR KILL! +'+killDelta+' (total: '+ks.kills+')','#f0f');
var myTeam=playerTeams[0]!==undefined?playerTeams[0]:0;
var victimHandled=false;
for(var qi=_sbDeathQueue.length-1;qi>=0&&!victimHandled;qi--){
var dq=_sbDeathQueue[qi];
if(!dq.processed&&Date.now()-dq.time<3000&&dq.victimTeam!==myTeam){
if(_isLastOnTeam(dq.victimId,playerTeams,S.infTeams)){
dq.processed=true;victimHandled=true;
infLog('PROTECTED: '+dq.victimName+' is last on T'+(playerTeams[dq.victimId]||'?')+', not switching','#ff0');
addKillFeedEntry(dq.victimName+' protected (last on team)','#ff0');}
else{
gameAPI.cmd('set_team '+dq.victimId+' '+myTeam);
playerTeams[dq.victimId]=myTeam;
dq.processed=true;S.infKills++;victimHandled=true;
infLog('INFECTED: '+dq.victimName+' → T'+myTeam+' (you killed them)','#0f0');
addKillFeedEntry('You infected '+dq.victimName,'#ff9800');}}}
if(!victimHandled){
S.infKills+=killDelta;
infLog('Kill detected. Victim will be switched when identified on scoreboard.','#ff0');
addKillFeedEntry('You killed '+killDelta+' player(s)','#ff9800');}
_localKillScore.kills=ks.kills;_localKillScore.score=ks.score;}
var ds=getDeathStatsFromFiber();
if(ds&&ds!==_lastDeathStats){
_lastDeathStats=ds;
var killerName=ds.killerUsername||'?';
infLog('YOU DIED! Killed by '+killerName,'#f44');
addKillFeedEntry(killerName+' killed you','#f44336');
var killerId=nameToId[killerName];
if(killerId!==undefined){
var killerTeam=playerTeams[killerId];
if(killerTeam!==undefined&&killerTeam!==playerTeams[0]){
if(_isLastOnTeam(0,playerTeams,S.infTeams)){
infLog('YOU died but protected (last on T'+playerTeams[0]+')','#ff0');
addKillFeedEntry('You protected (last on team)','#ff0');}
else{
gameAPI.cmd('set_team 0 '+killerTeam);
playerTeams[0]=killerTeam;S.infKills++;
infLog('YOU were infected! Joined T'+killerTeam+' ('+killerName+')','#f0f');
addKillFeedEntry('You joined '+killerName+'\'s team','#ff9800');}}}
else if(S.infTeams===2){
if(_isLastOnTeam(0,playerTeams,S.infTeams)){
infLog('YOU died but protected, last on T'+playerTeams[0]+' (2T)','#ff0');
addKillFeedEntry('You protected (last on team)','#ff0');}
else{
var flipTeam=(playerTeams[0]===0)?1:0;
gameAPI.cmd('set_team 0 '+flipTeam);
playerTeams[0]=flipTeam;S.infKills++;
infLog('YOU were infected (2T flip)! Joined T'+flipTeam,'#f0f');
addKillFeedEntry('You switched to T'+flipTeam,'#ff9800');}}}
if(canvasSB&&canvasSB.length>0){
var sbNow={};canvasSB.forEach(function(p){sbNow[p.name]={score:p.score,id:nameToId[p.name]};});
var now=Date.now();
Object.keys(sbNow).forEach(function(name){
var prev=_prevSBScores[name];
if(!prev)return;
var delta=sbNow[name].score-prev.score;
if(delta>2000&&now-prev.time<2000){
var kid=sbNow[name].id;
if(kid!==undefined&&kid!==0){
_sbKillQueue.push({killerName:name,killerId:kid,killerTeam:playerTeams[kid],time:now});
infLog('Kill detected: '+name+' (T'+(playerTeams[kid]||'?')+') score +'+delta,'#f0f');}}});
Object.keys(_deathCooldown).forEach(function(name){
if(sbNow[name]){delete _deathCooldown[name];}});
Object.keys(_prevSBScores).forEach(function(name){
var prev=_prevSBScores[name];
if(!prev||prev.score<50)return;
var cur=sbNow[name];
if(!cur){
if(_deathCooldown[name])return;
_deathCooldown[name]=now;
var vid=nameToId[name];
if(vid!==undefined&&vid!==0){
_sbDeathQueue.push({victimName:name,victimId:vid,victimTeam:playerTeams[vid],time:now,processed:false});
infLog('DISAPPEARED: '+name+' (id:'+vid+', T'+(playerTeams[vid]||'?')+')','#ff0');}
else{
infLog('DISAPPEARED: '+name+' but no ID mapping','#888');}}
else if(cur.score<prev.score*0.10&&prev.score>500){
if(_deathCooldown[name])return;
_deathCooldown[name]=now;
var vid2=nameToId[name];
if(vid2!==undefined&&vid2!==0){
_sbDeathQueue.push({victimName:name,victimId:vid2,victimTeam:playerTeams[vid2],time:now,processed:false});
infLog('DEATH (score reset): '+name+' (id:'+vid2+') '+prev.score+'→'+cur.score,'#ff0');}}});
Object.keys(sbNow).forEach(function(name){_prevSBScores[name]={score:sbNow[name].score,time:now};});
var staleTime=now-15000;
Object.keys(_prevSBScores).forEach(function(name){if(_prevSBScores[name].time<staleTime)delete _prevSBScores[name];});
Object.keys(_deathCooldown).forEach(function(name){if(_deathCooldown[name]<staleTime)delete _deathCooldown[name];});}
var now2=Date.now();
_sbDeathQueue.forEach(function(death){
if(death.processed||now2-death.time>5000)return;
var matchedKiller=null;
for(var ki=_sbKillQueue.length-1;ki>=0;ki--){
var kill=_sbKillQueue[ki];
if(now2-kill.time>5000)continue;
if(Math.abs(kill.time-death.time)>3000)continue;
if(kill.killerTeam!==death.victimTeam){matchedKiller=kill;break;}}
if(!matchedKiller){
if(S.infTeams===2){
var anyKill=false;
for(var ki2=_sbKillQueue.length-1;ki2>=0;ki2--){
if(Math.abs(_sbKillQueue[ki2].time-death.time)<=3000){anyKill=true;matchedKiller=_sbKillQueue[ki2];break;}}
if(!anyKill){
infLog(death.victimName+' disappeared without a matching kill, skipping','#888');
return;}}
else{
infLog(death.victimName+' disappeared without a matching kill, skipping','#888');
return;}}
if(_isLastOnTeam(death.victimId,playerTeams,S.infTeams)){
death.processed=true;
infLog('PROTECTED: '+death.victimName+' is last on T'+(playerTeams[death.victimId]||'?')+', not switching','#ff0');
addKillFeedEntry(death.victimName+' protected (last on team)','#ff0');}
else if(matchedKiller.killerTeam!==death.victimTeam){
gameAPI.cmd('set_team '+death.victimId+' '+matchedKiller.killerTeam);
playerTeams[death.victimId]=matchedKiller.killerTeam;
S.infKills++;death.processed=true;
infLog('INFECTED: '+death.victimName+' → T'+matchedKiller.killerTeam+' (killed by '+matchedKiller.killerName+')','#0f0');
addKillFeedEntry(matchedKiller.killerName+' infected '+death.victimName,'#ff9800');}
else if(S.infTeams===2){
var newTeam=(death.victimTeam===0)?1:0;
gameAPI.cmd('set_team '+death.victimId+' '+newTeam);
playerTeams[death.victimId]=newTeam;
S.infKills++;death.processed=true;
infLog('INFECTED (2T flip): '+death.victimName+' → T'+newTeam,'#0f0');
addKillFeedEntry(death.victimName+' → T'+newTeam,'#ff9800');}});
_sbKillQueue=_sbKillQueue.filter(function(k){return now2-k.time<10000;});
_sbDeathQueue=_sbDeathQueue.filter(function(d){return !d.processed&&now2-d.time<10000;});
},500);});
b.querySelector('#mm-ifx').addEventListener('click',function(){if(!S.infRunning)return;S.infRunning=false;if(S.infInterval){clearInterval(S.infInterval);S.infInterval=null;}
var stEl=b.querySelector('#mm-ifst');stEl.textContent='Infection STOPPED';stEl.style.color='#f00';b.querySelector('#mm-ifs').className='mm-btn success';
prevPlayers=null;playerScores={};playerTeams={};playerPresence={};
var logEl=b.querySelector('#mm-iflog');if(logEl)logEl.innerHTML+='<div style="color:#f00">Infection stopped.</div>';});
if(S.infRunning){b.querySelector('#mm-ifs').className='mm-btn active';b.querySelector('#mm-ifst').textContent='Infection ACTIVE ('+S.infTeams+' teams)';b.querySelector('#mm-ifst').style.color='#0f0';}
var _hsaCurrentAdmin=null;
b.querySelector('#mm-hsas').addEventListener('click',function(){
if(S.hsaRunning)return;S.hsaRunning=true;_hsaCurrentAdmin=null;
var stEl=b.querySelector('#mm-hsast');stEl.textContent='Highest Score = Admin ACTIVE';stEl.style.color='#0f0';
this.className='mm-btn active';
var logEl=b.querySelector('#mm-hsalog');logEl.style.display='block';
function hsaLog(msg,c){logEl.innerHTML+='<div style="color:'+(c||'#aaa')+'">'+new Date().toLocaleTimeString()+' '+msg+'</div>';logEl.scrollTop=logEl.scrollHeight;}
var _hsaJoinOrder=[];
hsaLog('Started. Checking scoreboard every 2s.','#0f0');
var _hsaPollCount=0;
S.hsaInterval=setInterval(function(){
_hsaPollCount++;
var fpl=getPlayerList();
var fiberNames={};
if(fpl&&fpl.length>0){
fpl.forEach(function(p){
if(p.id!==undefined&&p.name){
_lobbyNameToId[p.name]=p.id;
fiberNames[p.name]=true;}});}
var canvasSB=getScoreboardFromCanvas();
if(!canvasSB||canvasSB.length===0){
if(_hsaPollCount<=3)hsaLog('Waiting for scoreboard data...','#888');
return;}
var sb=[];
canvasSB.forEach(function(p){
if(!p.name)return;
var pid=_lobbyNameToId[p.name];
if(pid===undefined){
if(_hsaJoinOrder.indexOf(p.name)<0){_hsaJoinOrder.push(p.name);}
pid=_hsaJoinOrder.indexOf(p.name)+1;
_lobbyNameToId[p.name]=pid;}
sb.push({name:p.name,id:pid,score:p.score});});
if(sb.length===0){return;}
sb.sort(function(a,b){return(b.score||0)-(a.score||0);});
if(_hsaPollCount%5===1){
var sbList=sb.slice(0,5).map(function(p){return p.name+':'+p.score+'(id'+p.id+')';}).join(', ');
hsaLog('Scoreboard['+sb.length+']: '+sbList,'#888');}
var topPlayer=null,topScore=-1;
for(var si=0;si<sb.length;si++){
var p=sb[si];if(!p.name||!p.score||p.score<=0)continue;
if(p.id===0)continue;
topPlayer=p;topScore=p.score;break;}
if(!topPlayer||topScore<=0)return;
var topId=topPlayer.id;
if(topId===undefined||topId===0)return;
if(_hsaCurrentAdmin&&(_hsaCurrentAdmin.id===topId||_hsaCurrentAdmin.name===topPlayer.name))return;
if(_hsaCurrentAdmin){
gameAPI.cmd('demote '+_hsaCurrentAdmin.id);
hsaLog('Demoted: '+_hsaCurrentAdmin.name+' (id:'+_hsaCurrentAdmin.id+')','#f44');
addKillFeedEntry(_hsaCurrentAdmin.name+' demoted','#f44336');}
gameAPI.cmd('promote '+topId);
_hsaCurrentAdmin={id:topId,name:topPlayer.name};
hsaLog('Promoted: '+topPlayer.name+' (id:'+topId+', score:'+topScore+')','#0f0');
addKillFeedEntry(topPlayer.name+' is now admin! ('+topScore+')','#ff9800');
stEl.textContent='HSA: '+topPlayer.name+' ('+topScore+')';
},2000);});
b.querySelector('#mm-hsax').addEventListener('click',function(){
if(!S.hsaRunning)return;S.hsaRunning=false;
if(S.hsaInterval){clearInterval(S.hsaInterval);S.hsaInterval=null;}
var stEl=b.querySelector('#mm-hsast');stEl.textContent='Highest Score = Admin STOPPED';stEl.style.color='#f00';
b.querySelector('#mm-hsas').className='mm-btn success';_hsaCurrentAdmin=null;
var logEl=b.querySelector('#mm-hsalog');if(logEl)logEl.innerHTML+='<div style="color:#f00">Stopped.</div>';});
if(S.hsaRunning){b.querySelector('#mm-hsas').className='mm-btn active';b.querySelector('#mm-hsast').textContent='HSA ACTIVE';}
b.querySelectorAll('.mm-inp').forEach(stopK);}

function rPackets(b){
pktLogActive=true;
var wsStatus=WS.connected?'<span style="color:#0f0">CONNECTED</span> ('+WS.url+')':'<span style="color:#f00">NOT CONNECTED</span>';
b.innerHTML='<div class="mm-info">Network traffic monitor. View all data sent to and from the game server.</div>'
+'<div class="mm-sec"><h3>Connection</h3><div id="mm-wsstatus" style="font-size:11px;margin-bottom:6px">'+wsStatus+'</div>'
+'<div style="font-size:11px;color:#aaa">Total IN: <span id="mm-wsin">'+WS.totalIn+'</span> | Total OUT: <span id="mm-wsout">'+WS.totalOut+'</span></div></div>'
+'<div class="mm-sec"><h3>Opcode Stats</h3><div class="mm-row"><div class="mm-btn small" id="mm-pops" title="Refresh opcode statistics for incoming and outgoing packets">Refresh Stats</div></div>'
+'<div id="mm-opstats" style="font-family:monospace;font-size:10px;max-height:150px;overflow-y:auto;background:rgba(0,0,0,0.3);padding:6px;border-radius:4px"></div></div>'
+'<div class="mm-sec"><h3>Controls</h3><div class="mm-row"><div class="mm-btn" id="mm-pp2" title="Pause/resume the live packet log">Pause</div><div class="mm-btn" id="mm-pc" title="Clear all captured packets and reset counters">Clear</div><div class="mm-btn" id="mm-pe" title="Export captured packets as a JSON file">Export</div><div class="mm-btn" id="mm-peall" title="Export full capture including connection info and opcode stats">Export All</div></div></div>'
+'<div class="mm-sec"><h3>Live Log</h3><div id="mm-plog" style="font-family:monospace;font-size:10px;max-height:250px;overflow-y:auto;background:rgba(0,0,0,0.3);padding:6px;border-radius:4px;word-break:break-all"></div></div>';
var logEl=b.querySelector('#mm-plog'),paused=false;
function fmtPkt(pkt){var hex=Array.from(pkt.data.slice(0,32)).map(function(x){return x.toString(16).padStart(2,'0');}).join(' ');if(pkt.size>32)hex+='...';var c=pkt.dir==='send'?'#4a9eff':'#4caf50';var opLabel=pkt.op!==undefined?' op=0x'+pkt.op.toString(16):'';var ts=new Date(pkt.time).toLocaleTimeString();return '<div style="border-bottom:1px solid #222;padding:2px 0"><span style="color:'+c+'">'+pkt.dir.toUpperCase()+'</span> <span style="color:#666">'+ts+'</span> <span style="color:#ff9800">'+opLabel+'</span> <span style="color:#888">['+pkt.size+'B]</span> '+hex+'</div>';}
logEl.innerHTML=WS.packets.slice(-50).map(fmtPkt).join('');
var _lastPktIdx=WS.packets.length;
var _pktPollTimer=setInterval(function(){
if(paused||document.hidden||!document.body.contains(logEl)){clearInterval(_pktPollTimer);return;}
var pkts=WS.packets;if(pkts.length>_lastPktIdx){
for(var pi=_lastPktIdx;pi<pkts.length;pi++){logEl.innerHTML+=fmtPkt(pkts[pi]);}
while(logEl.children.length>100)logEl.removeChild(logEl.firstChild);
logEl.scrollTop=logEl.scrollHeight;_lastPktIdx=pkts.length;}
var inEl=b.querySelector('#mm-wsin'),outEl=b.querySelector('#mm-wsout');
if(inEl)inEl.textContent=WS.totalIn;if(outEl)outEl.textContent=WS.totalOut;
},500);
function renderOpStats(){
var html='<div style="color:#4a9eff;margin-bottom:4px">--- INCOMING ('+WS.totalIn+' total) ---</div>';
var inOps=Object.keys(WS.inByOp).sort(function(a,b){return WS.inByOp[b].count-WS.inByOp[a].count;});
inOps.forEach(function(op){var info=WS.inByOp[op];var avg=info.sizes.length?Math.round(info.sizes.reduce(function(a,b){return a+b;},0)/info.sizes.length):0;
html+='<div>op 0x'+parseInt(op).toString(16).padStart(2,'0')+': count='+info.count+' avgSize='+avg+'</div>';});
html+='<div style="color:#4caf50;margin:4px 0">--- OUTGOING ('+WS.totalOut+' total) ---</div>';
var outOps=Object.keys(WS.outByOp).sort(function(a,b){return WS.outByOp[b].count-WS.outByOp[a].count;});
outOps.forEach(function(op){var info=WS.outByOp[op];var avg=info.sizes.length?Math.round(info.sizes.reduce(function(a,b){return a+b;},0)/info.sizes.length):0;
html+='<div>op 0x'+parseInt(op).toString(16).padStart(2,'0')+': count='+info.count+' avgSize='+avg+'</div>';});
if(inOps.length===0&&outOps.length===0)html+='<div style="color:#666">No packets captured yet. Join a game to start capturing.</div>';
b.querySelector('#mm-opstats').innerHTML=html;}
renderOpStats();
b.querySelector('#mm-pops').addEventListener('click',renderOpStats);
b.querySelector('#mm-pp2').addEventListener('click',function(){paused=!paused;this.textContent=paused?'Resume':'Pause';this.className='mm-btn'+(paused?' active':'');});
b.querySelector('#mm-pc').addEventListener('click',function(){WS.packets.length=0;if(W.__diepWS){W.__diepWS.inByOp={};W.__diepWS.outByOp={};W.__diepWS.totalIn=0;W.__diepWS.totalOut=0;}logEl.innerHTML='';renderOpStats();});
b.querySelector('#mm-pe').addEventListener('click',function(){var data=WS.packets.map(function(p){return{dir:p.dir,time:new Date(p.time).toISOString(),size:p.size,op:p.op,hex:Array.from(p.data).map(function(x){return x.toString(16).padStart(2,'0');}).join('')};});var blob=new Blob([JSON.stringify(data,null,2)],{type:'application/json'});var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='diep-packets-'+Date.now()+'.json';a.click();});
b.querySelector('#mm-peall').addEventListener('click',function(){var data={connection:{url:WS.url,connected:WS.connected,totalIn:WS.totalIn,totalOut:WS.totalOut},inByOp:WS.inByOp,outByOp:WS.outByOp,packets:WS.packets.map(function(p){return{dir:p.dir,time:new Date(p.time).toISOString(),size:p.size,op:p.op,hex:Array.from(p.data).map(function(x){return x.toString(16).padStart(2,'0');}).join('')};})};var blob=new Blob([JSON.stringify(data,null,2)],{type:'application/json'});var a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='diep-full-capture-'+Date.now()+'.json';a.click();});}

var CMD_META={
allow_boss_control:{args:'<true|false>',desc:'Enable/disable boss control'},
allow_fast_leveling:{args:'<true|false>',desc:'Enable/disable fast leveling'},
allow_new_joins:{args:'<true|false>',desc:'Allow/block new players joining'},
allow_self_destruct:{args:'<true|false>',desc:'Enable/disable self destruct'},
allow_tank_switch:{args:'<true|false>',desc:'Enable/disable tank switching'},
ban:{args:'<playerIdOrName>',desc:'Ban a player from the game'},
con_toggle:{args:'',desc:'Toggle the in-game console'},
demote:{args:'<playerName>',desc:'Demote a player from admin'},
freeze_teams:{args:'',desc:'Freeze current team assignments'},
game_self_destruct:{args:'',desc:'Self destruct your tank'},
game_spawn:{args:'[playerName]',desc:'Spawn/respawn, optionally as name'},
help:{args:'',desc:'Show available commands'},
join_team:{args:'<teamNumber>',desc:'Join a specific team (0-based)'},
kick:{args:'<playerName>',desc:'Kick a player from the game'},
kill_bosses:{args:'',desc:'Kill all active bosses'},
map_fixed_size:{args:'<width> <height>',desc:'Set fixed map size in units'},
map_sbx_size:{args:'',desc:'Use dynamic sandbox map sizing'},
net_replace_color:{args:'<index> <hex>',desc:'Replace a specific color'},
net_replace_colors:{args:'',desc:'Reset all colors to default'},
promote:{args:'<playerName>',desc:'Promote a player to admin'},
set_invulnerability:{args:'<true|false>',desc:'Enable/disable god mode'},
set_level:{args:'<number>',desc:'Set player level directly'},
set_max_level:{args:'<number>',desc:'Set the max level cap'},
set_shape_max_count:{args:'<number>',desc:'Set max shapes on the map'},
set_tank:{args:'<tankId>',desc:'Switch to a specific tank by ID'},
set_team:{args:'<playerIdOrName> <team>',desc:'Move a player to a team'},
set_team_count:{args:'<number>',desc:'Set number of teams (2-8)'},
show_leader_arrow:{args:'<true|false>',desc:'Show/hide leader arrow'},
spawn_boss:{args:'<BossName>',desc:'Spawn a boss (Guardian, FallenOverlord, etc)'},
ui_replace_colors:{args:'',desc:'Replace UI colors'},
unfreeze_teams:{args:'',desc:'Unfreeze team assignments'}
};
var ALL_CMDS=Object.keys(CMD_META).sort();

var _scriptLoops=[];
var _scriptRunning=false;
var _scriptLog=null;
function _slog(msg,color){if(_scriptLog){_scriptLog.innerHTML+='<div style="color:'+(color||'#aaa')+'">'+msg+'</div>';_scriptLog.scrollTop=_scriptLog.scrollHeight;}}
function _stopAllLoops(){for(var i=0;i<_scriptLoops.length;i++)clearInterval(_scriptLoops[i]);_scriptLoops=[];}

var SCRIPT_API={
wait:{args:'ms',desc:'Pause execution for ms milliseconds',ex:'wait(500)'},
log:{args:'msg',desc:'Print message to script output',ex:'log("hello")'},
echo:{args:'msg',desc:'Alias for log',ex:'echo("done")'},
cls:{args:'',desc:'Clear script output',ex:'cls()'},
god:{args:'',desc:'Enable god mode',ex:'god()'},
ungod:{args:'',desc:'Disable god mode',ex:'ungod()'},
lvl:{args:'n',desc:'Set max level to n',ex:'lvl(45)'},
fastlvl:{args:'[on]',desc:'Toggle or set fast leveling',ex:'fastlvl(true)'},
tank:{args:'id',desc:'Switch to tank by ID',ex:'tank(12)'},
tankinfo:{args:'[id]',desc:'Show tank details (tier, FOV, barrels, caps, path)',ex:'tankinfo(12)'},
upgrades:{args:'[id]',desc:'Show what a tank upgrades to',ex:'upgrades(7)'},
colors:{args:'',desc:'Show exact diep.io game colors',ex:'colors()'},
statcalc:{args:'[stat,pts,lvl]',desc:'Calculate stat values from formulas',ex:'statcalc(null,7,45)'},
spawn:{args:'[name]',desc:'Respawn, optionally as name',ex:'spawn("test")'},
boss:{args:'name',desc:'Spawn a boss by name',ex:'boss("Guardian")'},
killboss:{args:'',desc:'Kill all bosses',ex:'killboss()'},
kill:{args:'',desc:'Self destruct (if enabled)',ex:'kill()'},
team:{args:'n',desc:'Join team number n',ex:'team(0)'},
teams:{args:'n',desc:'Set number of teams',ex:'teams(4)'},
freeze:{args:'',desc:'Freeze teams',ex:'freeze()'},
unfreeze:{args:'',desc:'Unfreeze teams',ex:'unfreeze()'},
map:{args:'w, h',desc:'Set map size (width x height)',ex:'map(5000, 5000)'},
build:{args:'str',desc:'Apply stat build string (8 digits)',ex:'build("77777777")'},
maxstats:{args:'',desc:'Max all stats to 7',ex:'maxstats()'},
players:{args:'',desc:'Get player list (returns array)',ex:'players()'},
score:{args:'',desc:'Get your current score',ex:'score()'},
alive:{args:'',desc:'Check if you are alive (returns bool)',ex:'alive()'},
loop:{args:'ms, fn',desc:'Run fn every ms (returns id). Use stop(id) to cancel',ex:'loop(1000, function(){ log("tick") })'},
stop:{args:'[id]',desc:'Stop a loop by id, or stop all if no id',ex:'stop()'},
rand:{args:'min, max',desc:'Random integer between min and max',ex:'rand(1, 10)'},
set:{args:'name, val',desc:'Set a convar value',ex:'set("ren_dark_mode", "true")'},
get:{args:'name',desc:'Get a convar value',ex:'get("ren_dark_mode")'},
cmd:{args:'str',desc:'Execute a raw game command string',ex:'cmd("spawn_boss Guardian")'},
kick:{args:'name',desc:'Kick a player',ex:'kick("player1")'},
ban:{args:'name',desc:'Ban a player',ex:'ban("player1")'},
promote:{args:'name',desc:'Promote player to admin',ex:'promote("player1")'},
demote:{args:'name',desc:'Demote player from admin',ex:'demote("player1")'},
joins:{args:'on',desc:'Allow or block new joins',ex:'joins(false)'},
shapes:{args:'n',desc:'Set max shape count',ex:'shapes(128)'},
dark:{args:'',desc:'Apply Dark theme',ex:'dark()'},
reset:{args:'',desc:'Reset all visuals to Classic theme',ex:'reset()'},
theme:{args:'name',desc:'Apply a theme by name, or list all themes',ex:'theme("Neon")'},
setteam:{args:'pid, team',desc:'Move a player to a specific team',ex:'setteam(1, 0)'},
color:{args:'id, hex',desc:'Replace an entity color by ID',ex:'color(0, "#ff0000")'},
resetcolors:{args:'',desc:'Reset all entity colors to default',ex:'resetcolors()'},
arrow:{args:'on',desc:'Show or hide the leader arrow',ex:'arrow(true)'},
selfdestruct:{args:'[on]',desc:'Toggle self destruct ability',ex:'selfdestruct(true)'},
bossctrl:{args:'[on]',desc:'Toggle boss control ability',ex:'bossctrl(true)'},
tankswitch:{args:'[on]',desc:'Toggle tank switching ability',ex:'tankswitch(true)'},
lockdown:{args:'',desc:'Block new joins and freeze teams',ex:'lockdown()'},
open:{args:'',desc:'Allow joins, unfreeze, enable perms',ex:'open()'},
respawn:{args:'',desc:'Respawn and reapply all settings',ex:'respawn()'},
name:{args:'n',desc:'Spawn with a specific name',ex:'name("MyName")'},
hp:{args:'n',desc:'Set health regen stat (0-7)',ex:'hp(7)'},
maxhp:{args:'n',desc:'Set max health stat (0-7)',ex:'maxhp(7)'},
bdmg:{args:'n',desc:'Set body damage stat (0-7)',ex:'bdmg(7)'},
bspd:{args:'n',desc:'Set bullet speed stat (0-7)',ex:'bspd(7)'},
bpen:{args:'n',desc:'Set bullet penetration stat (0-7)',ex:'bpen(7)'},
bdmg2:{args:'n',desc:'Set bullet damage stat (0-7)',ex:'bdmg2(7)'},
reload:{args:'n',desc:'Set reload stat (0-7)',ex:'reload(7)'},
mspd:{args:'n',desc:'Set movement speed stat (0-7)',ex:'mspd(7)'},
dynmap:{args:'',desc:'Use dynamic sandbox map sizing',ex:'dynmap()'},
time:{args:'',desc:'Get current timestamp string',ex:'time()'},
notify:{args:'msg',desc:'Show a kill feed notification',ex:'notify("Hello!")'},
count:{args:'',desc:'Count online players',ex:'count()'},
scoreboard:{args:'',desc:'Get scoreboard data (returns array)',ex:'scoreboard()'},
repeat:{args:'n, fn',desc:'Run a function n times with await support',ex:'repeat(5, function(){ boss("Guardian"); wait(500) })'},
abs:{args:'',desc:'Set level to 120 with fast leveling',ex:'abs()'},
godcombo:{args:'',desc:'God mode + max level 45 + max stats',ex:'godcombo()'},
sandbox:{args:'',desc:'Enable all sandbox permissions',ex:'sandbox()'},
armageddon:{args:'',desc:'Spawn 2 of every boss type',ex:'armageddon()'}
};

function _buildScriptEnv(logFn){
var env={};
env.wait=function(ms){return new Promise(function(r){setTimeout(r,ms||0);});};
env.log=function(msg){logFn(String(msg),'#aaa');};
env.echo=env.log;
env.cls=function(){if(_scriptLog)_scriptLog.innerHTML='';};
env.god=function(){S.god=true;if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}setTimeout(function(){gameAPI.cmd('set_invulnerability true');},200);logFn('God mode ON','#0f0');};
env.ungod=function(){S.god=false;S.godPromoted=false;gameAPI.cmd('set_invulnerability false');logFn('God mode OFF','#f00');};
env.lvl=function(n){gameAPI.cmd('set_max_level '+(n||45));logFn('Max level set to '+(n||45),'#0f0');};
env.fastlvl=function(on){var v=on===undefined?true:!!on;S.fastLvl=v;gameAPI.cmd('allow_fast_leveling '+(v?'true':'false'));logFn('Fast leveling '+(v?'ON':'OFF'),v?'#0f0':'#f00');};
env.tank=function(id){gameAPI.cmd('allow_tank_switch true');S.tankSw=true;switchToTank(id||0,null);logFn('Tank set to '+(TANKS[id]||id),'#0f0');};
env.tankinfo=function(id){var tid=id!==undefined?id:(function(){var c=getCurrentTankName();return c?c.id:0;})();var nm=TANKS[tid]||'Unknown';var inf=TANK_INFO[tid];if(!inf){logFn('No info for tank #'+tid,'#f00');return;}var caps=getTankCaps(tid);var pth=getTankUpgradePath(tid);var ptStr=pth?pth.map(function(p){return TANKS[p];}).join(' > '):'N/A';logFn(nm+' (ID:'+tid+') | Tier:'+inf.tier+' | FOV:'+inf.fov+'x | Barrels:'+inf.b+(inf.invis?' | Invis':'')+(inf.zoom?' | Zoom':''),'#8cf');logFn('Stat caps: '+caps.join('/'),'#fc8');logFn('Path: '+ptStr,'#aaa');};
env.upgrades=function(id){var tid=id!==undefined?id:(function(){var c=getCurrentTankName();return c?c.id:0;})();var ch=TANK_UPGRADES[tid];if(!ch||ch.length===0){logFn((TANKS[tid]||'Tank #'+tid)+' has no upgrades','#888');return;}logFn((TANKS[tid]||'Tank #'+tid)+' upgrades to: '+ch.map(function(c){return TANKS[c]||'#'+c;}).join(', '),'#8f8');};
env.colors=function(){logFn('Game colors: tank='+DIEP_COLORS.tank+' red='+DIEP_COLORS.teamRed+' blue='+DIEP_COLORS.teamBlue+' green='+DIEP_COLORS.teamGreen+' purple='+DIEP_COLORS.teamPurple,'#8cf');logFn('Shapes: sq='+DIEP_COLORS.square+' tri='+DIEP_COLORS.triangle+' pent='+DIEP_COLORS.pentagon+' crash='+DIEP_COLORS.crasher,'#fc8');};
env.statcalc=function(stat,pts,lvl){pts=pts||7;lvl=lvl||45;var r={};r.hpRegen=STAT_FORMULAS.hpRegen(pts).toFixed(2)+'%/s';r.maxHP=STAT_FORMULAS.maxHP(pts,lvl);r.bodyDmgShape=STAT_FORMULAS.bodyDmgShape(pts);r.bodyDmgTank=STAT_FORMULAS.bodyDmgTank(pts);r.bulletSpd=STAT_FORMULAS.bulletSpd(pts).toFixed(1);r.bulletPen=STAT_FORMULAS.bulletPen(pts);r.bulletDmg=STAT_FORMULAS.bulletDmg(pts);r.maxSpeed=STAT_FORMULAS.tankMaxSpd(pts,lvl).toFixed(2);if(stat&&r[stat]){logFn(stat+'('+pts+'pts, L'+lvl+'): '+r[stat],'#0f0');}else{for(var k in r)logFn(k+': '+r[k],'#aaa');}};
env.spawn=function(name){gameAPI.cmd('game_spawn'+(name?' '+name:''));logFn('Spawned'+(name?' as '+name:''),'#0f0');};
env.boss=function(name){gameAPI.cmd('spawn_boss '+(name||'Guardian'));logFn('Spawned boss: '+(name||'Guardian'),'#ff0');};
env.killboss=function(){gameAPI.cmd('kill_bosses');logFn('Killed all bosses','#f00');};
env.kill=function(){gameAPI.cmd('allow_self_destruct true');S.selfDest=true;gameAPI.cmd('game_self_destruct');logFn('Self destructed','#f00');};
env.team=function(n){gameAPI.cmd('join_team '+(n||0));logFn('Joined team '+(n||0),'#0f0');};
env.teams=function(n){gameAPI.cmd('set_team_count '+(n||2));logFn('Set '+(n||2)+' teams','#0f0');};
env.freeze=function(){gameAPI.cmd('freeze_teams');logFn('Teams frozen','#ff0');};
env.unfreeze=function(){gameAPI.cmd('unfreeze_teams');logFn('Teams unfrozen','#0f0');};
env.map=function(w,h){gameAPI.cmd('map_fixed_size '+(w||5000)+' '+(h||w||5000));logFn('Map size: '+(w||5000)+'x'+(h||w||5000),'#0f0');};
env.build=function(s){var bs=s||'77777777';maxStatsDelayed(bs);logFn('Build applied: '+bs,'#0f0');};
env.maxstats=function(){maxStatsDelayed('77777777');logFn('Max stats applied','#0f0');};
env.players=function(){var pl=getPlayerList();logFn('Players: '+(pl?pl.length:0),'#aaa');return pl||[];};
env.score=function(){var ks=getLocalKillScoreFromFiber();var s=ks?ks.score:0;logFn('Score: '+s,'#aaa');return s;};
env.alive=function(){var ks=getLocalKillScoreFromFiber();var a=ks!==null;logFn('Alive: '+a,'#aaa');return a;};
env.loop=function(ms,fn){var id=setInterval(fn,ms||1000);_scriptLoops.push(id);logFn('Loop started ('+ms+'ms), id='+id,'#888');return id;};
env.stop=function(id){if(id!==undefined){clearInterval(id);_scriptLoops=_scriptLoops.filter(function(x){return x!==id;});logFn('Stopped loop '+id,'#888');}else{_stopAllLoops();logFn('All loops stopped','#888');}};
env.rand=function(min,max){return Math.floor(Math.random()*(max-min+1))+min;};
env.set=function(n,v){gameAPI.set(n,String(v));};
env.get=function(n){return gameAPI.get(n);};
env.cmd=function(s){gameAPI.cmd(s);};
env.kick=function(n){gameAPI.cmd('kick '+n);logFn('Kicked: '+n,'#f00');};
env.ban=function(n){gameAPI.cmd('ban '+n);logFn('Banned: '+n,'#f00');};
env.promote=function(n){gameAPI.cmd('promote '+n);logFn('Promoted: '+n,'#0f0');};
env.demote=function(n){gameAPI.cmd('demote '+n);logFn('Demoted: '+n,'#ff0');};
env.joins=function(on){gameAPI.cmd('allow_new_joins '+(on?'true':'false'));S.newJoins=!!on;logFn('New joins '+(on?'ON':'OFF'),on?'#0f0':'#f00');};
env.shapes=function(n){gameAPI.cmd('set_shape_max_count '+(n||128));logFn('Shapes: '+(n||128),'#0f0');};
env.dark=function(){_applyGameTheme('Dark');logFn('Dark theme applied','#0f0');};
env.reset=function(){_applyGameTheme('Classic');logFn('Visuals reset to Classic','#0f0');};
env.theme=function(name){if(!name){logFn('Themes: '+Object.keys(GAME_THEMES).join(', '),'#4a9eff');return;}if(!GAME_THEMES[name]){logFn('Unknown theme. Available: '+Object.keys(GAME_THEMES).join(', '),'#f00');return;}_applyGameTheme(name);logFn('Applied theme: '+name,'#0f0');};
env.setteam=function(pid,t){gameAPI.cmd('set_team '+pid+' '+t);logFn('Moved player '+pid+' to team '+t,'#0f0');};
env.color=function(id,hex){gameAPI.set('net_replace_color',id+' '+hex);logFn('Color '+id+' set to '+hex,'#0f0');};
env.resetcolors=function(){gameAPI.cmd('net_replace_colors');logFn('Colors reset','#0f0');};
env.arrow=function(on){gameAPI.cmd('show_leader_arrow '+(on?'true':'false'));logFn('Leader arrow '+(on?'ON':'OFF'),on?'#0f0':'#f00');};
env.selfdestruct=function(on){var v=on===undefined?true:!!on;S.selfDest=v;gameAPI.cmd('allow_self_destruct '+(v?'true':'false'));logFn('Self destruct '+(v?'ON':'OFF'),v?'#0f0':'#f00');};
env.bossctrl=function(on){var v=on===undefined?true:!!on;S.bossCon=v;gameAPI.cmd('allow_boss_control '+(v?'true':'false'));logFn('Boss control '+(v?'ON':'OFF'),v?'#0f0':'#f00');};
env.tankswitch=function(on){var v=on===undefined?true:!!on;S.tankSw=v;gameAPI.cmd('allow_tank_switch '+(v?'true':'false'));logFn('Tank switch '+(v?'ON':'OFF'),v?'#0f0':'#f00');};
env.lockdown=function(){gameAPI.cmd('allow_new_joins false');gameAPI.cmd('freeze_teams');S.newJoins=false;logFn('Lockdown: joins blocked, teams frozen','#ff0');};
env.open=function(){gameAPI.cmd('allow_new_joins true');gameAPI.cmd('unfreeze_teams');gameAPI.cmd('allow_tank_switch true');gameAPI.cmd('allow_self_destruct true');S.newJoins=true;S.tankSw=true;S.selfDest=true;logFn('Opened: joins, teams, tank switch, self destruct','#0f0');};
env.respawn=function(){gameAPI.cmd('game_spawn');setTimeout(reapplySettings,500);logFn('Respawned with settings reapplied','#0f0');};
env.name=function(n){gameAPI.cmd('game_spawn '+(n||''));logFn('Spawned as '+(n||'default'),'#0f0');};
function _setStat(idx,val,logFn){var v=Math.max(0,Math.min(7,parseInt(val)||0));var cur=gameAPI.get('game_stats_build')||'00000000';while(cur.length<8)cur+='0';var arr=cur.split('');arr[idx]=String(v);var nb=arr.join('');applyBuild(nb);logFn(STATS[idx]+' set to '+v,'#0f0');}
env.hp=function(n){_setStat(0,n,logFn);};
env.maxhp=function(n){_setStat(1,n,logFn);};
env.bdmg=function(n){_setStat(2,n,logFn);};
env.bspd=function(n){_setStat(3,n,logFn);};
env.bpen=function(n){_setStat(4,n,logFn);};
env.bdmg2=function(n){_setStat(5,n,logFn);};
env.reload=function(n){_setStat(6,n,logFn);};
env.mspd=function(n){_setStat(7,n,logFn);};
env.dynmap=function(){gameAPI.cmd('map_sbx_size');logFn('Dynamic map sizing enabled','#0f0');};
env.time=function(){var t=new Date().toLocaleTimeString();logFn(t,'#aaa');return t;};
env.notify=function(msg){addKillFeedEntry(msg||'','#4a9eff');logFn('Notification sent','#0f0');};
env.count=function(){var pl=getPlayerList();var c=pl?pl.length:0;logFn('Players online: '+c,'#aaa');return c;};
env.scoreboard=function(){var sb=getScoreboardFromCanvas();logFn('Scoreboard: '+(sb?sb.length:0)+' entries','#aaa');return sb||[];};
env.repeat=function(n,fn){var count=parseInt(n)||1;return (async function(){for(var i=0;i<count;i++){var r=fn(i);if(r&&typeof r.then==='function')await r;}})();};
env.abs=function(buildStr){gameAPI.cmd('set_max_level 120');gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;if(buildStr)applyBuild(buildStr);holdKToLevel(12000);logFn('Level 120 with fast leveling','#0f0');};
env.godcombo=function(){S.god=true;if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}setTimeout(function(){gameAPI.cmd('set_invulnerability true');},200);gameAPI.cmd('set_max_level 45');gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;applyBuild('77777777');holdKToLevel(1500);logFn('God + Level 45 + Max Stats','#0f0');};
env.sandbox=function(){gameAPI.cmd('allow_tank_switch true');gameAPI.cmd('allow_self_destruct true');gameAPI.cmd('allow_fast_leveling true');gameAPI.cmd('allow_boss_control true');gameAPI.cmd('allow_new_joins true');S.tankSw=true;S.selfDest=true;S.fastLvl=true;S.bossCon=true;S.newJoins=true;logFn('All sandbox permissions enabled','#0f0');};
env.armageddon=function(){var bosses=['Guardian','FallenOverlord','FallenBooster','Summoner','Defender'];for(var i=0;i<bosses.length;i++){gameAPI.cmd('spawn_boss '+bosses[i]);gameAPI.cmd('spawn_boss '+bosses[i]);}logFn('Spawned 2 of each boss type','#f00');};
return env;}

function _generateDocsMD(){
var md='# DiepFried Scripting API\\n\\n';
md+='## Raw Commands\\n\\nAny line that isn\'t a function call is executed as a raw game command.\\n```\\nset_invulnerability true\\nspawn_boss Guardian\\nset_max_level 45\\n```\\n\\n';
md+='## Game Commands\\n\\n| Command | Args | Description |\\n|---------|------|-------------|\\n';
ALL_CMDS.forEach(function(c){var m=CMD_META[c];md+='| `'+c+'` | `'+m.args+'` | '+m.desc+' |\\n';});
md+='\\n## Script Functions\\n\\n| Function | Args | Description | Example |\\n|----------|------|-------------|---------|\\n';
Object.keys(SCRIPT_API).sort().forEach(function(k){var f=SCRIPT_API[k];md+='| `'+k+'()` | `'+f.args+'` | '+f.desc+' | `'+f.ex+'` |\\n';});
md+='\\n## Notes\\n\\n- Lines starting with `#` or `//` are comments\\n- `wait()` pauses between commands\\n- `loop()` runs repeatedly; `stop()` cancels\\n- Scripts run top-to-bottom; use `wait()` for delays\\n';
return md;}

function _runScript(code,logFn){
_stopAllLoops();
_scriptRunning=true;
var env=_buildScriptEnv(logFn);
var lines=code.split('\n');
var asyncBody='';
for(var i=0;i<lines.length;i++){
var l=lines[i].trim();
if(!l||l.charAt(0)==='#'||l.indexOf('//')===0)continue;
var isApiCall=false;
var apiNames=Object.keys(SCRIPT_API);
for(var j=0;j<apiNames.length;j++){if(l.indexOf(apiNames[j]+'(')===0||l.indexOf('var ')===0||l.indexOf('if(')===0||l.indexOf('if (')===0||l.indexOf('for(')===0||l.indexOf('for (')===0||l.indexOf('while(')===0||l.indexOf('while (')===0||l.indexOf('function ')===0||l.indexOf('{')===0||l.indexOf('}')===0||l.indexOf('return')===0||l.indexOf('else')===0){isApiCall=true;break;}}
if(l.indexOf('await ')===0){asyncBody+=l+';\n';}
else if(isApiCall){
if(l.indexOf('wait(')===0||l.indexOf('repeat(')===0){asyncBody+='await '+l+';\n';}
else{asyncBody+=l+';\n';}}
else{asyncBody+='cmd("'+l.replace(/\\/g,'\\\\').replace(/"/g,'\\"')+'");\n';}}
var fnNames=Object.keys(env);
var fnArgs=fnNames.join(',');
var fnVals=fnNames.map(function(n){return env[n];});
try{
var fn=new Function(fnArgs,'return (async function(){'+asyncBody+'})();');
fn.apply(null,fnVals).then(function(){_scriptRunning=false;logFn('Script finished','#4caf50');}).catch(function(e){_scriptRunning=false;logFn('Error: '+e.message,'#f44336');});
}catch(e){_scriptRunning=false;logFn('Syntax error: '+e.message,'#f44336');}}

function _getScripts(){try{var d=localStorage.getItem('diepFriedScripts');if(!d){var old=localStorage.getItem('diepFriedPresets');if(old){localStorage.setItem('diepFriedScripts',old);return JSON.parse(old);}}return JSON.parse(d||'[]');}catch(e){return [];}}
function _saveScripts(s){try{localStorage.setItem('diepFriedScripts',JSON.stringify(s));}catch(e){}}

function _acHTML(inp,acEl,cmds){
var v=inp.value.trim().toLowerCase();
if(!v){acEl.style.display='none';return;}
var word=v.split(/\s+/)[0];
var matches=cmds.filter(function(c){return c.indexOf(word)>=0;});
if(matches.length>0&&matches.length<=12){
acEl.style.display='block';
acEl.innerHTML=matches.map(function(c){
var meta=CMD_META[c];var api=SCRIPT_API[c];
var tip=meta?(meta.args?c+' '+meta.args:'(no args)')+' - '+meta.desc:(api?(api.args?c+'('+api.args+')':'(no args)')+' - '+api.desc:'');
return '<span style="cursor:pointer;padding:2px 6px;display:inline-block;border-radius:3px;margin:1px" class="ac-item" title="'+tip.replace(/"/g,'&quot;')+'">'+c+(meta&&meta.args?' <span style="opacity:0.5">'+meta.args+'</span>':'')+'</span>';
}).join('');
acEl.querySelectorAll('.ac-item').forEach(function(el){el.addEventListener('click',function(){
var t=this.textContent.trim().split(' ')[0];inp.value=t+' ';inp.focus();acEl.style.display='none';});});
}else{acEl.style.display='none';}}

var _allAcNames=ALL_CMDS.concat(Object.keys(SCRIPT_API)).sort();
function rConsole(b){
b.innerHTML='<div class="mm-info">Run game commands directly. Press Tab to autocomplete. Hover buttons for info.</div>'
+'<div class="mm-sec"><h3>Command</h3><div class="mm-row"><input class="mm-inp" id="mm-ci" placeholder="e.g. help, spawn_boss Guardian" style="flex:1" /><div class="mm-btn success" id="mm-cs" title="Execute the command in the input field">Execute</div></div><div id="mm-ac" style="display:none;font-size:10px;color:#4a9eff;background:rgba(0,0,0,0.3);border-radius:4px;padding:4px;margin-top:4px;max-height:80px;overflow-y:auto"></div><div id="mm-tt" style="display:none;font-size:10px;color:#ff0;background:rgba(0,0,0,0.4);border-radius:4px;padding:4px;margin-top:2px"></div><div style="font-size:10px;color:#666;margin-top:4px">Enter=execute, Up/Down=history, Tab=autocomplete</div></div>'
+'<div class="mm-sec"><h3>Quick</h3><div class="mm-grid">'
+'<div class="mm-btn small" data-qc="help" title="Shows all available commands">help</div>'
+'<div class="mm-btn small" data-qc="set_invulnerability true" title="Enables god mode">God ON</div>'
+'<div class="mm-btn small" data-qc="set_invulnerability false" title="Disables god mode">God OFF</div>'
+'<div class="mm-btn small" data-qc="set_max_level 45" title="Sets max level cap to 45">Lvl 45</div>'
+'<div class="mm-btn small" data-qc="set_max_level 120" title="Sets max level cap to 120">Lvl 120</div>'
+'<div class="mm-btn small" data-qc="allow_fast_leveling true" title="Enables instant leveling">Fast Lvl ON</div>'
+'<div class="mm-btn small" data-qc="allow_fast_leveling false" title="Disables fast leveling">Fast Lvl OFF</div>'
+'<div class="mm-btn small" data-qc="game_spawn" title="Respawns your tank">Respawn</div>'
+'<div class="mm-btn small" data-qc="kill_bosses" title="Kills all active bosses on the map">Kill Bosses</div>'
+'<div class="mm-btn small" data-qc="spawn_boss Guardian" title="Spawns a Guardian">Guardian</div>'
+'<div class="mm-btn small" data-qc="spawn_boss FallenOverlord" title="Spawns a Fallen Overlord">Fallen OL</div>'
+'<div class="mm-btn small" data-qc="spawn_boss FallenBooster" title="Spawns a Fallen Booster">Fallen Boost</div>'
+'<div class="mm-btn small" data-qc="spawn_boss Summoner" title="Spawns a Summoner">Summoner</div>'
+'<div class="mm-btn small" data-qc="spawn_boss Defender" title="Spawns a Defender">Defender</div>'
+'<div class="mm-btn small" data-qc="allow_tank_switch true" title="Allows switching between tanks">Tank Sw ON</div>'
+'<div class="mm-btn small" data-qc="allow_tank_switch false" title="Locks tank switching">Tank Sw OFF</div>'
+'<div class="mm-btn small" data-qc="allow_self_destruct true" title="Enables self destruct ability">Self Dest ON</div>'
+'<div class="mm-btn small" data-qc="allow_self_destruct false" title="Disables self destruct">Self Dest OFF</div>'
+'<div class="mm-btn small" data-qc="allow_boss_control true" title="Allows controlling spawned bosses">Boss Ctrl ON</div>'
+'<div class="mm-btn small" data-qc="allow_boss_control false" title="Disables boss control">Boss Ctrl OFF</div>'
+'<div class="mm-btn small" data-qc="allow_new_joins true" title="Allows new players to join">Joins ON</div>'
+'<div class="mm-btn small" data-qc="allow_new_joins false" title="Blocks new players from joining">Joins OFF</div>'
+'<div class="mm-btn small" data-qc="freeze_teams" title="Locks current team assignments">Freeze Teams</div>'
+'<div class="mm-btn small" data-qc="unfreeze_teams" title="Unlocks team assignments">Unfreeze</div>'
+'<div class="mm-btn small" data-qc="set_team_count 2" title="Sets the game to 2 teams">2 Teams</div>'
+'<div class="mm-btn small" data-qc="set_team_count 3" title="Sets the game to 3 teams">3 Teams</div>'
+'<div class="mm-btn small" data-qc="set_team_count 4" title="Sets the game to 4 teams">4 Teams</div>'
+'<div class="mm-btn small" data-qc="set_team_count 6" title="Sets the game to 6 teams">6 Teams</div>'
+'<div class="mm-btn small" data-qc="map_sbx_size" title="Uses dynamic sandbox map sizing">Dyn Map</div>'
+'<div class="mm-btn small" data-qc="map_fixed_size 1000 1000" title="Tiny 1000x1000 map">Tiny Map</div>'
+'<div class="mm-btn small" data-qc="map_fixed_size 3000 3000" title="Small 3000x3000 map">Small Map</div>'
+'<div class="mm-btn small" data-qc="map_fixed_size 5000 5000" title="Medium 5000x5000 map">Med Map</div>'
+'<div class="mm-btn small" data-qc="map_fixed_size 10000 10000" title="Large 10000x10000 map">Large Map</div>'
+'<div class="mm-btn small" data-qc="map_fixed_size 25000 25000" title="Huge 25000x25000 map">Huge Map</div>'
+'<div class="mm-btn small" data-qc="map_fixed_size 50000 50000" title="Massive 50000x50000 map">Massive Map</div>'
+'<div class="mm-btn small" data-qc="show_leader_arrow true" title="Shows arrow pointing to the leader">Leader Arrow ON</div>'
+'<div class="mm-btn small" data-qc="show_leader_arrow false" title="Hides the leader arrow">Leader Arrow OFF</div>'
+'<div class="mm-btn small" data-qc="set_shape_max_count 128" title="Maximum shapes on the map">Max Shapes</div>'
+'<div class="mm-btn small" data-qc="set_shape_max_count 0" title="Removes all shapes from the map">No Shapes</div>'
+'<div class="mm-btn small" data-qc="set_shape_max_count 32" title="Reduces shapes to 32">Few Shapes</div>'
+'<div class="mm-btn small" data-qc="net_replace_colors" title="Resets all colors to default">Reset Colors</div>'
+'<div class="mm-btn small" data-qc="con_toggle" title="Toggles the in-game developer console">Toggle Console</div>'
+'</div></div>'
+'<div class="mm-sec"><h3>Reference</h3><div style="font-size:10px;color:#999;line-height:1.6;padding:6px;background:rgba(0,0,0,0.2);border-radius:4px"><b>Self:</b> set_invulnerability, game_spawn, join_team<br><b>Level:</b> set_max_level, allow_fast_leveling<br><b>Switch:</b> allow_tank_switch, allow_self_destruct, allow_boss_control<br><b>Bosses:</b> spawn_boss, kill_bosses<br><b>Map:</b> map_fixed_size W H, map_sbx_size, set_shape_max_count<br><b>Teams:</b> set_team_count, join_team, set_team, freeze/unfreeze_teams<br><b>Players:</b> kick, ban, promote, demote, allow_new_joins<br><b>Visual:</b> show_leader_arrow, net_replace_color</div></div>'
+'<div class="mm-sec"><h3>History</h3><div id="mm-ch" style="font-family:monospace;font-size:11px;color:#888;max-height:120px;overflow-y:auto"></div></div>'
+'<div class="mm-sec" style="text-align:center;padding:12px 0"><div style="font-size:13px;font-weight:bold;color:#4a9eff">DiepFried v1</div><div style="font-size:11px;color:#888;margin-top:4px">by disinvoke.</div></div>';
var inp=b.querySelector('#mm-ci'),hist=b.querySelector('#mm-ch'),acEl=b.querySelector('#mm-ac'),ttEl=b.querySelector('#mm-tt');
function upH(){hist.innerHTML=cmdHistory.slice(-20).reverse().map(function(c){return '<div style="padding:2px 0;border-bottom:1px solid #222">&gt; '+_ht(c)+'</div>';}).join('');}upH();
function exec(){var cmd=inp.value.trim();if(!cmd)return;cmdHistory.push(cmd);histIdx=cmdHistory.length;inp.value='';gameAPI.cmd(cmd);upH();acEl.style.display='none';ttEl.style.display='none';}
function showAC(){_acHTML(inp,acEl,ALL_CMDS);var v=inp.value.trim().toLowerCase();var word=v.split(/\s+/)[0];var meta=CMD_META[word];if(meta){ttEl.style.display='block';ttEl.innerHTML='<b>'+word+'</b> '+(meta.args||'(no args)')+' - '+meta.desc;}else{ttEl.style.display='none';}}
inp.addEventListener('input',showAC);
inp.addEventListener('keydown',function(e){_stop.call(e);if(e.key==='Enter')exec();else if(e.key==='Tab'){_prevent.call(e);var v=inp.value.trim().toLowerCase();var matches=ALL_CMDS.filter(function(c){return c.indexOf(v)===0;});if(matches.length===1){inp.value=matches[0]+' ';acEl.style.display='none';showAC();}else if(matches.length>1){var cp=matches[0];for(var i=1;i<matches.length;i++){while(matches[i].indexOf(cp)!==0)cp=cp.slice(0,-1);}if(cp.length>v.length)inp.value=cp;showAC();}}else if(e.key==='ArrowUp'){if(histIdx>0){histIdx--;inp.value=cmdHistory[histIdx]||'';}_prevent.call(e);}else if(e.key==='ArrowDown'){if(histIdx<cmdHistory.length-1){histIdx++;inp.value=cmdHistory[histIdx]||'';}else{histIdx=cmdHistory.length;inp.value='';}_prevent.call(e);}});
inp.addEventListener('keyup',function(e){_stop.call(e);});
b.querySelector('#mm-cs').addEventListener('click',exec);
b.querySelectorAll('[data-qc]').forEach(function(btn){btn.addEventListener('click',function(){cmdHistory.push(this.dataset.qc);gameAPI.cmd(this.dataset.qc);upH();flash(this,'#0f0');});});}

function toggle(){if(!menuEl)createMenu();menuVisible=!menuVisible;menuEl.className='theme-'+_currentTheme+(menuVisible?' visible':'');}
document.addEventListener('keydown',function(e){
if(e.key==='Insert'){_prevent.call(e);_stop.call(e);toggle();return;}
var tag=(e.target.tagName||'').toLowerCase();
if(tag==='input'||tag==='textarea'||tag==='select')return;
if(e.altKey&&e.key==='g'){_prevent.call(e);S.god=!S.god;if(S.god){if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}setTimeout(function(){gameAPI.cmd('set_invulnerability true');},200);}else{S.godPromoted=false;gameAPI.cmd('set_invulnerability false');}}
if(e.altKey&&e.key==='m'){_prevent.call(e);gameAPI.cmd('set_max_level 45');gameAPI.cmd('allow_fast_leveling true');S.fastLvl=true;applyBuild('77777777');holdKToLevel(1500);}
if(e.altKey&&e.key==='t'){_prevent.call(e);S.tankSw=!S.tankSw;gameAPI.cmd('allow_tank_switch '+(S.tankSw?'true':'false'));}
},true);
document.addEventListener('keydown',function(e){if(menuEl&&menuVisible&&menuEl.contains(e.target))_stop.call(e);},true);
document.addEventListener('keyup',function(e){if(menuEl&&menuVisible&&menuEl.contains(e.target))_stop.call(e);},true);
function init(){if(!document.body){setTimeout(init,100);return;}
var ind=document.createElement('div');ind.style.cssText='position:fixed;bottom:10px;right:10px;padding:4px 10px;background:rgba(0,0,0,0.6);color:#4a9eff;font-size:11px;border-radius:4px;z-index:999998;pointer-events:none;font-family:sans-serif';
ind.innerHTML='<img src="'+LOGO_URI+'" width="14" height="14" style="vertical-align:middle;margin-right:4px;border-radius:2px" />DiepFried [Insert]';document.body.appendChild(ind);setTimeout(function(){ind.style.opacity='0.4';},3000);
var crossEl=document.createElement('div');crossEl.id='df-crosshair';crossEl.style.cssText='position:fixed;pointer-events:none;z-index:2147483646;display:none;transform:translate(-50%,-50%)';document.body.appendChild(crossEl);
var guideEl=document.createElement('div');guideEl.id='df-guides';guideEl.style.cssText='position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483645;display:none';document.body.appendChild(guideEl);
document.addEventListener('mousemove',function(e){if(S.crosshairActive){crossEl.style.left=e.clientX+'px';crossEl.style.top=e.clientY+'px';}},true);
try{var savedTheme=localStorage.getItem('df_game_theme');if(savedTheme&&GAME_THEMES[savedTheme]){_applyGameTheme(savedTheme);}}catch(e){}
var _oldFavicons=document.querySelectorAll('link[rel="icon"],link[rel="shortcut icon"]');_oldFavicons.forEach(function(l){l.parentNode.removeChild(l);});var _newFav=document.createElement('link');_newFav.rel='icon';_newFav.type='image/png';_newFav.href=FAVICON_URI;document.head.appendChild(_newFav);
var fpsEl=document.createElement('div');fpsEl.style.cssText='position:fixed;top:10px;left:50%;transform:translateX(-50%);padding:3px 10px;background:rgba(0,0,0,0.5);color:#4caf50;font-size:11px;border-radius:4px;z-index:999998;pointer-events:none;font-family:monospace';
document.body.appendChild(fpsEl);var fpsTimes=[];var _fpsPlayerCount=0;var _fpsLastPlayerCheck=0;
function updateFPS(){var now=performance.now();fpsTimes.push(now);while(fpsTimes.length>0&&fpsTimes[0]<now-1000)fpsTimes.shift();
var fps=fpsTimes.length;if(now-_fpsLastPlayerCheck>2000){var pList=getPlayerList();_fpsPlayerCount=pList?pList.length:0;_fpsLastPlayerCheck=now;}
fpsEl.textContent='FPS: '+fps+(_fpsPlayerCount>0?' | Players: '+_fpsPlayerCount:'')+(S.infRunning?' | INF: '+S.infKills:'');
requestAnimationFrame(updateFPS);}requestAnimationFrame(updateFPS);
setInterval(function(){gameAPI.init();},500);
setInterval(renderKillFeed,2000);
setInterval(function(){
var pl=getPlayerList();
if(pl)trackPlayerChanges(pl);},2000);
var lastAlive=false;var _lastGodSent=0;
setInterval(function(){
var ks=getLocalKillScoreFromFiber();
var alive=ks!==null;
if(alive&&!lastAlive){reapplySettings();}
var now=Date.now();if(alive&&S.god&&now-_lastGodSent>3000){if(!S.godPromoted){gameAPI.cmd('promote 0');S.godPromoted=true;}gameAPI.cmd('set_invulnerability true');_lastGodSent=now;}
lastAlive=alive;},200);}
init();
W.DiepFried={WS:WS,gameAPI:gameAPI,getPlayerList:getPlayerList,toggle:toggle};

})();