Premium all-in-one mod menu for diep.io with powerful features for regular gameplay and custom games.
// ==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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
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]+') '+(_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?'':' Stat caps: '+caps.join('/');tip+=capStr;var pth=getTankUpgradePath(ids[t]);if(pth&&pth.length>1)tip+=' '+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,'<')+'</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("Guardian")\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">×</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,'"')+'">'+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">> '+_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};
})();