// ==UserScript==
// @name Meeland Enhancement Suite
// @namespace meeland-script
// @version 8.2.0
// @description The ultimate Meeland.io mod — fly, go invisible, steal pets hands-free, teleport, noclip, auto-lock, auto-fountain, and more. Fully configurable. Works on 60+ sites including CrazyGames, Miniplay, KBH Games, and meeland.io.
// @match *://*/*
// @run-at document-end
// @license MIT
// @grant none
// @downloadURL none
// ==/UserScript==
(async function () {
'use strict';
const W = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
for (let i = 0; i < 150 && !W.pc?.app?.root; i++) await new Promise(r => setTimeout(r, 100));
if (!W.pc?.app?.root) return;
const STORE_KEY = 'ml_wp';
const CFG_KEY = 'ml_cfg';
const isMobile = ('ontouchstart' in window || navigator.maxTouchPoints > 0) && window.matchMedia('(pointer: coarse)').matches;
let ACCEL = 6;
let FLY_MIN_SPEED = 10;
let SPEED_CAP = 100;
let GRAVITY = -18;
let SPEED_DEFAULT = 7;
let flyActive = false;
let _flyStartTime = 0;
let _flyEmoteTimer = null;
const _FLY_EMOTES = ['Waving','Breakdance','Macarena','Shuffle','Smooth Moves','Techno'];
const slots = new Array(10).fill(null);
let flyUp = false;
let flyDown = false;
let flyVelY = 0;
let prevTick = Date.now();
let sprinting = false;
let sprintSpeed = SPEED_DEFAULT;
let homePos = null;
let backPos = null;
let _pendingBaseCapture = false;
let _roomJoinTime = 0;
let cuddleTarget = null;
let cuddling = false;
let accelEnabled = true;
let featFly = true;
let featSprint = true;
let featWaypoints = true;
let featCuddle = true;
let featCuddleFollow = true;
let featPets = true;
let featAutoLock = true;
let featAntiKnockback = true;
let featNoclip = false;
let featFreeMoney = true;
let featAutoCollect = true;
let featInvincible = true;
let featGhostMode = true;
let featFreeStars = true;
let featFreeDiamonds = true;
let featAutoAttack = true;
let featAutoFountain = true;
let featInvisible = false;
let featProTips = true;
let _freeStarsTimer = null;
let _freeDiamondsTimer = null;
let _fountainTimer = null;
let autoCollectInterval = 30;
let snipeFilter = 'exclusive';
let snipeActive = false;
let snipeAutoDrop = true;
let snipeAutoHop = !!sessionStorage.getItem('ml_snipeAutoHop');
let _snipeHopNoMatchStart = 0;
let _snipeCooldown = 0;
let _snipeWasHolding = false;
const autoRefresh = true;
const refreshInterval = 5;
let kbListeningRow = null;
let petFilter = 'wild';
let petSortCol = 'price';
let petSortDir = -1;
let petAutoRefresh = true;
let petRefreshInterval = 1;
const KEYBINDS = {
fly: 'Space',
flyDown: 'KeyF',
setHome: 'KeyQ',
home: 'Backquote',
back: 'KeyZ',
cuddle: 'KeyJ',
settings: 'KeyM',
pets: 'KeyK',
slots: 'KeyI',
help: 'Slash',
snipe: 'KeyU',
attack: 'KeyG',
invisible:'KeyX',
towerComplete: 'KeyY',
};
const DEFAULT_KEYBINDS = { ...KEYBINDS };
function saveSettings() {
try {
localStorage.setItem(CFG_KEY, JSON.stringify({
ACCEL, SPEED_CAP, SPEED_DEFAULT, accelEnabled,
featFly, featSprint, featWaypoints, featCuddle, featCuddleFollow, featPets, featAutoLock,
featAntiKnockback, featNoclip, featFreeMoney, featAutoCollect, featInvincible, featGhostMode,
featFreeStars, featFreeDiamonds, featAutoAttack, featAutoFountain,
featInvisible, featProTips,
autoCollectInterval,
snipeFilter, snipeAutoDrop, snipeActive,
keybinds: { ...KEYBINDS },
petFilter, petSortCol, petSortDir, petAutoRefresh, petRefreshInterval,
}));
} catch (_) {}
}
function loadSettings() {
try {
const raw = localStorage.getItem(CFG_KEY);
if (!raw) return;
const d = JSON.parse(raw);
const assign = (key, fn) => { if (d[key] !== undefined) fn(d[key]); };
assign('ACCEL', v => ACCEL = v);
assign('SPEED_CAP', v => SPEED_CAP = v);
assign('SPEED_DEFAULT', v => SPEED_DEFAULT = v);
assign('accelEnabled', v => accelEnabled = v);
assign('featFly', v => featFly = v);
assign('featSprint', v => featSprint = v);
assign('featWaypoints', v => featWaypoints = v);
assign('featCuddle', v => featCuddle = v); assign('featVisit', v => featCuddle = v);
assign('featCuddleFollow', v => featCuddleFollow = v); assign('featStalk', v => featCuddleFollow = v);
assign('featPets', v => featPets = v);
assign('featAutoLock', v => featAutoLock = v);
assign('featAntiKnockback', v => featAntiKnockback = v);
assign('featNoclip', v => featNoclip = v);
assign('featFreeMoney', v => featFreeMoney = v);
assign('featAutoCollect', v => featAutoCollect = v);
assign('featInvincible', v => featInvincible = v);
assign('featGhostMode', v => featGhostMode = v);
assign('featFreeStars', v => featFreeStars = v);
assign('featFreeDiamonds', v => featFreeDiamonds = v);
assign('featAutoAttack', v => featAutoAttack = v);
assign('featAutoFountain', v => featAutoFountain = v);
assign('featInvisible', v => featInvisible = v);
assign('featProTips', v => featProTips = v);
assign('autoCollectInterval', v => autoCollectInterval = v);
assign('snipeFilter', v => snipeFilter = v);
assign('snipeAutoDrop', v => snipeAutoDrop = v);
assign('snipeActive', v => snipeActive = v);
assign('petFilter', v => petFilter = v);
assign('petSortCol', v => petSortCol = v);
assign('petSortDir', v => petSortDir = v);
assign('petAutoRefresh', v => petAutoRefresh = v);
assign('petRefreshInterval', v => petRefreshInterval = v);
if (d.keybinds) {
for (const k of Object.keys(KEYBINDS)) {
if (typeof d.keybinds[k] === 'string') KEYBINDS[k] = d.keybinds[k];
}
}
} catch (_) {}
}
loadSettings();
function _cached(ttl, fn) {
let v = null, t = 0;
const get = () => { const n = Date.now(); if (t && n - t < ttl) return v; t = n; return (v = fn() || null); };
get.clear = () => { v = null; t = 0; };
return get;
}
let _playerCache = null, _playerCacheTime = 0;
const getPlayer = () => {
const now = Date.now();
if (_playerCacheTime && now - _playerCacheTime < 200) return _playerCache;
_playerCache = W.pc?.app?.root?.findByName('Player') ?? null;
if (_playerCache) _playerCacheTime = now;
return _playerCache;
};
const getKcc = p => p?.script?.kcc ?? null;
const getPC = p => p?.script?.playerController ?? null;
const $ = id => document.getElementById(id);
const flash = id => {
const el = $(id);
if (el) { el.classList.add('fresh'); setTimeout(() => el.classList.remove('fresh'), 400); }
if (_nativeHud) {
const idMap = { 'ml-fly': 'fly', 'ml-spr': 'spr', 'ml-home': 'home', 'ml-go': 'go', 'ml-back': 'back', 'ml-slots': 'slots', 'ml-lock': 'lock', 'ml-tc': 'tc' };
const k = idMap[id];
if (k && _nativeHud[k]) { _nativeHud[k].classList.add('fresh'); setTimeout(() => _nativeHud[k].classList.remove('fresh'), 400); }
}
};
const syncSlider = (id, valId, v, uom) => { const el = $(id), ve = $(valId); if (el) el.value = v; if (ve) ve.textContent = uom ? v + uom : v; };
const vec3 = v => ({ x: v.x, y: v.y, z: v.z });
const getCamera = () => W.pc?.app?.root?.findByName('Camera') ?? null;
const _rawSend = room => room.__mlOrigSend || room.send.bind(room);
function capturePos(player) {
const p = player.getPosition();
const r = player.getEulerAngles();
const camera = getCamera();
const cam = camera?.getEulerAngles();
let se = null;
for (const inst of (camera?.script?._scripts || [])) {
if (inst.eulers) { se = vec3(inst.eulers); break; }
}
return { x: p.x, y: p.y, z: p.z, rot: vec3(r), cam: cam ? vec3(cam) : null, se };
}
function restoreCam(posData) {
if (!posData?.cam) return;
const camera = getCamera();
if (!camera) return;
let scriptRestored = false;
for (const inst of (camera.script?._scripts || [])) {
if ('_yaw' in inst) { inst._yaw = posData.cam.y; scriptRestored = true; }
if ('_pitch' in inst) { inst._pitch = posData.cam.x; scriptRestored = true; }
if ('yaw' in inst) { inst.yaw = posData.cam.y; scriptRestored = true; }
if ('pitch' in inst) { inst.pitch = posData.cam.x; scriptRestored = true; }
if (inst.eulers && posData.se) {
inst.eulers.x = posData.se.x; inst.eulers.y = posData.se.y; inst.eulers.z = posData.se.z;
scriptRestored = true;
}
}
if (!scriptRestored) camera.setEulerAngles(posData.cam.x, posData.cam.y, posData.cam.z);
}
const FEAT_IDS = ['ml-f-fly','ml-f-sprint','ml-f-waypoints','ml-f-cuddle','ml-f-cuddle-follow','ml-f-pets','ml-f-autolock','ml-f-antiknockback','ml-f-noclip','ml-f-freemoney','ml-f-autocollect','ml-f-invincible','ml-f-ghostmode','ml-f-freestars','ml-f-freediamonds','ml-f-autoattack','ml-f-autofountain','ml-f-invisible','ml-f-protips'];
const MOVE_KEYS = new Set(['KeyW','KeyA','KeyS','KeyD','ArrowUp','ArrowDown','ArrowLeft','ArrowRight']);
function getPlayerState() {
const nm = W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager;
if (!nm?.room?.state?.players) return null;
return nm.room.state.players.get(nm.room.sessionId) ?? null;
}
const setSpeed = (pc, kcc, v) => {
if (pc) { pc.currentSpeed = v; pc.speed = v; }
if (kcc.speed !== undefined) kcc.speed = v;
const ps = getPlayerState();
if (ps && ps.movementSpeed !== undefined) ps.movementSpeed = v / SPEED_DEFAULT;
};
function teleport(player, pos) {
player.setPosition(pos.x, pos.y, pos.z);
player.rigidbody?.teleport(pos.x, pos.y, pos.z);
if (pos.rot) player.setEulerAngles(pos.rot.x, pos.rot.y, pos.rot.z);
restoreCam(pos);
}
function serializePos(p) {
if (!p) return null;
const o = vec3(p);
if (p.rot) o.rot = vec3(p.rot);
if (p.cam) o.cam = vec3(p.cam);
if (p.se) o.se = vec3(p.se);
return o;
}
function saveWaypoints() {
try {
localStorage.setItem(STORE_KEY, JSON.stringify({
home: serializePos(homePos),
back: serializePos(backPos),
slots: slots.map(s => serializePos(s)),
}));
} catch (_) {}
}
function loadWaypoints() {
try {
const raw = localStorage.getItem(STORE_KEY);
if (!raw) return;
const d = JSON.parse(raw);
if (d.home) homePos = d.home;
if (d.back) backPos = d.back;
if (Array.isArray(d.slots)) d.slots.forEach((s, i) => { if (s) slots[i] = s; });
} catch (_) {}
}
function flyOn(kcc, hover) {
flyActive = true;
_flyStartTime = Date.now();
kcc.gravity = 0;
if (hover) {
flyUp = false;
flyDown = false;
flyVelY = 0;
kcc._velY = 0;
} else {
flyUp = true;
flyVelY = FLY_MIN_SPEED;
kcc._velY = FLY_MIN_SPEED;
}
_flyEmoteLoop();
}
function flyOff(kcc, resetVel = true) {
flyActive = false;
flyUp = false;
flyDown = false;
flyVelY = 0;
kcc.gravity = GRAVITY;
if (resetVel) kcc._velY = 0;
_flyEmoteStop();
}
function _flyEmoteLoop() {
clearTimeout(_flyEmoteTimer);
_flyEmoteTimer = null;
if (!flyActive) return;
const app = W.pc?.app;
if (!app) return;
const name = _FLY_EMOTES[Math.random() * _FLY_EMOTES.length | 0];
app.fire('NetworkManager:Send', 'emotePlay', name);
const asset = app.assets?.find('Emote-' + name + '.glb', 'animation');
const dur = asset?.resource?.duration || 4;
_flyEmoteTimer = setTimeout(_flyEmoteLoop, dur * 1000 + 200);
}
function _flyEmoteStop() {
clearTimeout(_flyEmoteTimer);
_flyEmoteTimer = null;
const app = W.pc?.app;
if (app) app.fire('NetworkManager:Send', 'emoteStop');
}
const DESCRIPTION = `
Meeland Enhancement Suite
The ultimate Meeland.io mod. Fly, teleport, steal pets effortlessly, go invisible, and dominate every game mode. Works on 60+ sites including CrazyGames, Miniplay, KBH Games, and more.
PC + Mobile. Keyboard shortcuts on desktop, tap-friendly HUD buttons on mobile.
100% free, always. Got feedback? Leave a ⭐ positive review — that's what keeps updates coming.
What You Get
🚀 Fly — Soar above the map. Hold Space to rise, F to drop. Configurable speed.
⚡ Speed Hack — Hold Shift to go faster than anyone. Adjustable cap.
👁️ Invisible Mode — Press X to vanish. Other players literally cannot see you.
🔍 Anti-Invisibility — If someone else tries to go invisible, you still see them as a ghost.
🎯 Pet Sniper — Auto-steal pets matching your filters. Teleports, grabs, and drops — fully hands-free. Works in both Steal a Pet and Escape Waves.
🛡️ Untouchable Carry — Stolen pets are carried at a fake height so nobody can attack you.
💀 Can't Die — Immune to falling, waves, and knockback. Toggle each separately.
👻 Noclip — Walk through any wall or obstacle.
⚔️ Instant Attack — G teleports to the nearest player and hits them.
📍 Waypoints — Save up to 10 positions. Teleport home with one key. Z to toggle back.
🐕 Pet Browser — K shows every pet in the room. Sort, filter, teleport, or grab any of them.
🐾 Cuddle Panel — J lists all players by distance. Click to teleport and follow.
🔒 Auto-Lock — Your base stays locked. Always.
⛲ Auto-Fountain — Permanent 1.5× profit multiplier. Re-activates the moment it expires. Works in Waves too.
💰 Free Money + Stars + Diamonds — Auto-farms daily coins, unlimited stars (Waves), and free diamonds (Tower) without ads.
🐾 Auto-Collect Earnings — Pet income claimed automatically every 30s.
🏠 Auto Return Home — After a steal, teleports you back to base to deposit safely.
⚙️ Fully Configurable — M opens settings. Toggle any feature, rebind any key, adjust all speeds. Everything saves automatically.
✏️ Change Username — Rename your character from the Settings panel.
🏗️ Tower Complete — Press Y in Obby Tower to instantly finish all checkpoints and win.
Keys
All rebindable in Settings.
Space — Fly up
F — Fly down
Shift — Sprint
X — Invisible
G — Attack nearest
Q — Save home
` — Teleport home
Z — Toggle back
J — Players
K — Pets
U — Sniper
I — Slots
M — Settings
Y — Tower Complete
? — Help
Install
Desktop (Chrome, Edge, Firefox, Opera, Brave)
Get Tampermonkey or Violentmonkey
Click Install above
Open any Meeland game — it just works
Mobile (Android)
Install Firefox for Android from the Play Store
Open Firefox → Menu → Add-ons → install Tampermonkey
Visit this page in Firefox and tap Install
Play Meeland — tap-friendly HUD buttons appear automatically
iOS: Apple blocks browser extensions on all iOS browsers (including Firefox). Use a desktop browser or Android.
60+ Supported Sites
meeland.io, CrazyGames, Miniplay.com, MiniPlay.io, PlayMiniGames, RocketGames.io, KBH Games, Gamenora, WooGames.io, TwoPlayerGames.org, KizGame, OmiGames, PlayingFunGames, Gameflare.com, Free AI Games, ZapGames.io, Play-Games.com, GoGy Games, iogames.games, Sprunki-Game.io, 1000Games.io, Play-IOGames.com, ArcadeHippo, Sleepy Arcade, HotGames.io, geodashlite.io, YoPlay.io, Geometry-Free.com, Sprunki.org, Gombis.com, Wordle Unlimited, Games-Kids.com, spacewavesgame.io, amongusfree.io, Veck IO, geometrylitegame.org, Geometry Dash Subzero, Dinosaur-Game.io, Crossy-Road.io, Snow Rider 3D, Drive Mad 3, geometrylitegame.io, That's Not My Neighbor, Melon Playground, Capybara Game, FNF Games, 8Games.net, CrazyGames.tools, LoveMoneyGames.com, Zahraj.cz, iogames.onl, 1Games.io, TrendGames.io, StickmanHook2.io, GeometryDashSpam.io, SGameS.org, robbrainrot.io, Animalverse.social, BasketRandom.io, YupGames.io, GameDistribution CDN, + any site embedding Meeland in an iframe
Privacy
✅ Client-side only — no data leaves your browser
Disclaimer
For educational and entertainment purposes. Use at your own risk.
`;
let _savedPointerLock = false;
let _intentionalPointerExit = false;
const anyPanelOpen = () =>
$('ml-plist')?.classList.contains('open') ||
$('ml-settings')?.classList.contains('open') ||
$('ml-pets')?.classList.contains('open') ||
$('ml-dialog')?.classList.contains('open') ||
$('ml-slots-pop')?.classList.contains('open');
function panelPause() {
_savedPointerLock = _savedPointerLock || !!document.pointerLockElement;
if (document.pointerLockElement) {
_intentionalPointerExit = true;
document.exitPointerLock();
}
}
function panelResume() {
if (anyPanelOpen()) return;
if (_savedPointerLock) {
const app = W.pc?.app;
if (app) app.fire('GameManager:LockMouse', 'GameManager:GameResumed', null, 'GameManager:GamePause');
}
_savedPointerLock = false;
}
function _getEnemyName(entity) {
let n = null;
try { n = entity.script?.enemy?.usernameEntity?.element?.text; } catch (_) {}
if (!n) { const ue = entity.findByName?.('Username'); if (ue?.element?.text) n = ue.element.text; }
return n || null;
}
const _getPetTycoon = _cached(5000, () => {
const app = W.pc?.app;
return app?.root?.findByName('PetTycoonRoom')?.script?.petTycoon
|| app?.root?.find(e => e.script?.petTycoon)?.[0]?.script?.petTycoon;
});
const _getEscapeTsunami = _cached(5000, () => {
const app = W.pc?.app;
return app?.root?.findByName('EscapeTsunamiRoom')?.script?.escapeTsunamiRoom
|| app?.root?.find(e => e.script?.escapeTsunamiRoom)?.[0]?.script?.escapeTsunamiRoom;
});
const _getNetworkManager = _cached(5000, () =>
W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager
);
const _getBasesManager = _cached(5000, () =>
W.pc?.app?.root?.findByName('Bases')?.script?.petTycoonBasesManager
);
const _findPetsManager = _cached(5000, () => {
const root = W.pc?.app?.root;
if (!root) return null;
const pmEntity = root.findByName('PetsManager');
let pm = pmEntity?.script?.petsManager;
if (!pm && pmEntity?.script) {
for (const k of Object.keys(pmEntity.script)) {
const s = pmEntity.script[k];
if (s?.activePets || s?.basePets) { pm = s; break; }
}
}
if (!pm) {
root.find(e => {
if (!e.script) return false;
for (const k of Object.keys(e.script)) {
const s = e.script[k];
if (s?.activePets || s?.basePets) { pm = s; return true; }
}
return false;
});
}
return pm;
});
const _serverLockedSessions = new Set();
let _lockDataReceived = false;
function createHUD() {
if ($('ml-hud')) return;
const s = document.createElement('style');
s.textContent = [
'#ml-hud{display:none}',
'#ml-dialog{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.75);z-index:100000;display:none;overflow:auto;padding:40px 20px;box-sizing:border-box;scrollbar-width:auto;scrollbar-color:rgba(160,120,240,.5) rgba(255,255,255,.06);-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'#ml-dialog::-webkit-scrollbar{width:8px}',
'#ml-dialog::-webkit-scrollbar-track{background:rgba(255,255,255,.06);border-radius:4px}',
'#ml-dialog::-webkit-scrollbar-thumb{background:rgba(160,120,240,.5);border-radius:4px;border:1px solid rgba(255,255,255,.08)}',
'#ml-dialog::-webkit-scrollbar-thumb:hover{background:rgba(160,120,240,.7)}',
'#ml-dialog.open{display:block}',
'#ml-inner{background:#181818;color:#ccc;max-width:580px;margin:0 auto;border-radius:8px;padding:24px 28px;font-family:sans-serif;font-size:14px;line-height:1.65;position:relative}',
'#ml-inner h1{color:#fff;font-size:1.2em;margin:0 0 10px}',
'#ml-inner h2{color:#aaa;font-size:.85em;text-transform:uppercase;letter-spacing:.08em;margin:18px 0 6px;border-top:1px solid #333;padding-top:12px}',
'#ml-inner li{margin:4px 0}',
'#ml-inner kbd{background:#2a2a2a;border:1px solid #444;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.9em}',
'#ml-inner a{color:#4af}',
'#ml-inner blockquote{border-left:3px solid #333;margin:8px 0 0;padding:6px 12px;color:#888;font-size:.9em}',
'#ml-close{position:absolute;top:10px;right:14px;cursor:pointer;color:#555;font-size:20px;background:none;border:none;font-family:monospace;line-height:1}',
'#ml-close:hover{color:#fff}',
'#ml-plist{position:fixed;top:50%;right:20px;transform:translateY(-50%);width:260px;max-height:70vh;display:none;z-index:100001;pointer-events:auto;overflow:hidden;border-radius:16px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:linear-gradient(170deg,rgba(55,45,80,.92) 0%,rgba(45,38,72,.94) 40%,rgba(40,32,65,.95) 100%);border:1.5px solid rgba(160,120,240,.3);box-shadow:0 8px 32px rgba(0,0,0,.4),0 0 0 1px rgba(80,50,140,.2),0 0 60px rgba(120,80,200,.1);user-select:none}',
'#ml-plist::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#7c3aed,#a855f7,#c084fc,#a855f7,#7c3aed);border-radius:16px 16px 0 0;z-index:1}',
'#ml-plist.open{display:flex;flex-direction:column}',
'#ml-plist-head{padding:14px 14px 10px;display:flex;align-items:center;justify-content:space-between}',
'#ml-plist-title{font-size:14px;font-weight:800;color:rgba(255,250,255,.97);letter-spacing:.02em}',
'#ml-plist-timer{font-size:10px;color:rgba(190,155,245,.65);font-variant-numeric:tabular-nums;margin-left:6px;font-weight:600}',
'#ml-plist-refresh{cursor:pointer;background:rgba(140,100,220,.12);border:1px solid rgba(140,100,220,.25);color:rgba(200,170,255,.8);font-size:12px;padding:3px 9px;border-radius:8px;font-family:inherit;transition:all .15s}',
'#ml-plist-refresh:hover{background:rgba(140,100,220,.22);color:rgba(240,220,255,.95);border-color:rgba(140,100,220,.4)}',
'#ml-plist-close{cursor:pointer;background:rgba(255,255,255,.08);border:none;color:rgba(220,200,245,.6);font-size:16px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-family:inherit;line-height:1;transition:all .15s}',
'#ml-plist-close:hover{color:#ff6b6b;background:rgba(255,80,80,.15)}',
'#ml-plist-body{overflow-y:auto;flex:1;padding:4px 8px 6px;scrollbar-width:auto;scrollbar-color:rgba(160,120,240,.5) rgba(255,255,255,.06);-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'#ml-plist-body::-webkit-scrollbar{width:8px}',
'#ml-plist-body::-webkit-scrollbar-track{background:rgba(255,255,255,.06);border-radius:4px}',
'#ml-plist-body::-webkit-scrollbar-thumb{background:rgba(160,120,240,.5);border-radius:4px;border:1px solid rgba(255,255,255,.08)}',
'#ml-plist-body::-webkit-scrollbar-thumb:hover{background:rgba(160,120,240,.7)}',
'#ml-plist-empty{color:rgba(190,155,245,.5);text-align:center;padding:20px 8px;font-size:12px;font-style:italic}',
'.ml-prow{display:flex;align-items:center;gap:8px;padding:7px 10px;margin:2px 0;border-radius:10px;cursor:pointer;transition:all .15s;border:1px solid transparent;background:rgba(140,100,220,.04)}',
'.ml-prow:hover{background:rgba(140,100,220,.1);border-color:rgba(140,100,220,.15)}',
'.ml-prow:active{background:rgba(140,100,220,.18);transform:scale(.98);border-color:rgba(140,100,220,.25)}',
'.ml-pnum{font-size:10px;color:rgba(168,130,230,.6);font-weight:700;min-width:16px;text-align:right;font-variant-numeric:tabular-nums}',
'.ml-pinfo{flex:1;overflow:hidden}',
'.ml-pname{font-size:12.5px;font-weight:600;color:rgba(255,250,255,.97);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}',
'.ml-pdist{font-size:10px;color:rgba(168,130,230,.55);line-height:1.2}',
'.ml-parrow{color:rgba(140,100,220,.3);font-size:14px;transition:all .15s}',
'.ml-prow:hover .ml-parrow{color:rgba(168,130,230,.75);transform:translateX(3px)}',
'#ml-settings{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:320px;max-height:80vh;display:none;z-index:100002;pointer-events:auto;overflow:hidden;border-radius:16px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;user-select:none;background:linear-gradient(170deg,rgba(55,45,80,.92) 0%,rgba(45,38,72,.94) 40%,rgba(40,32,65,.95) 100%);border:1.5px solid rgba(160,120,240,.3);box-shadow:0 8px 32px rgba(0,0,0,.4),0 0 0 1px rgba(80,50,140,.2),0 0 60px rgba(120,80,200,.1)}',
'#ml-settings::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#7c3aed,#a855f7,#c084fc,#a855f7,#7c3aed);border-radius:16px 16px 0 0;z-index:1}',
'#ml-settings.open{display:flex;flex-direction:column}',
'#ml-settings-head{position:relative;padding:18px 18px 14px;display:flex;align-items:center;justify-content:space-between}',
'#ml-settings-title{font-size:16px;font-weight:800;color:rgba(255,250,255,.97);letter-spacing:.02em}',
'#ml-settings-close{cursor:pointer;background:rgba(255,255,255,.08);border:none;color:rgba(220,200,245,.6);font-size:16px;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-family:inherit;line-height:1;transition:all .15s}',
'#ml-settings-close:hover{color:#ff6b6b;background:rgba(255,80,80,.15)}',
'#ml-settings-reset{cursor:pointer;background:rgba(255,255,255,.08);border:1px solid rgba(160,120,240,.25);color:rgba(220,200,245,.7);font-size:10px;padding:5px 14px;border-radius:8px;font-family:inherit;font-weight:600;transition:all .15s}',
'#ml-settings-reset:hover{color:rgba(240,220,255,.9);background:rgba(140,100,220,.15);border-color:rgba(140,100,220,.4)}',
'#ml-settings-body{position:relative;overflow-y:auto;flex:1;padding:2px 12px 14px;scrollbar-width:auto;scrollbar-color:rgba(160,120,240,.5) rgba(255,255,255,.06);-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'#ml-settings-body::-webkit-scrollbar{width:8px}',
'#ml-settings-body::-webkit-scrollbar-track{background:rgba(255,255,255,.06);border-radius:4px}',
'#ml-settings-body::-webkit-scrollbar-thumb{background:rgba(160,120,240,.5);border-radius:4px;border:1px solid rgba(255,255,255,.08)}',
'#ml-settings-body::-webkit-scrollbar-thumb:hover{background:rgba(160,120,240,.7)}',
'.ml-sgroup{margin:0 0 2px}',
'#ml-tabs{display:flex;gap:4px;padding:0 16px 12px;margin-bottom:2px;border-bottom:1px solid rgba(160,120,240,.15)}',
'.ml-tab{flex:1;padding:8px 0;font-size:11px;font-weight:700;text-align:center;color:rgba(200,180,240,.5);cursor:pointer;transition:all .2s;border-radius:10px;background:rgba(255,255,255,.05);border:1px solid transparent;font-family:inherit}',
'.ml-tab:hover{color:rgba(200,180,240,.7);background:rgba(140,100,220,.08)}',
'.ml-tab.active{color:#fff;background:linear-gradient(135deg,rgba(140,90,230,.45) 0%,rgba(110,70,210,.35) 100%);border-color:rgba(160,110,240,.35);box-shadow:0 2px 12px rgba(130,80,220,.25),inset 0 1px 0 rgba(255,255,255,.1)}',
'.ml-tab-content{display:none;min-height:380px}',
'.ml-tab-content.active{display:block}',
'.ml-srow{display:flex;align-items:center;justify-content:space-between;padding:9px 10px;border-radius:10px;transition:all .12s;margin:1px 0}',
'.ml-srow:hover{background:rgba(140,100,220,.06)}',
'.ml-srow-label{font-size:13px;color:rgba(245,238,255,.92);flex:1;font-weight:500}',
'.ml-srow-value{font-size:11px;color:rgba(190,155,245,.9);font-variant-numeric:tabular-nums;min-width:34px;text-align:right;margin-right:8px;font-weight:700}',
'.ml-toggle{position:relative;width:40px;height:22px;border-radius:11px;background:rgba(255,255,255,.12);border:1.5px solid rgba(255,255,255,.15);cursor:pointer;transition:all .2s;flex-shrink:0}',
'.ml-toggle::after{content:"";position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:7px;background:rgba(255,255,255,.35);transition:all .2s;box-shadow:0 1px 3px rgba(0,0,0,.3)}',
'.ml-toggle.on{background:linear-gradient(135deg,#7c3aed 0%,#a855f7 100%);border-color:rgba(168,85,247,.5)}',
'.ml-toggle.on::after{left:21px;background:#fff;box-shadow:0 1px 4px rgba(120,60,220,.4),0 0 8px rgba(168,85,247,.3)}',
'.ml-slider{-webkit-appearance:none;appearance:none;width:90px;height:6px;border-radius:3px;background:rgba(255,255,255,.12);outline:none;cursor:pointer;border:none}',
'.ml-slider::-webkit-slider-thumb{-webkit-appearance:none;width:18px;height:18px;border-radius:9px;background:linear-gradient(135deg,#a855f7 0%,#7c3aed 100%);border:2px solid rgba(255,255,255,.25);box-shadow:0 2px 8px rgba(120,60,220,.4);cursor:pointer;transition:all .15s}',
'.ml-slider::-webkit-slider-thumb:hover{transform:scale(1.1);box-shadow:0 2px 12px rgba(120,60,220,.5)}',
'.ml-slider::-moz-range-thumb{width:18px;height:18px;border-radius:9px;background:linear-gradient(135deg,#a855f7 0%,#7c3aed 100%);border:2px solid rgba(255,255,255,.25);box-shadow:0 2px 8px rgba(120,60,220,.4);cursor:pointer}',
'#ml-keybinds-body{padding:0}',
'.ml-krow{display:flex;align-items:center;justify-content:space-between;padding:9px 10px;border-radius:10px;cursor:pointer;transition:all .12s;margin:1px 0}',
'.ml-krow:hover{background:rgba(140,100,220,.06)}',
'.ml-krow.listening{background:rgba(255,180,40,.1);border:1px solid rgba(255,180,40,.35);border-radius:10px}',
'.ml-krow-action{font-size:13px;color:rgba(245,238,255,.92);font-weight:500}',
'.ml-krow-key{font-size:11px;color:rgba(190,155,245,.9);font-weight:700;padding:4px 10px;border-radius:8px;background:rgba(140,100,220,.1);border:1px solid rgba(140,100,220,.2);min-width:40px;text-align:center;transition:all .15s}',
'.ml-krow.listening .ml-krow-key{color:rgba(255,200,80,.9);background:rgba(120,80,0,.15);border-color:rgba(255,180,40,.4)}',
'#ml-pets{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:680px;max-height:82vh;display:none;z-index:100004;pointer-events:auto;overflow:hidden;border-radius:16px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;user-select:none;background:linear-gradient(170deg,rgba(55,45,80,.92) 0%,rgba(45,38,72,.94) 40%,rgba(40,32,65,.95) 100%);border:1.5px solid rgba(160,120,240,.3);box-shadow:0 8px 32px rgba(0,0,0,.4),0 0 0 1px rgba(80,50,140,.2),0 0 60px rgba(120,80,200,.1)}',
'#ml-pets::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#7c3aed,#a855f7,#c084fc,#a855f7,#7c3aed);border-radius:16px 16px 0 0;z-index:1}',
'#ml-pets.open{display:flex;flex-direction:column}',
'#ml-pets-head{position:relative;padding:16px 18px 12px;display:flex;align-items:center;justify-content:space-between}',
'#ml-pets-title{font-size:16px;font-weight:800;color:rgba(255,250,255,.97);letter-spacing:.02em}',
'#ml-pets-count{font-size:10px;color:rgba(190,155,245,.7);margin-left:8px;font-weight:600}',
'#ml-pets-close{cursor:pointer;background:rgba(255,255,255,.08);border:none;color:rgba(220,200,245,.6);font-size:16px;width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-family:inherit;line-height:1;transition:all .15s}',
'#ml-pets-close:hover{color:#ff6b6b;background:rgba(255,80,80,.15)}',
'#ml-pets-filter{position:relative;padding:8px 14px;border-bottom:1px solid rgba(160,120,240,.15)}',
'#ml-pets-search{width:100%;background:rgba(255,255,255,.07);border:1.5px solid rgba(160,120,240,.25);color:rgba(240,230,255,.9);font-size:11px;padding:7px 12px;border-radius:10px;font-family:inherit;outline:none;box-sizing:border-box;transition:all .15s}',
'#ml-pets-search:focus{border-color:rgba(140,100,220,.45);box-shadow:0 0 8px rgba(120,70,200,.12)}',
'#ml-pets-search::placeholder{color:rgba(168,130,230,.35)}',
'#ml-pets-body{position:relative;overflow-y:auto;flex:1;padding:0;scrollbar-width:auto;scrollbar-color:rgba(160,120,240,.5) rgba(255,255,255,.06);-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'#ml-pets-body::-webkit-scrollbar{width:8px}',
'#ml-pets-body::-webkit-scrollbar-track{background:rgba(255,255,255,.06);border-radius:4px}',
'#ml-pets-body::-webkit-scrollbar-thumb{background:rgba(160,120,240,.5);border-radius:4px;border:1px solid rgba(255,255,255,.08)}',
'#ml-pets-body::-webkit-scrollbar-thumb:hover{background:rgba(160,120,240,.7)}',
'#ml-ptable{width:100%;border-collapse:collapse;font-size:11px}',
'#ml-ptable th{position:sticky;top:0;background:linear-gradient(180deg,rgba(55,45,80,.98),rgba(45,38,72,.95));color:rgba(168,130,230,.7);font-size:9.5px;text-transform:uppercase;letter-spacing:.15em;padding:7px 8px;text-align:left;cursor:pointer;border-bottom:1px solid rgba(140,100,220,.15);white-space:nowrap;transition:color .12s;z-index:1;font-weight:700}',
'#ml-ptable th:hover{color:rgba(200,170,255,.95)}',
'#ml-ptable th.sort-asc::after{content:" ▲";font-size:8px}',
'#ml-ptable th.sort-desc::after{content:" ▼";font-size:8px}',
'#ml-ptable td{padding:5px 8px;border-bottom:1px solid rgba(160,120,240,.08);color:rgba(245,238,255,.85);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:140px}',
'#ml-ptable tr:hover td{background:rgba(140,100,220,.08)}',
'#ml-ptable .own-you{color:rgba(80,255,120,.9);font-weight:600}',
'#ml-ptable .own-wild{color:rgba(255,190,60,.85);font-style:italic}',
'#ml-ptable .pet-var{color:rgba(180,140,255,.85);font-style:italic}',
'#ml-ptable .pet-mut-Golden{color:rgba(255,215,0,.95)}',
'#ml-ptable .pet-mut-Diamond{color:rgba(185,242,255,.95)}',
'#ml-ptable .pet-mut-Emerald{color:rgba(80,255,120,.95)}',
'#ml-ptable .pet-mut-Rainbow{color:rgba(255,120,200,.95)}',
'#ml-ptable .pet-mut-Galaxy{color:rgba(200,140,255,.95)}',
'#ml-ptable .pet-rar{font-size:10px;letter-spacing:.05em}',
'.pet-tp{background:rgba(140,100,220,.15);border:1px solid rgba(140,100,220,.3);color:rgba(200,170,255,.8);font-size:11px;padding:3px 8px;border-radius:8px;cursor:pointer;transition:all .12s;line-height:1}',
'.pet-tp:hover{background:rgba(140,100,220,.3);color:#fff;border-color:rgba(168,120,255,.6)}',
'.pet-grab{background:rgba(60,200,60,.15);border:1px solid rgba(60,200,60,.3);color:rgba(140,255,140,.8);font-size:11px;padding:3px 8px;border-radius:8px;cursor:pointer;transition:all .12s;line-height:1;margin-left:3px}',
'.pet-grab:hover{background:rgba(60,200,60,.3);color:#fff;border-color:rgba(80,230,80,.6)}',
'#ml-pets-empty{color:rgba(190,155,245,.5);text-align:center;padding:30px 8px;font-size:12px;font-style:italic}',
'#ml-slots-pop{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:300px;max-height:70vh;display:none;z-index:100003;pointer-events:auto;overflow:hidden;border-radius:16px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;user-select:none;background:linear-gradient(170deg,rgba(55,45,80,.92) 0%,rgba(45,38,72,.94) 40%,rgba(40,32,65,.95) 100%);border:1.5px solid rgba(160,120,240,.3);box-shadow:0 8px 32px rgba(0,0,0,.4),0 0 0 1px rgba(80,50,140,.2),0 0 60px rgba(120,80,200,.1)}',
'#ml-slots-pop::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#7c3aed,#a855f7,#c084fc,#a855f7,#7c3aed);border-radius:16px 16px 0 0;z-index:1}',
'#ml-slots-pop.open{display:flex;flex-direction:column}',
'#ml-slots-pop-head{padding:14px 14px 10px;display:flex;align-items:center;justify-content:space-between}',
'#ml-slots-pop-title{font-size:14px;font-weight:800;color:rgba(255,250,255,.97);letter-spacing:.02em}',
'#ml-slots-pop-close{cursor:pointer;background:rgba(255,255,255,.08);border:none;color:rgba(220,200,245,.6);font-size:16px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-family:inherit;line-height:1;transition:all .15s}',
'#ml-slots-pop-close:hover{color:#ff6b6b;background:rgba(255,80,80,.15)}',
'#ml-slots-pop-body{overflow-y:auto;flex:1;padding:4px 8px 8px;scrollbar-width:auto;scrollbar-color:rgba(160,120,240,.5) rgba(255,255,255,.06);-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'#ml-slots-pop-body::-webkit-scrollbar{width:8px}',
'#ml-slots-pop-body::-webkit-scrollbar-track{background:rgba(255,255,255,.06);border-radius:4px}',
'#ml-slots-pop-body::-webkit-scrollbar-thumb{background:rgba(160,120,240,.5);border-radius:4px;border:1px solid rgba(255,255,255,.08)}',
'#ml-slots-pop-body::-webkit-scrollbar-thumb:hover{background:rgba(160,120,240,.7)}',
'.ml-slot{display:flex;align-items:center;gap:6px;padding:5px 8px;margin:2px 0;border-radius:8px;transition:all .12s;border:1px solid transparent}',
'.ml-slot:hover{background:rgba(140,100,220,.1);border-color:rgba(140,100,220,.15)}',
'.ml-slot-num{font-size:10px;color:rgba(168,130,230,.6);font-weight:700;min-width:14px;text-align:right}',
'.ml-slot-info{flex:1;font-size:10px;color:rgba(200,180,240,.65);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}',
'.ml-slot-info.filled{color:rgba(200,180,240,.9)}',
'.ml-slot-go{background:rgba(140,100,220,.15);border:1px solid rgba(140,100,220,.25);color:rgba(200,170,255,.8);font-size:10px;padding:2px 8px;border-radius:6px;cursor:pointer;transition:all .12s}',
'.ml-slot-go:hover{background:rgba(140,100,220,.3);color:#fff}',
'.ml-slot-go.empty{opacity:.35;pointer-events:none}',
'.ml-slot-set{background:rgba(60,180,60,.12);border:1px solid rgba(60,180,60,.25);color:rgba(140,230,140,.8);font-size:10px;padding:2px 8px;border-radius:6px;cursor:pointer;transition:all .12s}',
'.ml-slot-set:hover{background:rgba(60,180,60,.25);color:#fff}',
'.ml-slot-clr{background:rgba(220,60,60,.12);border:1px solid rgba(220,60,60,.25);color:rgba(255,140,140,.8);font-size:10px;padding:2px 6px;border-radius:6px;cursor:pointer;transition:all .12s}',
'.ml-slot-clr:hover{background:rgba(220,60,60,.25);color:#fff}',
'.ml-slot-clr.empty{opacity:.35;pointer-events:none}',
'#ml-snipe{position:fixed;top:60px;right:10px;width:360px;max-height:calc(100vh - 20px);display:none;z-index:100005;pointer-events:auto;overflow:hidden auto;border-radius:16px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;user-select:none;background:linear-gradient(170deg,rgba(55,45,80,.92) 0%,rgba(45,38,72,.94) 40%,rgba(40,32,65,.95) 100%);border:1.5px solid rgba(240,120,120,.3);box-shadow:0 8px 32px rgba(0,0,0,.4),0 0 0 1px rgba(140,50,50,.2),0 0 60px rgba(200,80,80,.1)}',
'#ml-snipe::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#ed3a3a,#f75555,#fc8484,#f75555,#ed3a3a);border-radius:16px 16px 0 0;z-index:1}',
'#ml-snipe.open{display:flex;flex-direction:column}',
'#ml-snipe-head{padding:14px 14px 10px;display:flex;align-items:center;justify-content:space-between}',
'#ml-snipe-title{font-size:14px;font-weight:800;color:rgba(255,250,255,.97);letter-spacing:.02em}',
'#ml-snipe-close{cursor:pointer;background:rgba(255,255,255,.08);border:none;color:rgba(220,200,245,.6);font-size:16px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-family:inherit;line-height:1;transition:all .15s}',
'#ml-snipe-close:hover{color:#ff6b6b;background:rgba(255,80,80,.15)}',
'#ml-snipe-body{padding:8px 14px 14px}',
'#ml-snipe-search{width:100%;background:rgba(255,255,255,.07);border:1.5px solid rgba(240,120,120,.25);color:rgba(240,230,255,.9);font-size:11px;padding:7px 12px;border-radius:10px;font-family:inherit;outline:none;box-sizing:border-box;transition:all .15s}',
'#ml-snipe-search:focus{border-color:rgba(240,120,120,.45);box-shadow:0 0 8px rgba(200,70,70,.12)}',
'#ml-snipe-search::placeholder{color:rgba(255,170,170,.5)}',
'#ml-snipe-toggle{width:100%;margin-top:10px;padding:8px 0;border-radius:10px;border:1.5px solid rgba(240,120,120,.3);background:rgba(240,120,120,.12);color:rgba(255,200,200,.95);font-size:12px;font-weight:700;cursor:pointer;transition:all .15s;font-family:inherit}',
'#ml-snipe-toggle:hover{background:rgba(240,120,120,.25)}',
'#ml-snipe-toggle.active{background:rgba(60,200,60,.15);border-color:rgba(60,200,60,.3);color:rgba(140,255,140,.95)}',
'#ml-snipe-status{margin-top:8px;font-size:11px;color:rgba(255,220,220,.85);min-height:14px}',
'#ml-snipe-types{margin-top:4px;font-size:10px;color:rgba(255,220,210,.9);line-height:1.5;max-height:80px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(240,120,120,.3) transparent;-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'.ml-snipe-type-row{padding:1px 0;border-bottom:1px solid rgba(240,120,120,.06)}',
'#ml-snipe-log{margin-top:6px;max-height:120px;overflow-y:auto;font-size:9.5px;color:rgba(220,200,240,.7);scrollbar-width:thin;scrollbar-color:rgba(160,120,240,.4) transparent;-webkit-overflow-scrolling:touch;touch-action:pan-y}',
'.ml-snipe-entry{padding:1px 0;border-bottom:1px solid rgba(160,120,240,.08);font-family:monospace;letter-spacing:-.02em}',
'#ml-carry-timer{position:fixed;bottom:18px;left:18px;display:none;padding:6px 14px;border-radius:10px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;font-weight:800;color:rgba(255,220,140,.95);background:rgba(40,32,60,.85);border:1.5px solid rgba(255,180,80,.35);box-shadow:0 4px 16px rgba(0,0,0,.3);z-index:100010;pointer-events:none;letter-spacing:.03em}',
].join('');
document.head.appendChild(s);
const h = document.createElement('div');
const carryTimer = document.createElement('div');
carryTimer.id = 'ml-carry-timer';
document.body.appendChild(carryTimer);
h.id = 'ml-hud';
h.innerHTML = ' ';
document.body.appendChild(h);
const d = document.createElement('div');
d.id = 'ml-dialog';
d.innerHTML = `✕ ${DESCRIPTION}
`;
document.body.appendChild(d);
const plist = document.createElement('div');
plist.id = 'ml-plist';
plist.innerHTML = '';
document.body.appendChild(plist);
const settings = document.createElement('div');
settings.id = 'ml-settings';
settings.innerHTML = `
`;
document.body.appendChild(settings);
const KB_LABELS = {
fly: 'Fly (toggle/up)',
flyDown: 'Fly Down',
setHome: 'Set Home',
home: 'Teleport Home',
back: 'Back Teleport',
cuddle: 'Cuddle Panel',
settings: 'Settings',
pets: 'Pet Browser',
slots: 'Waypoint Slots',
snipe: 'Pet Sniper',
attack: 'Attack',
help: 'Help / Description',
towerComplete: 'Tower Complete',
};
function keyCodeLabel(code) {
if (code === 'Space') return 'Space';
if (code === 'Backquote') return '`';
if (code.startsWith('Key')) return code.slice(3);
if (code.startsWith('Digit')) return code.slice(5);
return code;
}
function buildKeybindsHTML() {
let rows = '';
for (const [action, code] of Object.entries(KEYBINDS)) {
rows += '' + (KB_LABELS[action] || action) + ' ' + keyCodeLabel(code) + '
';
}
$('ml-keybinds-body').innerHTML = rows;
}
buildKeybindsHTML();
const petsPanel = document.createElement('div');
petsPanel.id = 'ml-pets';
petsPanel.innerHTML = '
';
document.body.appendChild(petsPanel);
const slotsPop = document.createElement('div');
slotsPop.id = 'ml-slots-pop';
slotsPop.innerHTML = 'Waypoint Slots ✕
';
document.body.appendChild(slotsPop);
function buildSlotsHTML() {
const body = $('ml-slots-pop-body');
if (!body) return;
let html = '';
for (let i = 0; i < 10; i++) {
const s = slots[i];
const info = s ? `${s.x.toFixed(1)}, ${s.y.toFixed(1)}, ${s.z.toFixed(1)}` : 'empty';
html += `${i} ${info} Go Set ✕
`;
}
body.innerHTML = html;
}
buildSlotsHTML();
$('ml-slots-pop-close').addEventListener('click', () => toggleSlots(false));
slotsPop.addEventListener('click', e => {
const goBtn = e.target.closest('.ml-slot-go');
const setBtn = e.target.closest('.ml-slot-set');
const clrBtn = e.target.closest('.ml-slot-clr');
if (!goBtn && !setBtn && !clrBtn) return;
const idx = parseInt((goBtn || setBtn || clrBtn).dataset.slot);
if (isNaN(idx)) return;
const p = getPlayer();
if (!p) return;
if (clrBtn) {
slots[idx] = null;
saveWaypoints();
flash('ml-slots');
buildSlotsHTML();
console.log(`[slot ${idx}] cleared`);
} else if (setBtn) {
slots[idx] = capturePos(p);
saveWaypoints();
flash('ml-slots');
buildSlotsHTML();
console.log(`[slot ${idx}] saved via popout`);
} else if (goBtn && slots[idx]) {
backPos = capturePos(p);
teleport(p, slots[idx]);
saveWaypoints();
flash('ml-slots');
toggleSlots(false);
console.log(`[slot ${idx}] teleported via popout`);
}
});
slotsPop.addEventListener('ml-rebuild', () => buildSlotsHTML());
const snipePanel = document.createElement('div');
snipePanel.id = 'ml-snipe';
snipePanel.innerHTML = 'Pet Sniper ✕
';
document.body.appendChild(snipePanel);
[plist, settings, d, petsPanel, slotsPop, snipePanel].forEach(el => {
['mousedown','mouseup','click','pointerdown','pointerup','mousemove','mouseover','mouseenter','mouseleave','wheel','contextmenu','pointermove','pointerover','pointerenter'].forEach(evt => {
el.addEventListener(evt, e => e.stopPropagation());
});
['keydown','keyup','keypress'].forEach(evt => {
el.addEventListener(evt, e => e.stopPropagation());
});
});
function makeDraggable(panel, handle) {
let dragging = false, ox = 0, oy = 0;
handle.style.cursor = 'grab';
panel.style.resize = 'both';
panel.style.overflow = 'auto';
function startDrag(cx, cy, tag) {
if (tag === 'BUTTON' || tag === 'INPUT' || tag === 'LABEL') return false;
dragging = true;
handle.style.cursor = 'grabbing';
const rect = panel.getBoundingClientRect();
panel.style.left = rect.left + 'px';
panel.style.top = rect.top + 'px';
panel.style.right = 'auto';
panel.style.bottom = 'auto';
panel.style.transform = 'none';
ox = cx - rect.left;
oy = cy - rect.top;
return true;
}
function moveDrag(cx, cy) {
if (!dragging) return;
const ph = panel.offsetHeight || 40;
const pw = panel.offsetWidth || 40;
panel.style.left = Math.max(0, Math.min(cx - ox, window.innerWidth - Math.min(pw, 40))) + 'px';
panel.style.top = Math.max(0, Math.min(cy - oy, window.innerHeight - Math.min(ph, 40))) + 'px';
}
function endDrag() {
if (dragging) { dragging = false; handle.style.cursor = 'grab'; }
}
handle.addEventListener('mousedown', e => { if (startDrag(e.clientX, e.clientY, e.target.tagName)) e.preventDefault(); });
window.addEventListener('mousemove', e => moveDrag(e.clientX, e.clientY), true);
window.addEventListener('mouseup', endDrag, true);
handle.addEventListener('touchstart', e => {
const t = e.touches[0];
if (startDrag(t.clientX, t.clientY, e.target.tagName)) { e.preventDefault(); e.stopPropagation(); }
}, { passive: false });
window.addEventListener('touchmove', e => {
if (!dragging) return;
moveDrag(e.touches[0].clientX, e.touches[0].clientY);
e.preventDefault();
}, { passive: false, capture: true });
window.addEventListener('touchend', endDrag, true);
panel._resetPos = () => {
panel.style.left = '';
panel.style.top = '';
panel.style.right = '';
panel.style.bottom = '';
panel.style.transform = '';
panel.style.width = '';
panel.style.height = '';
};
}
makeDraggable(plist, $('ml-plist-head'));
makeDraggable(settings, $('ml-settings-head'));
makeDraggable(petsPanel, $('ml-pets-head'));
makeDraggable(slotsPop, $('ml-slots-pop-head'));
makeDraggable(snipePanel, $('ml-snipe-head'));
makeDraggable(d.querySelector('#ml-inner'), d.querySelector('#ml-inner h1'));
function syncSettingsUI() {
syncSlider('ml-s-cap', 'ml-sv-cap', SPEED_CAP);
syncSlider('ml-s-accel', 'ml-sv-accel', ACCEL);
syncSlider('ml-s-base', 'ml-sv-base', SPEED_DEFAULT);
syncSlider('ml-s-acinterval', 'ml-sv-acinterval', autoCollectInterval, 's');
const ae = $('ml-s-accel-en');
if (ae) ae.classList.toggle('on', accelEnabled);
const featVals = [featFly, featSprint, featWaypoints, featCuddle, featCuddleFollow, featPets, featAutoLock, featAntiKnockback, featNoclip, featFreeMoney, featAutoCollect, featInvincible, featGhostMode, featFreeStars, featFreeDiamonds, featAutoAttack, featAutoFountain, featInvisible, featProTips];
FEAT_IDS.forEach((id, i) => { const el = $(id); if (el) el.classList.toggle('on', featVals[i]); });
}
syncSettingsUI();
function getPlayers() {
const holder = W.pc?.app?.root?.findByName('EnemyHolder');
if (!holder) return [];
const player = getPlayer();
const playerPos = player?.getPosition();
const results = [];
for (const enemy of holder.children) {
const pos = enemy.getPosition();
let username = _getEnemyName(enemy);
if (!username || username === 'Enemy') username = enemy.name !== 'Enemy' ? enemy.name : null;
if (!username) username = 'Player';
const dist = playerPos ? Math.floor(playerPos.distance(pos)) : '?';
results.push({ name: username, dist, pos: vec3(pos), entity: enemy });
}
results.sort((a, b) => (typeof a.dist === 'number' && typeof b.dist === 'number') ? a.dist - b.dist : 0);
return results;
}
function refreshPlayerList() {
const body = $('ml-plist-body');
if (!body) return;
const players = getPlayers();
if (players.length === 0) {
body.innerHTML = 'No other players found
';
return;
}
body.innerHTML = '';
players.forEach((p, i) => {
const row = document.createElement('div');
row.className = 'ml-prow';
row.innerHTML = `${i + 1} → `;
row.querySelector('.ml-pname').textContent = p.name;
row.addEventListener('click', () => {
const player = getPlayer();
if (!player) return;
const freshPos = p.entity?.getPosition();
const target = freshPos || p.pos;
backPos = capturePos(player);
teleport(player, vec3(target));
saveWaypoints();
if (featCuddleFollow) {
cuddleTarget = p.entity;
cuddling = true;
console.log(`[cuddle] locked → ${p.name}`);
}
flash('ml-back');
const app = W.pc?.app;
if (app) app.fire('GameManager:GameResumed');
});
body.appendChild(row);
});
}
function getPetOwnerId(pet) {
if (!pet) return null;
const d = pet.owner;
if (d === false) return false;
if (d != null) return d;
return pet?.data?.owner ?? pet?.ownerId ?? pet?.data?.ownerId ?? null;
}
let _ownerMapCache = null;
let _ownerMapTime = 0;
function buildOwnerMap() {
const now = Date.now();
if (_ownerMapCache && now - _ownerMapTime < 2000) return _ownerMapCache;
const map = {};
const myId = W.pc?.sessionId;
if (myId) map[myId] = 'Yours';
const nm = W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager;
if (nm?.sessionId && !map[nm.sessionId]) map[nm.sessionId] = 'Yours';
const holder = W.pc?.app?.root?.findByName('EnemyHolder');
if (holder) {
for (const child of holder.children) {
let id = child.id || child.sessionId || child.playerId;
if (!id && child.script) {
for (const sk of Object.keys(child.script._scriptsIndex || {})) {
const inst = child.script[sk];
id = inst?.id || inst?.sessionId || inst?.playerId;
if (id) break;
}
}
if (!id) continue;
const name = _getEnemyName(child);
if (name && name !== 'Enemy') map[id] = name;
}
}
_ownerMapCache = map;
_ownerMapTime = now;
return map;
}
function getPetOwnerName(pet, ownerMap) {
const ownerId = getPetOwnerId(pet);
if (ownerId === false || ownerId == null) return 'Wild';
if (ownerMap?.[ownerId]) return ownerMap[ownerId];
return ownerId.length > 10 ? ownerId.substring(0, 8) + '…' : ownerId;
}
function getPetsManager() { return _findPetsManager(); }
function getAllPets() {
const pm = getPetsManager();
if (!pm) return [];
hookPetSpawn();
const ownerMap = buildOwnerMap();
const pets = [];
const scan = (map, type) => {
if (!map) return;
map.forEach((pet, token) => {
const name = pet.name || String(token).substring(0, 8);
const sd = petSpawnData.get(token);
let mutation = sd?.mutation ?? '';
let rarity = sd?.rarity ?? '';
let profit = sd?.profit ?? null;
if (mutation === 'Default') mutation = '';
if (!mutation) {
try {
if (pet._mlMut) { mutation = pet._mlMut; }
else {
const mutNames = ['Golden', 'Diamond', 'Emerald', 'Rainbow', 'Galaxy'];
for (const mn of mutNames) {
if (pet.findByName?.(`Mutation ${mn} Effect`)) { mutation = mn; break; }
}
if (mutation) pet._mlMut = mutation;
}
} catch (_) {}
}
if (!rarity) {
try {
if (pet._mlRar) { rarity = pet._mlRar; }
else {
const RARITIES = ['BASIC', 'RARE', 'EPIC', 'LEGENDARY', 'MYTHICAL', 'EXOTIC', 'SECRET', 'EXCLUSIVE'];
for (const r of RARITIES) {
const node = pet.findByName?.(r);
if (node && node.enabled) { rarity = r; break; }
}
if (rarity) pet._mlRar = rarity;
}
} catch (_) {}
}
if (!profit) {
try {
const statsEl = pet.findByName?.('PetStats');
if (statsEl) {
const profitEl = statsEl.findByName?.('Profit') || statsEl.findByName?.('PetProfit');
if (profitEl?.element?.text) {
const m = profitEl.element.text.match(/\$\s*([\d,.]+)\s*([kmbtqsxi]*)/);
if (m) {
let v = parseFloat(m[1].replace(/,/g, ''));
const suf = m[2]?.toLowerCase();
if (suf === 'k') v *= 1e3;
else if (suf === 'm') v *= 1e6;
else if (suf === 'b') v *= 1e9;
else if (suf === 't') v *= 1e12;
else if (suf === 'q') v *= 1e15;
profit = v;
}
}
}
} catch (_) {}
}
const pos = pet.getPosition ? pet.getPosition() : pet.position;
const price = pet.price ?? 0;
pets.push({
token,
name,
price,
income: profit ?? 0,
owner: getPetOwnerName(pet, ownerMap),
ownerId: getPetOwnerId(pet),
type,
mutation,
rarity,
x: pos?.x ?? 0,
y: pos?.y ?? 0,
z: pos?.z ?? 0,
});
});
};
scan(pm.activePets, 'active');
scan(pm.basePets, 'base');
return pets;
}
let lastRenderedPets = [];
const grabbedTokens = new Map();
function renderPetTable() {
const body = $('ml-pets-body');
const countEl = $('ml-pets-count');
if (!body) return;
let pets = getAllPets();
if (countEl) countEl.textContent = `(${pets.length})`;
if (pets.length === 0) {
lastRenderedPets = [];
body.innerHTML = 'No pets found in this room
';
return;
}
if (petFilter) pets = pets.filter(p => petMatchesFilter(p, petFilter));
const playerPos = getPlayer()?.getPosition();
if (playerPos) pets.forEach(p => { p._dist = Math.hypot(p.x - playerPos.x, p.y - playerPos.y, p.z - playerPos.z); });
pets.sort((a, b) => {
const key = petSortCol === 'dist' ? '_dist' : petSortCol;
let av = a[key], bv = b[key];
if (av == null) av = Infinity;
if (bv == null) bv = Infinity;
if (typeof av === 'string') av = av.toLowerCase();
if (typeof bv === 'string') bv = bv.toLowerCase();
if (av < bv) return -petSortDir;
if (av > bv) return petSortDir;
return 0;
});
lastRenderedPets = pets;
const cols = ['name','mutation','rarity','owner','price','income','dist','go'];
const labels = { name:'Name', mutation:'Mutation', rarity:'Rarity', owner:'Owner', price:'Worth', income:'Income/s', dist:'Dist', go:'' };
let html = '';
cols.forEach(c => {
const cls = petSortCol === c ? (petSortDir === 1 ? 'sort-asc' : 'sort-desc') : '';
html += c === 'go' ? ' ' : `${labels[c]} `;
});
html += ' ';
const now = Date.now();
pets.forEach(p => {
const ownerCls = p.owner === 'Yours' ? 'own-you' : p.owner === 'Wild' ? 'own-wild' : '';
const mutCls = p.mutation ? `pet-mut-${p.mutation}` : 'pet-var';
const tokenStr = String(p.token);
const grabbed = grabbedTokens.has(tokenStr) && grabbedTokens.get(tokenStr) > now;
const grabBtn = p.owner !== 'Yours' ? `${grabbed ? '\u2714' : '\u270B'} ` : '';
html += `${esc(p.name)} ${esc(p.mutation) || '\u2014'} ${esc(p.rarity) || '\u2014'} ${esc(p.owner)} ${numFmt(p.price)} ${numFmt(p.income)}/s ${p._dist != null ? numFmt(Math.round(p._dist)) : '\u2014'} \u279C ${grabBtn} `;
});
html += '
';
body.innerHTML = html;
}
function findPetByToken(token) {
return lastRenderedPets.find(p => String(p.token) === token) || null;
}
$('ml-pets-body').addEventListener('click', e => {
const th = e.target.closest('th[data-col]');
if (th) {
const col = th.dataset.col;
if (petSortCol === col) petSortDir *= -1;
else { petSortCol = col; petSortDir = col === 'price' || col === 'income' || col === 'dist' ? -1 : 1; }
saveSettings();
renderPetTable();
return;
}
const tpBtn = e.target.closest('.pet-tp');
if (tpBtn) {
const p = findPetByToken(tpBtn.dataset.token);
if (!p) return;
const player = getPlayer();
if (!player) return;
player.setPosition(p.x, p.y + 1, p.z);
togglePetsPanel(false);
return;
}
const grabBtn = e.target.closest('.pet-grab');
if (grabBtn) {
const p = findPetByToken(grabBtn.dataset.token);
if (!p) return;
const app = W.pc?.app;
if (!app) return;
const player = getPlayer();
if (!player) return;
const tokenStr = String(p.token);
const orig = player.getPosition().clone();
player.setPosition(p.x, p.y + 0.5, p.z);
grabBtn.disabled = true;
grabBtn.textContent = '…';
setTimeout(() => {
app.fire('ModeOverlay:BuyPet', p.token);
console.log(`[ml] grab pet: ${p.name} (${p.token})`);
setTimeout(() => {
player.setPosition(orig.x, orig.y, orig.z);
setTimeout(() => {
app.fire('NetworkManager:Send', 'dropPet');
console.log(`[ml] drop pet at origin`);
grabbedTokens.set(tokenStr, Date.now() + 5000);
renderPetTable();
}, 150);
}, 150);
}, 100);
}
});
function esc(s) {
return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');
}
function numFmt(n) {
if (n >= 1e15) return (n / 1e15).toFixed(1) + 'q';
if (n >= 1e12) return (n / 1e12).toFixed(1) + 't';
if (n >= 1e9) return (n / 1e9).toFixed(1) + 'b';
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'm';
if (n >= 1e3) return (n / 1e3).toFixed(1) + 'k';
return n % 1 === 0 ? String(n) : n.toFixed(1);
}
function petMatchesFilter(p, raw) {
if (!raw) return true;
const text = [p.name, p.owner, p.mutation, p.rarity].join(' ').toLowerCase();
const tokens = raw.match(/\(|\)|AND|OR|[^\s()]+/gi) || [];
function parseOr(i) {
let [result, j] = parseAnd(i);
while (j < tokens.length && tokens[j]?.toUpperCase() === 'OR') {
const [r2, j2] = parseAnd(j + 1);
result = result || r2;
j = j2;
}
return [result, j];
}
function parseAnd(i) {
let [result, j] = parseAtom(i);
while (j < tokens.length) {
const up = tokens[j]?.toUpperCase();
if (up === 'AND') { j++; }
else if (up === 'OR' || tokens[j] === ')') break;
const [r2, j2] = parseAtom(j);
result = result && r2;
j = j2;
}
return [result, j];
}
function parseAtom(i) {
if (i >= tokens.length) return [false, i];
if (tokens[i] === '(') {
const [result, j] = parseOr(i + 1);
return [result, j < tokens.length && tokens[j] === ')' ? j + 1 : j];
}
const t = tokens[i].toLowerCase();
if (t.includes('*') || t.includes('%')) {
const re = new RegExp(t.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/[*%]/g, '.*'));
return [re.test(text), i + 1];
}
return [text.includes(t), i + 1];
}
try { return parseOr(0)[0]; } catch (_) { return text.includes(raw.toLowerCase()); }
}
let petRefreshTimer = null;
function startPetRefresh() {
stopPetRefresh();
petRefreshTimer = setInterval(() => renderPetTable(), petRefreshInterval * 1000);
}
function stopPetRefresh() {
if (petRefreshTimer) { clearInterval(petRefreshTimer); petRefreshTimer = null; }
}
function togglePetsPanel(forceOpen) {
const open = forceOpen !== undefined ? forceOpen : !petsPanel.classList.contains('open');
petsPanel.classList.toggle('open', open);
if (open) {
$('ml-pets-search').value = petFilter;
$('ml-pets-auto').checked = petAutoRefresh;
syncSlider('ml-pets-interval', 'ml-pets-iv', petRefreshInterval);
renderPetTable();
if (petAutoRefresh) startPetRefresh();
panelPause();
} else {
stopPetRefresh();
petsPanel._resetPos?.();
panelResume();
}
}
$('ml-pets-btn').addEventListener('click', () => { togglePetsPanel(); });
$('ml-pets-close').addEventListener('click', () => togglePetsPanel(false));
$('ml-pets-search').addEventListener('input', e => {
petFilter = e.target.value;
saveSettings();
renderPetTable();
});
$('ml-pets-auto').addEventListener('change', e => {
petAutoRefresh = e.target.checked;
saveSettings();
if (petAutoRefresh && petsPanel.classList.contains('open')) startPetRefresh();
else stopPetRefresh();
});
$('ml-pets-interval').addEventListener('input', e => {
petRefreshInterval = parseInt(e.target.value);
const iv = $('ml-pets-iv');
if (iv) iv.textContent = petRefreshInterval;
saveSettings();
if (petAutoRefresh && petsPanel.classList.contains('open')) startPetRefresh();
});
let _snipeTimer = null;
let _snipeDropInProgress = false;
function isOwnerBaseLocked(ownerId) {
if (!ownerId) return false;
if (_serverLockedSessions.has(ownerId)) return true;
const bm = _getBasesManager();
if (bm?.activeBases) {
for (const bd of bm.activeBases) {
if (bd.data?.sessionId !== ownerId) continue;
return !!(bd.data.lockdownActive || bd.data.lockdownTimeLeft > 0);
}
}
return false;
}
const _snipedPets = [];
const SNIPE_HISTORY_CAP = 200;
function recordSnipe(token) {
_snipedPets.push({ token: String(token), time: Date.now() });
while (_snipedPets.length > SNIPE_HISTORY_CAP) _snipedPets.shift();
}
function getSnipeTime(token) {
const t = String(token);
for (let i = _snipedPets.length - 1; i >= 0; i--) {
if (_snipedPets[i].token === t) return _snipedPets[i].time;
}
return 0;
}
function snipeLog(msg) {
const log = $('ml-snipe-log');
if (!log) return;
const entry = document.createElement('div');
entry.className = 'ml-snipe-entry';
entry.textContent = new Date().toLocaleTimeString() + ' ' + msg;
log.prepend(entry);
while (log.children.length > 30) log.lastChild.remove();
}
function snipeScan() {
if (!snipeFilter) {
$('ml-snipe-status').textContent = 'No filter set';
$('ml-snipe-types').textContent = '';
return null;
}
const app = W.pc?.app;
const player = getPlayer();
if (!app || !player) {
$('ml-snipe-status').textContent = 'Waiting for game...';
return null;
}
const nm = _getNetworkManager();
const myId = nm?.room?.sessionId || W.pc?.sessionId;
const bm = _getBasesManager();
const isStealMode = !!(bm?.activeBases);
const pets = getAllPets();
const nameMatches = pets.filter(p => {
if (p.ownerId === myId || p.owner === 'Yours') return false;
return petMatchesFilter(p, snipeFilter);
});
let blocked = 0;
const matches = nameMatches.filter(p => {
if (isStealMode) {
if (p.owner === 'Wild' || !p.ownerId) { blocked++; return false; }
if (p.type === 'active') { blocked++; return false; }
if (isOwnerBaseLocked(p.ownerId)) { blocked++; return false; }
return true;
} else {
if (p.owner !== 'Wild' && p.ownerId) { blocked++; return false; }
return true;
}
});
const blockedStr = blocked ? ' (' + blocked + ' locked)' : '';
$('ml-snipe-status').textContent = (isStealMode ? '[Steal] ' : '[Waves] ') + matches.length + ' target(s) / ' + nameMatches.length + ' matched' + blockedStr;
const typeCounts = {};
for (const m of nameMatches) {
const variant = [m.rarity, m.mutation, m.name].filter(Boolean).join(' ');
const isLocked = isStealMode && m.ownerId && isOwnerBaseLocked(m.ownerId);
if (!typeCounts[variant]) typeCounts[variant] = { total: 0, locked: 0 };
typeCounts[variant].total++;
if (isLocked) typeCounts[variant].locked++;
}
const typesEl = $('ml-snipe-types');
if (typesEl) {
const entries = Object.entries(typeCounts).sort((a, b) => b[1].total - a[1].total);
typesEl.innerHTML = '';
for (const [k, v] of entries) {
const row = document.createElement('div');
row.className = 'ml-snipe-type-row';
let label = k + (v.total > 1 ? ' \u00d7' + v.total : '');
if (v.locked > 0) label += ' \ud83d\udd12' + v.locked;
row.textContent = label;
typesEl.appendChild(row);
}
}
return { matches, isStealMode, totalPets: pets.length, blocked };
}
let _snipeScanTimer = null;
function snipeTick() {
if (!snipeActive) return;
if (_hopInProgress) return;
const now = Date.now();
if (now < _snipeCooldown) {
$('ml-snipe-status').textContent = 'Cooldown ' + (((_snipeCooldown - now) / 1000) | 0) + 's...';
return;
}
const app = W.pc?.app;
const pt = _getPetTycoon();
const et = _getEscapeTsunami();
const holding = !!(pt?.isHoldingPet || et?.isHoldingPet);
if (_snipeWasHolding && !holding) {
_snipeCooldown = Date.now() + 10000;
console.log('[ml] pet deposited — 10s snipe cooldown');
}
_snipeWasHolding = holding;
if (holding) {
$('ml-snipe-status').textContent = 'Holding a pet \u2014 paused';
return;
}
const scan = snipeScan();
if (!scan || scan.matches.length === 0) {
if (snipeAutoHop && scan && scan.isStealMode && snipeFilter && scan.totalPets > 0 && !scan.blocked) {
if (!_snipeHopNoMatchStart) _snipeHopNoMatchStart = Date.now();
const elapsed = ((Date.now() - _snipeHopNoMatchStart) / 1000) | 0;
if (elapsed >= 5) {
snipeLog('No matches (' + scan.totalPets + ' pets) — hopping...');
$('ml-snipe-status').textContent = 'Hopping server...';
_snipeHopNoMatchStart = 0;
performServerHop();
return;
}
$('ml-snipe-status').textContent = 'No matches (' + scan.totalPets + ' pets loaded)';
}
return;
}
_snipeHopNoMatchStart = 0;
const { matches, isStealMode } = scan;
const player = getPlayer();
if (!player) return;
const pPos = player.getPosition();
for (const m of matches) {
m._dist = Math.hypot(m.x - pPos.x, m.y - pPos.y, m.z - pPos.z);
m._snipeTime = getSnipeTime(m.token);
m._value = Math.max(m.price || 0, m.income || 0);
}
matches.sort((a, b) => {
if (!isStealMode) {
if (!a._snipeTime && b._snipeTime) return -1;
if (a._snipeTime && !b._snipeTime) return 1;
if (a._snipeTime && b._snipeTime) return a._snipeTime - b._snipeTime;
}
if (isStealMode) {
if (b.income !== a.income) return b.income - a.income;
}
if (b._value !== a._value) return b._value - a._value;
return a._dist - b._dist;
});
const target = matches[0];
$('ml-snipe-status').textContent = 'Sniping: ' + target.name + '...';
snipeLog('Sniping ' + target.name + ' (' + (target.owner || 'wild') + ') dist=' + Math.round(target._dist));
if (isStealMode) {
if (!_lockDataReceived) {
$('ml-snipe-status').textContent = 'Waiting for lock data...';
return;
}
if (target.ownerId && isOwnerBaseLocked(target.ownerId)) {
snipeLog(target.name + ': base locked at last second, skipping');
$('ml-snipe-status').textContent = 'Base locked — skipping...';
_snipeCooldown = Date.now() + 500;
return;
}
const nm = _getNetworkManager();
const room = nm?.room;
const sid = room?.sessionId;
if (!room || !sid) return;
const rawSend = _rawSend(room);
const bm = _getBasesManager();
const maxSlots = bm?.myBase?.data?.places || 10;
const myBasePets = getAllPets().filter(p => p.ownerId === sid && p.type === 'base');
if (myBasePets.length >= maxSlots) {
myBasePets.sort((a, b) => a.income - b.income);
const lowest = myBasePets[0];
if (target.income <= lowest.income) {
snipeLog('Base full (' + myBasePets.length + '/' + maxSlots + '), target income ' + target.income + ' \u2264 lowest ' + lowest.income + ' — skip');
$('ml-snipe-status').textContent = 'Base full — target not worth it';
_snipeCooldown = Date.now() + 2000;
return;
}
rawSend('sellPet', lowest.token);
snipeLog('Auto-sold ' + lowest.name + ' (income ' + lowest.income + ') to make room for ' + target.name + ' (income ' + target.income + ')');
console.log('[ml] snipe auto-sell:', lowest.name, lowest.token, 'income=' + lowest.income);
}
_ghostSuppressed = true;
player.setPosition(target.x, target.y + 0.5, target.z);
const posMsg = { x: target.x, y: target.y + 0.5, z: target.z, w: 0 };
rawSend('p', posMsg);
recordSnipe(target.token);
snipeLog('TP\u2019d to ' + target.name + ' — stealing...');
$('ml-snipe-status').textContent = 'Stealing ' + target.name + '...';
_snipeCooldown = Date.now() + 8000;
for (let i = 1; i <= 4; i++) setTimeout(() => rawSend('p', posMsg), i * 80);
setTimeout(() => {
_ghostSuppressed = false;
room.send('stealPet', target.token);
console.log('[ml] snipe steal sent:', target.name, target.token);
}, 400);
} else {
const orig = player.getPosition().clone();
const nm = _getNetworkManager();
const room = nm?.room;
const sid = room?.sessionId;
if (!room || !sid) return;
const rawSend = _rawSend(room);
_snipeCooldown = Date.now() + 5000;
if (featGhostMode) {
_wavesGhostPos = { x: target.x, y: target.y + 0.5, z: target.z };
} else {
player.setPosition(target.x, target.y + 0.5, target.z);
}
(async () => {
await new Promise(r => setTimeout(r, 150));
app.fire('ModeOverlay:BuyPet', target.token);
recordSnipe(target.token);
console.log('[ml] snipe grab:', target.name, target.token);
const grabbed = await new Promise(resolve => {
const timer = setTimeout(() => { unsub(); resolve(false); }, 600);
const unsub = room.onMessage('petHeld', msg => {
if (msg.holderSessionId !== sid) return;
clearTimeout(timer); unsub(); resolve(true);
});
});
if (!grabbed) {
_wavesGhostPos = null;
if (!featGhostMode) player.setPosition(orig.x, orig.y, orig.z);
snipeLog(target.name + ': grab timeout');
$('ml-snipe-status').textContent = 'Grab failed. Retrying...';
_snipeCooldown = Date.now() + 500;
return;
}
_wavesGhostPos = null;
if (!featGhostMode) player.setPosition(orig.x, orig.y, orig.z);
if (snipeAutoDrop) {
_snipeDropInProgress = true;
const realPos = player.getPosition();
rawSend('p', { x: realPos.x, y: realPos.y, z: realPos.z, w: 0 });
let dropped = false;
for (let att = 0; att < 4 && !dropped; att++) {
await new Promise(r => setTimeout(r, att === 0 ? 200 : 500));
const et = _getEscapeTsunami();
if (!et?.isHoldingPet) { dropped = true; break; }
const dp = new Promise(resolve => {
const t = setTimeout(() => { u(); resolve(false); }, 800);
const u = room.onMessage('escapeTsunamiPetDropped', msg => {
if (msg.holderSessionId !== sid) return;
clearTimeout(t); u(); resolve(true);
});
});
rawSend('dropPet');
if (att > 0) rawSend('p', { x: realPos.x, y: realPos.y, z: realPos.z, w: 0 });
dropped = await dp;
if (!dropped) console.log('[ml] drop attempt', att + 1, 'failed');
}
_snipeDropInProgress = false;
if (!dropped) console.log('[ml] drop failed after retries — isHolding:', _getEscapeTsunami()?.isHoldingPet);
snipeLog('Sniped ' + target.name + (dropped ? ' ✓ dropped' : ' (drop failed!)'));
$('ml-snipe-status').textContent = dropped ? 'Dropped ' + target.name : 'Drop failed!';
_snipeCooldown = Date.now() + 300;
} else {
snipeLog('Sniped ' + target.name + ' ✓ — place on base!');
$('ml-snipe-status').textContent = 'Holding ' + target.name + '! Place on base.';
_snipeCooldown = Date.now() + 1000;
}
})();
}
}
let _heldPetCleanup = null;
function heldPetCleanupTick() {
if (!snipeActive || !snipeAutoDrop || _snipeDropInProgress) return;
const et = _getEscapeTsunami();
if (!et?.isHoldingPet) return;
const nm = _getNetworkManager();
const room = nm?.room;
if (!room) return;
const rawSend = _rawSend(room);
const pos = getPlayer()?.getPosition();
if (pos) rawSend('p', { x: pos.x, y: pos.y, z: pos.z, w: 0 });
rawSend('dropPet');
console.log('[ml] held-pet cleanup: forced drop');
}
function startSnipe() {
if (_snipeTimer) return;
_snipeTimer = setInterval(snipeTick, 200);
if (!_heldPetCleanup) _heldPetCleanup = setInterval(heldPetCleanupTick, 3000);
snipeLog('Sniping started: ' + snipeFilter);
}
function stopSnipe() {
if (_snipeTimer) { clearInterval(_snipeTimer); _snipeTimer = null; }
if (_heldPetCleanup) { clearInterval(_heldPetCleanup); _heldPetCleanup = null; }
}
function toggleSnipePanel(forceOpen) {
const wasOpen = snipePanel.classList.contains('open');
if (wasOpen && forceOpen === undefined && document.pointerLockElement) {
panelPause();
return;
}
const open = forceOpen !== undefined ? forceOpen : !wasOpen;
snipePanel.classList.toggle('open', open);
if (open) {
if (document.pointerLockElement) panelPause();
$('ml-snipe-search').value = snipeFilter;
$('ml-snipe-drop').checked = snipeAutoDrop;
$('ml-snipe-hop').checked = snipeAutoHop;
$('ml-snipe-toggle').textContent = snipeActive ? 'Stop Sniping' : 'Start Sniping';
$('ml-snipe-toggle').classList.toggle('active', snipeActive);
snipeScan();
if (!_snipeScanTimer && !_snipeTimer) _snipeScanTimer = setInterval(snipeScan, 2000);
} else if (wasOpen) {
if (_snipeScanTimer) { clearInterval(_snipeScanTimer); _snipeScanTimer = null; }
snipePanel._resetPos?.();
panelResume();
}
}
document.addEventListener('mousedown', e => {
if (!snipePanel.classList.contains('open')) return;
if (document.pointerLockElement) return;
if (snipePanel.contains(e.target)) return;
const app = W.pc?.app;
const canvas = app?.graphicsDevice?.canvas;
if (canvas) origRequestPointerLock.call(canvas, {unadjustedMovement: true});
});
$('ml-snipe-btn').addEventListener('click', () => { toggleSnipePanel(); });
$('ml-snipe-close').addEventListener('click', () => toggleSnipePanel(false));
$('ml-snipe-search').addEventListener('input', e => {
snipeFilter = e.target.value;
saveSettings();
});
$('ml-snipe-drop').addEventListener('change', e => {
snipeAutoDrop = e.target.checked;
saveSettings();
});
$('ml-snipe-hop').addEventListener('change', e => {
snipeAutoHop = e.target.checked;
if (snipeAutoHop) sessionStorage.setItem('ml_snipeAutoHop', '1');
else sessionStorage.removeItem('ml_snipeAutoHop');
_snipeHopNoMatchStart = 0;
});
$('ml-snipe-toggle').addEventListener('click', () => {
snipeActive = !snipeActive;
saveSettings();
$('ml-snipe-toggle').textContent = snipeActive ? 'Stop Sniping' : 'Start Sniping';
$('ml-snipe-toggle').classList.toggle('active', snipeActive);
if (snipeActive) startSnipe();
else { stopSnipe(); $('ml-snipe-status').textContent = 'Idle'; }
});
if (snipeActive) {
$('ml-snipe-toggle').textContent = 'Stop Sniping';
$('ml-snipe-toggle').classList.add('active');
startSnipe();
}
function toggleCuddlePanel(forceOpen) {
const wasOpen = plist.classList.contains('open');
const open = forceOpen !== undefined ? forceOpen : !wasOpen;
plist.classList.toggle('open', open);
if (open) {
refreshPlayerList();
panelPause();
if (!wasOpen && autoRefresh) startRefreshTimer();
} else if (wasOpen) {
stopRefreshTimer();
plist._resetPos?.();
panelResume();
}
}
const origRequestPointerLock = Element.prototype.requestPointerLock;
Element.prototype.requestPointerLock = function() {
if (anyPanelOpen()) {
if (W.pc?.isGamePaused) {
for (const id of ['ml-plist', 'ml-settings', 'ml-pets', 'ml-dialog', 'ml-slots-pop', 'ml-snipe']) {
$(id)?.classList.remove('open');
}
_savedPointerLock = false;
} else {
return;
}
}
return origRequestPointerLock.apply(this, arguments);
};
const origRequestFullscreen = Element.prototype.requestFullscreen;
Element.prototype.requestFullscreen = function(opts) {
return origRequestFullscreen.call(document.body, opts);
};
document.addEventListener('pointerlockchange', e => {
const wasIntentional = _intentionalPointerExit;
_intentionalPointerExit = false;
if (wasIntentional && !document.pointerLockElement) {
e.stopImmediatePropagation();
return;
}
if (document.pointerLockElement) {
const app = W.pc?.app;
if (app) app.fire('GameManager:GameResumed');
}
}, true);
document.addEventListener('visibilitychange', () => {
if (document.hidden) _intentionalPointerExit = false;
});
let refreshCountdown = refreshInterval;
let refreshTimer = null;
function startRefreshTimer() {
stopRefreshTimer();
refreshCountdown = refreshInterval;
const timerEl = $('ml-plist-timer');
if (timerEl) timerEl.textContent = refreshCountdown + 's';
refreshTimer = setInterval(() => {
refreshCountdown--;
if (timerEl) timerEl.textContent = refreshCountdown + 's';
if (refreshCountdown <= 0) {
refreshPlayerList();
refreshCountdown = refreshInterval;
if (timerEl) timerEl.textContent = refreshCountdown + 's';
}
}, 1000);
}
function stopRefreshTimer() {
if (refreshTimer) { clearInterval(refreshTimer); refreshTimer = null; }
}
$('ml-invis').addEventListener('click', () => {
featInvisible = !featInvisible;
_toggleInvisible(featInvisible);
_updateInvisEffect(featInvisible);
const el = $('ml-f-invisible');
if (el) el.classList.toggle('on', featInvisible);
saveSettings();
});
$('ml-tc').addEventListener('click', () => { _towerComplete(); });
$('ml-tp').addEventListener('click', () => { toggleCuddlePanel(); });
$('ml-plist-refresh').addEventListener('click', () => {
refreshPlayerList();
if (autoRefresh && plist.classList.contains('open')) startRefreshTimer();
});
$('ml-plist-close').addEventListener('click', () => toggleCuddlePanel(false));
function toggleSettings(forceOpen) {
const open = forceOpen !== undefined ? forceOpen : !settings.classList.contains('open');
settings.classList.toggle('open', open);
if (open) { panelPause(); refreshUsernameUI(); }
else if (!open) { kbListeningRow = null; settings._resetPos?.(); panelResume(); }
}
$('ml-cfg').addEventListener('click', () => { toggleSettings(); });
$('ml-settings-close').addEventListener('click', () => toggleSettings(false));
document.querySelectorAll('#ml-tabs .ml-tab').forEach(tab => {
tab.addEventListener('click', () => {
const t = tab.dataset.tab;
document.querySelectorAll('#ml-tabs .ml-tab').forEach(x => x.classList.toggle('active', x.dataset.tab === t));
document.querySelectorAll('#ml-settings-body .ml-tab-content').forEach(x => x.classList.toggle('active', x.dataset.tab === t));
if (t === 'keys') buildKeybindsHTML();
else { kbListeningRow = null; $('ml-keybinds-body').querySelectorAll('.ml-krow.listening').forEach(r => r.classList.remove('listening')); }
});
});
const DEFAULTS = { ACCEL: 6, SPEED_CAP: 100, SPEED_DEFAULT: 7, petRefreshInterval: 1, petFilter: 'wild', petSortCol: 'price', petSortDir: -1 };
$('ml-settings-reset').addEventListener('click', () => {
ACCEL = DEFAULTS.ACCEL;
SPEED_CAP = DEFAULTS.SPEED_CAP;
SPEED_DEFAULT = DEFAULTS.SPEED_DEFAULT;
petRefreshInterval = DEFAULTS.petRefreshInterval;
petFilter = DEFAULTS.petFilter;
petSortCol = DEFAULTS.petSortCol;
petSortDir = DEFAULTS.petSortDir;
accelEnabled = true;
featFly = true; featSprint = true; featWaypoints = true; featCuddle = true; featCuddleFollow = true; featPets = true; featAutoLock = true;
featAntiKnockback = true; featNoclip = false; featFreeMoney = true; featAutoCollect = true; featInvincible = true; featGhostMode = true;
featFreeStars = true; featFreeDiamonds = true; featAutoAttack = true; featAutoFountain = true; featInvisible = false; featProTips = true;
autoCollectInterval = 30;
Object.assign(KEYBINDS, DEFAULT_KEYBINDS);
syncSettingsUI();
buildKeybindsHTML();
updateHUDBadgeLabels();
console.log('[settings] reset');
saveSettings();
});
const sliderMap = [
{ id: 'ml-s-cap', valId: 'ml-sv-cap', apply: v => { SPEED_CAP = v; } },
{ id: 'ml-s-accel', valId: 'ml-sv-accel', apply: v => { ACCEL = v; } },
{ id: 'ml-s-base', valId: 'ml-sv-base', apply: v => { SPEED_DEFAULT = v; } },
{ id: 'ml-s-acinterval', valId: 'ml-sv-acinterval', uom: 's', apply: v => { autoCollectInterval = v; } },
];
sliderMap.forEach(s => {
const el = $(s.id);
const valEl = $(s.valId);
if (!el) return;
el.addEventListener('input', () => {
const v = parseInt(el.value);
if (valEl) valEl.textContent = s.uom ? v + s.uom : v;
s.apply(v);
saveSettings();
});
});
const accelToggle = $('ml-s-accel-en');
if (accelToggle) {
accelToggle.addEventListener('click', () => {
accelEnabled = !accelEnabled;
accelToggle.classList.toggle('on', accelEnabled);
saveSettings();
});
}
const featToggles = [
{ id: 'ml-f-fly', get: () => featFly, set: v => { featFly = v; if (!v) { const p = getPlayer(); const k = getKcc(p); if (k && flyActive) flyOff(k); } } },
{ id: 'ml-f-sprint', get: () => featSprint, set: v => { featSprint = v; if (!v) sprinting = false; } },
{ id: 'ml-f-waypoints', get: () => featWaypoints, set: v => { featWaypoints = v; } },
{ id: 'ml-f-cuddle', get: () => featCuddle, set: v => { featCuddle = v; if (!v) toggleCuddlePanel(false); } },
{ id: 'ml-f-cuddle-follow', get: () => featCuddleFollow, set: v => { featCuddleFollow = v; if (!v) { cuddling = false; cuddleTarget = null; } } },
{ id: 'ml-f-pets', get: () => featPets, set: v => { featPets = v; if (!v) togglePetsPanel(false); } },
{ id: 'ml-f-autolock', get: () => featAutoLock, set: v => { featAutoLock = v; } },
{ id: 'ml-f-antiknockback', get: () => featAntiKnockback, set: v => { featAntiKnockback = v; if (!v) antiKnockbackReady = false; } },
{ id: 'ml-f-noclip', get: () => featNoclip, set: v => { featNoclip = v; } },
{ id: 'ml-f-freemoney', get: () => featFreeMoney, set: v => { featFreeMoney = v; if (v && !freeMoneyDone) autoFarmFreeMoney(); } },
{ id: 'ml-f-autocollect', get: () => featAutoCollect, set: v => { featAutoCollect = v; } },
{ id: 'ml-f-invincible', get: () => featInvincible, set: v => { featInvincible = v; } },
{ id: 'ml-f-ghostmode', get: () => featGhostMode, set: v => { featGhostMode = v; if (!v) updateGhostEffect(); } },
{ id: 'ml-f-freestars', get: () => featFreeStars, set: v => { featFreeStars = v; if (v) _freeStarsStart(); else _freeStarsStop(); } },
{ id: 'ml-f-freediamonds', get: () => featFreeDiamonds, set: v => { featFreeDiamonds = v; if (v) _freeDiamondsStart(); else _freeDiamondsStop(); } },
{ id: 'ml-f-autoattack', get: () => featAutoAttack, set: v => { featAutoAttack = v; } },
{ id: 'ml-f-autofountain', get: () => featAutoFountain, set: v => { featAutoFountain = v; if (v) _fountainStart(); else _fountainStop(); } },
{ id: 'ml-f-invisible', get: () => featInvisible, set: v => { featInvisible = v; _toggleInvisible(v); _updateInvisEffect(v); } },
{ id: 'ml-f-protips', get: () => featProTips, set: v => { featProTips = v; } },
];
featToggles.forEach(ft => {
const el = $(ft.id);
if (!el) return;
el.addEventListener('click', () => {
const nv = !ft.get();
ft.set(nv);
el.classList.toggle('on', nv);
saveSettings();
console.log(`[feat] ${ft.id.replace('ml-f-','')} = ${nv}`);
});
});
function refreshUsernameUI() {
const statusEl = $('ml-uname-status');
const btnEl = $('ml-uname-btn');
const inputEl = $('ml-uname-input');
if (!statusEl) return;
const nm = _getNetworkManager();
const me = nm?.room?.state?.players?.get?.(nm?.room?.sessionId);
const apiMgr = W.pc?.app?.root?.findByName('APIManager')?.script?.apimanager;
const canChange = !!apiMgr?.accountData?.canChangeName;
statusEl.textContent = me?.username ? (canChange ? me.username + ' (1 free change)' : me.username + ' (used)') : '…';
statusEl.style.color = canChange ? 'rgba(140,230,140,.7)' : 'rgba(255,150,150,.6)';
if (btnEl) btnEl.disabled = !canChange;
if (btnEl) btnEl.style.opacity = canChange ? '1' : '.4';
if (inputEl) inputEl.disabled = !canChange;
}
$('ml-uname-btn')?.addEventListener('click', () => {
const input = $('ml-uname-input');
const name = input?.value?.trim();
if (!name) return;
if (name.length < 3 || name.length > 12) {
$('ml-uname-status').textContent = 'Must be 3-12 characters';
$('ml-uname-status').style.color = 'rgba(255,150,150,.8)';
return;
}
const token = localStorage.getItem('access_token_v0.01');
if (!token) { $('ml-uname-status').textContent = 'Not logged in'; return; }
const btn = $('ml-uname-btn');
btn.disabled = true;
btn.textContent = '…';
fetch('https://api.meeland.io/v1/users/changeName', {
method: 'POST',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
body: JSON.stringify({ name })
}).then(r => {
if (!r.ok) throw new Error(r.status === 400 || r.status === 403 ? 'Change unavailable' : 'Error ' + r.status);
return r.text().then(() => null);
}).then(() => {
console.log('[ml] username changed to: ' + name);
const app = W.pc?.app;
if (app) app.fire('NetworkManager:Send', 'refreshUsername');
input.value = '';
$('ml-uname-status').textContent = name + ' ✓';
$('ml-uname-status').style.color = 'rgba(140,230,140,.8)';
btn.textContent = 'Change';
btn.disabled = true;
btn.style.opacity = '.4';
}).catch(e => {
console.error('[ml] username change failed:', e.message);
$('ml-uname-status').textContent = e.message;
$('ml-uname-status').style.color = 'rgba(255,150,150,.8)';
btn.textContent = 'Change';
btn.disabled = false;
});
});
$('ml-keybinds-body').addEventListener('click', e => {
const row = e.target.closest('.ml-krow');
if (!row) return;
$('ml-keybinds-body').querySelectorAll('.ml-krow.listening').forEach(r => r.classList.remove('listening'));
if (kbListeningRow === row) { kbListeningRow = null; return; }
row.classList.add('listening');
row.querySelector('.ml-krow-key').textContent = '...';
kbListeningRow = row;
});
window.addEventListener('keydown', e => {
if (!kbListeningRow) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const action = kbListeningRow.dataset.action;
if (!action || !(action in KEYBINDS)) return;
if (e.code === 'Escape') {
kbListeningRow.classList.remove('listening');
kbListeningRow.querySelector('.ml-krow-key').textContent = keyCodeLabel(KEYBINDS[action]);
kbListeningRow = null;
return;
}
KEYBINDS[action] = e.code;
kbListeningRow.querySelector('.ml-krow-key').textContent = keyCodeLabel(e.code);
kbListeningRow.classList.remove('listening');
kbListeningRow = null;
saveSettings();
updateHUDBadgeLabels();
console.log('[keybind] ' + action + ' → ' + e.code);
}, true);
function updateHUDBadgeLabels() {
const map = {
'ml-fly': ['FLY', 'fly'],
'ml-spr': ['SPR', null],
'ml-home': ['SETHOME', 'setHome'],
'ml-go': ['HOME', 'home'],
'ml-back': ['BACK', 'back'],
'ml-tp': ['CUDDLE', 'cuddle'],
'ml-pets-btn': ['PETS', 'pets'],
'ml-snipe-btn': ['SNIPE', 'snipe'],
'ml-invis': ['INVIS', 'invisible'],
'ml-cfg': ['CFG', 'settings'],
'ml-tc': ['TC', 'towerComplete'],
};
const nativeKeyMap = { 'ml-fly': 'fly', 'ml-spr': 'spr', 'ml-home': 'home', 'ml-go': 'go', 'ml-back': 'back', 'ml-invis': 'invis', 'ml-tp': 'tp', 'ml-pets-btn': 'petsBtn', 'ml-snipe-btn': 'snipeBtn', 'ml-cfg': 'cfg', 'ml-tc': 'tc' };
for (const [id, [label, action]] of Object.entries(map)) {
if (_nativeHud) {
const k = nativeKeyMap[id];
const kl = k && _nativeHud[k]?._native?.keyLbl;
if (kl?.element) {
kl.element.text = !action ? 'SH' : keyBadge(KEYBINDS[action]);
}
}
}
}
updateHUDBadgeLabels();
function toggleHelp(forceOpen) {
const open = forceOpen !== undefined ? forceOpen : !d.classList.contains('open');
d.classList.toggle('open', open);
if (open) { panelPause(); }
else { d.querySelector('#ml-inner')._resetPos?.(); panelResume(); }
}
$('ml-help').addEventListener('click', () => { toggleHelp(); });
$('ml-close').addEventListener('click', () => toggleHelp(false));
d.addEventListener('click', e => { if (e.target === d) toggleHelp(false); });
d.addEventListener('wheel', e => { e.stopPropagation(); e.preventDefault(); d.scrollTop += e.deltaY; }, { passive: false });
function enableTouchScroll(el) {
el.addEventListener('touchstart', e => e.stopPropagation(), { passive: true });
el.addEventListener('touchmove', e => e.stopPropagation(), { passive: true });
el.addEventListener('touchend', e => e.stopPropagation(), { passive: true });
}
[d, $('ml-plist-body'), $('ml-settings-body'),
$('ml-pets-body'), $('ml-slots-pop-body'),
$('ml-snipe-types'), $('ml-snipe-log')].forEach(el => { if (el) enableTouchScroll(el); });
document.addEventListener('click', e => {
for (const [el, trigId, fn] of [
[plist, 'ml-tp', toggleCuddlePanel],
[settings, 'ml-cfg', toggleSettings],
[petsPanel, 'ml-pets-btn', togglePetsPanel],
[slotsPop, 'ml-slots', toggleSlots],
]) {
if (el.classList.contains('open') && !el.contains(e.target) && e.target.id !== trigId) fn(false);
}
});
}
function keyBadge(code) {
if (code === 'Space') return 'SPC';
if (code === 'Backquote') return '~';
if (code === 'ShiftLeft' || code === 'ShiftRight') return 'SH';
if (code === 'Slash') return '/';
if (code.startsWith('Key')) return code.slice(3);
if (code.startsWith('Digit')) return code.slice(5);
return code;
}
let _nativeHud = null;
function createNativeHUD() {
const app = W.pc?.app;
const pc = W.pc;
if (!app || !pc) return null;
const roundedTex = app.assets.get(236692532);
const fontAsset = app.assets.get(236690804);
const gpm = app.root.findByName('GamePlayMenu');
if (!gpm || !roundedTex || !fontAsset) return null;
const old = app.root.findByName('ML-HUD');
if (old) old.destroy();
const btnSize = 50, spacing = 56, rowGap = 68;
const COL_NORM = [0.071, 0.071, 0.071];
const COL_ON = [0, 0.5, 1];
const COL_FRESH = [0, 0.66, 1];
const COL_DISABLED = [0.071, 0.071, 0.071];
const topDefs = [
['fly', 'FLY', keyBadge(KEYBINDS.fly), 'ml-fly'],
['spr', 'SPR', 'SH', 'ml-spr'],
['home', 'SET', keyBadge(KEYBINDS.setHome), 'ml-home'],
['go', 'GO', keyBadge(KEYBINDS.home), 'ml-go'],
['back', 'BCK', keyBadge(KEYBINDS.back), 'ml-back'],
['slots','0/10', keyBadge(KEYBINDS.slots), 'ml-slots'],
['lock', 'LCK', '', 'ml-lock'],
];
const botDefs = [
['invis', 'INV', keyBadge(KEYBINDS.invisible), 'ml-invis'],
['tp', 'CUD', keyBadge(KEYBINDS.cuddle), 'ml-tp'],
['petsBtn', 'PET', keyBadge(KEYBINDS.pets), 'ml-pets-btn'],
['snipeBtn','SNP', keyBadge(KEYBINDS.snipe), 'ml-snipe-btn'],
['cfg', 'CFG', keyBadge(KEYBINDS.settings), 'ml-cfg'],
['help', '?', keyBadge(KEYBINDS.help), 'ml-help'],
['tc', 'TC', keyBadge(KEYBINDS.towerComplete), 'ml-tc'],
];
const mlGroup = new pc.Entity('ML-HUD');
mlGroup.addComponent('element', { type: 'group', anchor: [0,1,0,1], pivot: [0,1], width: 440, height: 200 });
mlGroup.setLocalPosition(10, -70, 0);
mlGroup.enabled = false;
function makeBtn(id, label, keyText, idx, yBase) {
const btn = new pc.Entity('ML-' + id);
btn.addComponent('element', { type: 'group', anchor: [0,1,0,1], pivot: [0,1], width: btnSize + 6, height: btnSize + 30 });
btn.setLocalPosition(idx * spacing, -yBase, 0);
const bg = new pc.Entity('BG');
bg.addComponent('element', {
type: 'image', anchor: [.5,.5,.5,.5], pivot: [.5,.5],
width: btnSize, height: btnSize,
color: new pc.Color(COL_NORM[0], COL_NORM[1], COL_NORM[2]),
opacity: 1, textureAsset: roundedTex.id, useInput: true
});
btn.addChild(bg);
const fs = label.length > 3 ? 15 : (label.length > 2 ? 20 : label.length > 1 ? 24 : 30);
const lbl = new pc.Entity('Label');
lbl.addComponent('element', {
type: 'text', anchor: [.5,.5,.5,.5], pivot: [.5,.5],
text: label, fontSize: fs, fontAsset: fontAsset.id,
color: new pc.Color(1, 1, 1),
outlineThickness: .3, outlineColor: new pc.Color(0, 0, 0),
autoWidth: true, autoHeight: true
});
bg.addChild(lbl);
if (keyText) {
const isLong = keyText.length > 2, isMed = keyText.length === 2;
if (isLong || isMed) {
const hg = isLong ? 6 : 3, rw = hg * 2 + 0.5;
const dk = new pc.Entity('DesktopKey');
dk.addComponent('element', { type:'group', anchor:[.5,.5,.5,.5], pivot:[.5,.5], width:25, height:25 });
dk.setLocalPosition(17.5, -17.5, 0);
bg.addChild(dk);
[[-hg,25,25],[hg,25,25],[0,rw,25]].forEach(([x,w,h],j)=>{
const e = new pc.Entity('Image');
const c = { type:'image', anchor:[.5,.5,.5,.5], pivot:[.5,.5], width:w, height:h, color:new pc.Color(.388,.388,.388), opacity:1 };
if (j < 2) c.textureAsset = roundedTex.id;
e.addComponent('element', c); if (x) e.setLocalPosition(x,0,0); dk.addChild(e);
});
[[-hg,20,20],[hg,20,20],[0,rw,20]].forEach(([x,w,h],j)=>{
const e = new pc.Entity('Group');
const c = { type:'image', anchor:[.5,.5,.5,.5], pivot:[.5,.5], width:w, height:h, color:new pc.Color(1,1,1), opacity:1 };
if (j < 2) c.textureAsset = roundedTex.id;
e.addComponent('element', c); if (x) e.setLocalPosition(x,0,0); dk.addChild(e);
});
const kl = new pc.Entity('Text');
kl.addComponent('element', { type:'text', anchor:[.5,.5,.5,.5], pivot:[.5,.5],
text:keyText, fontSize:15, fontAsset:fontAsset.id, color:new pc.Color(0,0,0), autoWidth:true, autoHeight:true });
kl.setLocalPosition(0, -0.3, 0); dk.addChild(kl);
} else {
const dk = new pc.Entity('DesktopKey');
dk.addComponent('element', { type:'image', anchor:[.5,.5,.5,.5], pivot:[.5,.5],
width:25, height:25, color:new pc.Color(.388,.388,.388), opacity:1, textureAsset:roundedTex.id });
dk.setLocalPosition(17.5, -17.5, 0);
bg.addChild(dk);
const inner = new pc.Entity('Group');
inner.addComponent('element', { type:'image', anchor:[.5,.5,.5,.5], pivot:[.5,.5],
width:20, height:20, color:new pc.Color(1,1,1), opacity:1, textureAsset:roundedTex.id });
dk.addChild(inner);
const kl = new pc.Entity('Text');
kl.addComponent('element', { type:'text', anchor:[.5,.5,.5,.5], pivot:[.5,.5],
text:keyText, fontSize:15, fontAsset:fontAsset.id, color:new pc.Color(0,0,0), autoWidth:true, autoHeight:true });
kl.setLocalPosition(0.6, 0, 0); dk.addChild(kl);
}
}
mlGroup.addChild(btn);
return { bg, lbl, btn, keyLbl: keyText ? btn.findByName('Text') : null };
}
function makeProxy(parts, htmlId) {
const st = {};
const setCol = (r, g, b) => { if (parts.bg.element) { parts.bg.element.color = new pc.Color(r, g, b); } };
const update = () => {
if (!parts.bg.element || !parts.lbl.element) return;
if (st.disabled) {
setCol(COL_DISABLED[0], COL_DISABLED[1], COL_DISABLED[2]);
parts.bg.element.opacity = .45;
parts.lbl.element.color = new pc.Color(1, .24, .24);
} else if (st.fresh) {
setCol(COL_FRESH[0], COL_FRESH[1], COL_FRESH[2]);
parts.bg.element.opacity = 1;
parts.lbl.element.color = new pc.Color(1, 1, 1);
} else if (st.on) {
setCol(COL_ON[0], COL_ON[1], COL_ON[2]);
parts.bg.element.opacity = 1;
parts.lbl.element.color = new pc.Color(1, 1, 1);
} else {
setCol(COL_NORM[0], COL_NORM[1], COL_NORM[2]);
parts.bg.element.opacity = 1;
parts.lbl.element.color = new pc.Color(1, 1, 1);
}
};
return {
_native: parts,
classList: {
toggle(cls, force) { st[cls] = force; update(); },
add(cls) { st[cls] = true; update(); },
remove(cls) { delete st[cls]; update(); },
contains(cls) { return !!st[cls]; }
},
set textContent(t) {
const short = t.replace(/^SLOTS\s*/, '').replace(/^\u{1F512}\s*/u, '').replace(/^\u{1F513}\s*/u, '');
parts.lbl.element.text = short.length > 5 ? short.substring(0, 5) : short;
},
get textContent() { return parts.lbl.element.text; },
addEventListener() {},
click() { const el = document.getElementById(htmlId); if (el) el.click(); }
};
}
const result = {};
topDefs.forEach(([key, label, keyText, htmlId], i) => {
result[key] = makeProxy(makeBtn(key, label, keyText, i, 0), htmlId);
});
botDefs.forEach(([key, label, keyText, htmlId], i) => {
result[key] = makeProxy(makeBtn(key, label, keyText, i, rowGap), htmlId);
});
if (result.tc?._native?.btn) result.tc._native.btn.enabled = false;
const prevOverlay = $('ml-click-overlay');
if (prevOverlay) prevOverlay.remove();
const clickOverlay = document.createElement('div');
clickOverlay.id = 'ml-click-overlay';
clickOverlay.style.cssText = 'position:fixed;inset:0;z-index:10;pointer-events:none';
document.body.appendChild(clickOverlay);
const clickDivs = {};
for (const key of Object.keys(result)) {
const div = document.createElement('div');
div.style.cssText = 'position:absolute;pointer-events:auto;cursor:pointer';
div.dataset.key = key;
clickOverlay.appendChild(div);
clickDivs[key] = div;
}
['mousedown','mouseup','pointerdown','pointerup'].forEach(evt => {
clickOverlay.addEventListener(evt, e => {
if (e.target.dataset?.key) e.stopPropagation();
});
});
clickOverlay.addEventListener('touchstart', e => {
if (e.target.dataset?.key) { e.stopPropagation(); e.preventDefault(); }
});
clickOverlay.addEventListener('touchend', e => {
const key = e.target.dataset?.key;
if (key && result[key]) {
e.stopPropagation(); e.preventDefault();
console.log('[ml] btn-touch ' + key);
result[key].click();
}
});
clickOverlay.addEventListener('click', e => {
const key = e.target.dataset?.key;
if (!key || !result[key]) return;
console.log('[ml] btn-click ' + key);
e.stopPropagation();
e.preventDefault();
result[key].click();
});
const btnLayout = {};
topDefs.forEach(([key], i) => { btnLayout[key] = { idx: i, yBase: 0 }; });
botDefs.forEach(([key], i) => { btnLayout[key] = { idx: i, yBase: rowGap }; });
let _syncDiag = false;
let _syncLast = 0;
function syncOverlay() {
requestAnimationFrame(syncOverlay);
const now = performance.now();
if (now - _syncLast < 500) return;
_syncLast = now;
const canvas = app.graphicsDevice.canvas;
const rect = canvas.getBoundingClientRect();
const cw = rect.width, ch = rect.height;
const scr = mlGroup.parent?.screen;
const refW = scr?.referenceResolution?.x || 1280;
const refH = scr?.referenceResolution?.y || 720;
const sx = cw / refW, sy = ch / refH;
const blend = scr?.scaleBlend ?? 0.5;
const scale = Math.pow(sx, 1 - blend) * Math.pow(sy, blend);
const groupY = Math.abs(mlGroup.localPosition.y);
const groupX = mlGroup.localPosition.x;
const visible = mlGroup.enabled;
for (const key of Object.keys(result)) {
const lay = btnLayout[key];
if (!lay) continue;
const bgLeft = groupX + lay.idx * spacing + 3;
const bgTop = groupY + lay.yBase + 15;
const div = clickDivs[key];
div.style.left = (rect.left + bgLeft * scale) + 'px';
div.style.top = (rect.top + bgTop * scale) + 'px';
div.style.width = (btnSize * scale) + 'px';
div.style.height = (btnSize * scale) + 'px';
div.style.display = visible ? 'block' : 'none';
}
if (!_syncDiag && visible) {
_syncDiag = true;
console.log('[ml] overlay-sync scale=' + scale.toFixed(3) + ' groupXY=' + groupX + ',' + groupY + ' canvas=' + cw.toFixed(0) + 'x' + ch.toFixed(0) + ' buttons=' + Object.keys(btnLayout).join(','));
}
}
const overlay = gpm.parent;
overlay.addChild(mlGroup);
syncOverlay();
const chatEntity = gpm.findByName('Chat');
if (chatEntity) {
const chatY = -(Math.abs(mlGroup.localPosition.y) + rowGap + btnSize + 12);
chatEntity.setLocalPosition(chatEntity.localPosition.x, chatY, 0);
console.log('[ml] chat repositioned to y=' + chatY);
}
function setPauseLayout() {
mlGroup.setLocalPosition(10, -115, 0);
}
function setGameplayLayout() {
mlGroup.setLocalPosition(10, -70, 0);
}
const pauseEntity = overlay.findByName('Pause');
if (pauseEntity) {
const origDesc = Object.getOwnPropertyDescriptor(pc.GraphNode.prototype, 'enabled');
if (origDesc?.set) {
Object.defineProperty(pauseEntity, 'enabled', {
get() { return origDesc.get.call(this); },
set(v) {
origDesc.set.call(this, v);
if (v) setPauseLayout(); else setGameplayLayout();
},
configurable: true
});
}
}
const loadingEntity = overlay.findByName('LoadingScreen');
if (loadingEntity) {
const origDesc = Object.getOwnPropertyDescriptor(pc.GraphNode.prototype, 'enabled');
if (origDesc?.set) {
Object.defineProperty(loadingEntity, 'enabled', {
get() { return origDesc.get.call(this); },
set(v) {
origDesc.set.call(this, v);
mlGroup.enabled = !v;
const mf = $('ml-mobile-fly');
if (mf) mf.style.display = v ? 'none' : 'flex';
},
configurable: true
});
mlGroup.enabled = !loadingEntity.enabled;
const mfInit = $('ml-mobile-fly');
if (mfInit) mfInit.style.display = loadingEntity.enabled ? 'none' : 'flex';
} else {
mlGroup.enabled = true;
const mfOrig = $('ml-mobile-fly');
if (mfOrig) mfOrig.style.display = 'flex';
}
} else {
mlGroup.enabled = true;
const mfFallback = $('ml-mobile-fly');
if (mfFallback) mfFallback.style.display = 'flex';
}
console.log('[ml] native PlayCanvas HUD created');
return result;
}
function getApiManager() {
const root = W.pc?.app?.root;
const scripts = root?.findComponents('script') || [];
for (const s of scripts) {
for (const name of Object.keys(s)) {
const inst = s[name];
if (inst?.freeCurrency && inst?.balance !== undefined) return inst;
}
}
return null;
}
let freeMoneyDone = false;
async function autoFarmFreeMoney() {
if (freeMoneyDone || !featFreeMoney) return;
const api = getApiManager();
if (!api) return;
const nm = W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager;
if (!nm?.room?.sessionId) return;
freeMoneyDone = true;
const remaining = (api.freeCurrencyAdLimit || 10) - (api.freeCurrencyAdCounter || 0);
if (remaining <= 0) { console.log('[ml] free money: daily limit already reached'); return; }
let total = 0, calls = 0;
for (let i = 0; i < remaining; i++) {
const before = api.balance;
api.freeCurrency();
await new Promise(r => setTimeout(r, 1500));
const gained = api.balance - before;
if (gained <= 0) break;
total += gained;
calls++;
}
console.log('[ml] free money: +' + total + ' coins (' + calls + ' calls, balance: ' + api.balance + ')');
}
function _freeStarsStart() {
_freeStarsStop();
if (!featFreeStars) return;
_freeStarsTick();
}
function _freeStarsStop() {
clearTimeout(_freeStarsTimer);
_freeStarsTimer = null;
}
function _freeStarsTick() {
_freeStarsStop();
if (!featFreeStars) return;
const nm = _getNetworkManager();
if (!nm?.room || nm.room.name !== 'EscapeTsunami') {
_freeStarsTimer = setTimeout(_freeStarsTick, 5000);
return;
}
const app = W.pc?.app;
if (!app) { _freeStarsTimer = setTimeout(_freeStarsTick, 5000); return; }
const room = nm.room;
let sent = 0;
for (let i = 0; i < 50; i++) {
if (!room?.connection?.isOpen) break;
room.send('getFreeStars');
sent++;
}
const p = room?.state?.players?.get(room?.sessionId);
console.log('[ml] free stars burst: +' + (sent * 100) + ' stars (sent ' + sent + ', balance: ' + (p?.stars ?? '?') + ')');
_freeStarsTimer = setTimeout(_freeStarsTick, 10000);
}
let _lastFountainSend = 0;
function _fountainStart() {
if (!featAutoFountain) return;
_fountainSend();
}
function _fountainStop() {
clearTimeout(_fountainTimer);
_fountainTimer = null;
}
function _fountainSend() {
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.connection?.isOpen) return;
if (room.name !== 'PetTycoon' && room.name !== 'EscapeTsunami') return;
_lastFountainSend = Date.now();
room.send('unlockDecoration', 'MagicFountain');
console.log('[ml] fountain: 1.5× profit boost sent');
}
function _fountainHookExpiry(room) {
if (room.__mlFountainHooked) return;
room.__mlFountainHooked = true;
room.onMessage('decorationDisabled', msg => {
if (!featAutoFountain) return;
if (msg?.sessionId !== room.sessionId) return;
if (msg?.decorationName !== 'MagicFountain') return;
_fountainTimer = setTimeout(() => { _fountainTimer = null; _fountainSend(); }, 1000);
console.log('[ml] fountain expired — re-sending in 1s');
});
}
function _freeDiamondsStart() {
_freeDiamondsStop();
if (!featFreeDiamonds) return;
_freeDiamondsTick();
}
function _freeDiamondsStop() {
clearTimeout(_freeDiamondsTimer);
_freeDiamondsTimer = null;
}
function _freeDiamondsTick() {
_freeDiamondsStop();
if (!featFreeDiamonds) return;
const nm = _getNetworkManager();
if (!nm?.room || nm.room.name !== 'TowerParkour') {
_freeDiamondsTimer = setTimeout(_freeDiamondsTick, 5000);
return;
}
const room = nm.room;
if (!room?.connection?.isOpen) { _freeDiamondsTimer = setTimeout(_freeDiamondsTick, 5000); return; }
let sent = 0;
for (let i = 0; i < 20; i++) {
if (!room?.connection?.isOpen) break;
room.send('adFreeGiveDiamonds');
sent++;
}
const p = room?.state?.players?.get(room?.sessionId);
console.log('[ml] free diamonds burst: sent ' + sent + ' (balance: ' + (p?.diamonds ?? '?') + ')');
_freeDiamondsTimer = setTimeout(_freeDiamondsTick, 310000);
}
let lastAutoCollect = 0;
let _mlBatchCollecting = false;
let _mlBatchTotal = 0;
let _mlBatchCount = 0;
let _mlBatchTimer = null;
let _mlCachedBalance = null;
function findPetOverlay() {
const app = W.pc?.app;
if (!app) return null;
let overlay = null;
app.root.find(e => {
if (!e.script) return false;
for (const k of Object.keys(e.script)) {
const s = e.script[k];
if (s?.coinContainer && s?.onCoinAnimation) { overlay = s; return true; }
}
return false;
});
return overlay;
}
function flushBatch() {
if (!_mlBatchCollecting) return;
_mlBatchCollecting = false;
clearTimeout(_mlBatchTimer);
if (_mlBatchTotal !== 0) {
console.log('[ml] auto-collect: batched ' + _mlBatchCount + ' notifications → +$' + _mlBatchTotal);
const overlay = findPetOverlay();
const app = W.pc?.app;
if (overlay) {
for (let i = 0; i < 3; i++) {
setTimeout(() => overlay.onCoinAnimation(5), i * 200);
}
}
if (_mlCachedBalance !== null && app?._mlOrigFire) {
const bal = _mlCachedBalance;
_mlCachedBalance = null;
setTimeout(() => {
app._mlOrigFire.call(app, 'ModeOverlay:Balance', bal);
}, 1500);
}
} else if (_mlCachedBalance !== null) {
const app = W.pc?.app;
if (app?._mlOrigFire) {
app._mlOrigFire.call(app, 'ModeOverlay:Balance', _mlCachedBalance);
}
_mlCachedBalance = null;
}
_mlBatchTotal = 0;
_mlBatchCount = 0;
}
function autoCollectPetEarnings() {
if (!featAutoCollect) return;
const now = Date.now();
if (now - lastAutoCollect < autoCollectInterval * 1000) return;
lastAutoCollect = now;
const app = W.pc?.app;
if (!app) return;
if (!app._mlNotifBatched) {
app._mlNotifBatched = true;
app._mlOrigFire = app.fire;
}
const pm = _findPetsManager();
if (!pm?.basePets || pm.basePets.size === 0) return;
const nm = _getNetworkManager();
if (!nm?.room?.sessionId) return;
const myId = nm.room.sessionId;
_mlBatchCollecting = true;
_mlBatchTotal = 0;
_mlBatchCount = 0;
clearTimeout(_mlBatchTimer);
let claimed = 0;
pm.basePets.forEach((pet, token) => {
if (String(pet.owner) === String(myId)) {
app.fire('NetworkManager:Send', 'claimPetBalance', token);
claimed++;
}
});
if (claimed > 0) {
console.log('[ml] auto-collect: claimed ' + claimed + ' pets');
_mlBatchTimer = setTimeout(flushBatch, 5000);
} else {
_mlBatchCollecting = false;
}
}
const petSpawnData = new Map();
const PET_SPAWN_CAP = 2000;
function hookPetSpawn() {
const app = W.pc?.app;
if (!app) return;
if (app._mlPetSpawnHooked) return;
app._mlPetSpawnHooked = true;
const onPetsSpawn = data => {
const items = Array.isArray(data) ? data : (data && typeof data === 'object' ? Object.values(data) : []);
if (!items.length) return;
if (petSpawnData.size > PET_SPAWN_CAP) {
const excess = petSpawnData.size - PET_SPAWN_CAP + items.length;
const it = petSpawnData.keys();
for (let i = 0; i < excess; i++) petSpawnData.delete(it.next().value);
}
for (const p of items) {
if (!p.token) continue;
petSpawnData.set(p.token, {
profit: p.profit ?? null,
rarity: p.rarity ?? null,
mutation: p.mutation ?? null,
isEgg: !!p.isEgg,
});
}
};
app.on('PetsManager:PetsSpawn', onPetsSpawn);
app.on('BasesManager:PetsSpawn', onPetsSpawn);
console.log('[ml] pet spawn hook installed');
}
function getMyBase() {
const basesScript = _getBasesManager();
if (!basesScript?.activeBases) return null;
const nm = _getNetworkManager();
const sessionId = nm?.room?.sessionId || W.pc?.sessionId;
if (!sessionId) return null;
for (const bd of basesScript.activeBases) {
if (bd?.data?.sessionId === sessionId) return basesScript.baseEntities?.[bd.data.id] ?? null;
}
return null;
}
function getLockBtn() {
const base = getMyBase();
if (!base) return null;
const btn = base.findByName('LockdownButton');
return btn?.script?.lockdownButton ?? null;
}
let _lastLockTriggerLog = 0;
function triggerLock() {
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.connection?.isOpen) return false;
_rawSend(room)('activateLockdown');
const now = Date.now();
if (_roomJoinTime) {
console.log('[ml] auto-lock: first lock ' + (now - _roomJoinTime) + 'ms after join');
_roomJoinTime = 0;
} else if (now - _lastLockTriggerLog > 10000) {
_lastLockTriggerLog = now;
console.log('[ml] auto-lock: triggered');
}
return true;
}
let lastLockCheck = 0;
function hookAntiDisconnect() {
const nm = _getNetworkManager();
const room = nm?.room;
if (!room) return;
if (room.__mlSendHooked) return;
room.__mlSendHooked = true;
const origSend = room.send.bind(room);
room.__mlOrigSend = origSend;
const HEIGHT_OFFSET = 200;
let _currentOffset = 0;
let _wasHolding = false;
let _earlyGhost = false;
let _earlyGhostTimer = null;
room.send = function (type, message) {
if (type === 'stealPet' && featGhostMode) {
_earlyGhost = true;
_currentOffset = HEIGHT_OFFSET;
_wasHolding = true;
if (_earlyGhostTimer) clearTimeout(_earlyGhostTimer);
_earlyGhostTimer = setTimeout(() => { _earlyGhost = false; _earlyGhostTimer = null; }, 5000);
console.log('[ml] ghost: early activate on stealPet send');
}
if (type === 'p' && message && featGhostMode && !_ghostSuppressed) {
if (_wavesGhostPos) {
return origSend(type, { x: _wavesGhostPos.x, y: _wavesGhostPos.y, z: _wavesGhostPos.z, w: message.w });
}
const pt = _getPetTycoon();
const holding = !!pt?.isHoldingPet;
if (holding) {
_earlyGhost = false;
_currentOffset = HEIGHT_OFFSET;
_wasHolding = true;
} else if (_earlyGhost) {
} else if (_wasHolding && _currentOffset > 0) {
_currentOffset = Math.max(0, _currentOffset - 10);
if (_currentOffset === 0) _wasHolding = false;
}
if (_currentOffset > 0) {
return origSend(type, { x: message.x, y: message.y + _currentOffset, z: message.z, w: message.w });
}
}
if (type === 'dropPet') {
_earlyGhost = false;
}
return origSend(type, message);
};
console.log('[ml] ghost position hook installed');
_playerCache = null; _playerCacheTime = 0;
_getPetTycoon.clear(); _getEscapeTsunami.clear();
_getNetworkManager.clear(); _getBasesManager.clear();
_findPetsManager.clear();
_serverLockedSessions.clear();
_lockDataReceived = false;
_pendingBaseCapture = true;
_roomJoinTime = Date.now();
_myPetTokens = new Set(); _myPetTokensTime = 0;
if (featAntiKnockback) {
room.onMessage('gotAttacked', () => {});
}
setTimeout(() => {
if (featInvisible) { _toggleInvisible(true); _updateInvisEffect(true); }
if (featAutoFountain) _fountainStart();
_fountainHookExpiry(room);
_hookProTips(room);
}, 2000);
let _stealReturnTimer = null;
function _stealReturnHome() {
if (!featGhostMode) return;
const rawSend = _rawSend(room);
if (_stealReturnTimer) {
clearTimeout(_stealReturnTimer);
}
console.log('[ml] steal return: 10s then TP base');
_stealReturnTimer = setTimeout(() => {
_stealReturnTimer = null;
const player = getPlayer();
if (!player) return;
const pt = _getPetTycoon();
const et = _getEscapeTsunami();
if (!pt?.isHoldingPet && !et?.isHoldingPet) {
console.log('[ml] steal return: not holding pet, skip TP');
return;
}
_wavesGhostPos = null;
backPos = capturePos(player);
const app = W.pc?.app;
if (app) app.fire('Player:TeleportToPersonalSpawn');
setTimeout(() => {
const pos = player.getPosition();
rawSend('p', { x: pos.x, y: pos.y, z: pos.z, w: 0 });
}, 100);
console.log('[ml] steal return: teleported to base');
}, 10000);
}
room.onMessage('petClaimed', msg => {
if (msg?.stealerSessionId && msg.stealerSessionId !== room.sessionId) return;
if (msg?.sessionId && msg.sessionId !== room.sessionId) return;
_snipeCooldown = Date.now() + 10000;
console.log('[ml] pet claimed — 10s snipe cooldown');
_stealReturnHome();
});
room.onMessage('petStolen', msg => {
if (msg?.stealerSessionId && msg.stealerSessionId !== room.sessionId) return;
if (msg?.sessionId && msg.sessionId !== room.sessionId) return;
console.log('[ml] pet stolen');
_stealReturnHome();
});
room.onMessage('modeNotification', msg => {
if (msg === 'PetClaimedSuccessfully' || (typeof msg === 'object' && msg.text === 'PetClaimedSuccessfully')) {
console.log('[ml] mode notification: claimed');
_stealReturnHome();
}
});
room.onMessage('baseLockdownTimers', msg => {
_serverLockedSessions.clear();
if (Array.isArray(msg)) {
for (const entry of msg) {
if (entry.sessionId) _serverLockedSessions.add(entry.sessionId);
}
}
_lockDataReceived = true;
});
room.onMessage('lockdownEnded', () => {
if (!featAutoLock) return;
const rs = _rawSend(room);
rs('activateLockdown');
console.log('[ml] auto-lock: instant re-lock on lockdownEnded');
});
room.onMessage('triggerFunction', msg => {
if (!msg?.client || msg.client === room.sessionId) return;
if (msg.name !== 'Disable' && msg.name !== 'Enable') return;
const nm2 = _getNetworkManager();
const enemy = nm2?.getPlayerById(msg.client);
if (!enemy) return;
const es = enemy.script?.enemy;
if (!es?.originEntity) return;
if (msg.name === 'Disable') {
es.originEntity.enabled = true;
_ghostifyEnemy(es.originEntity);
console.log('[ml] anti-invis: ghosted', enemy.username || msg.client);
} else {
_unghostifyEnemy(es.originEntity);
console.log('[ml] anti-invis: restored', enemy.username || msg.client);
}
});
if (typeof room.onLeave === 'function') {
room.onLeave((code) => {
console.log('[ml] room closed code=' + code);
});
}
if (room.state?.players?.onAdd) {
room.state.players.onAdd((player, sessionId) => {
if (sessionId === room.sessionId) return;
if (!snipeActive) return;
const bm = _getBasesManager();
if (!bm?.activeBases) return;
console.log('[ml] player joined in Steal — immediate snipe check');
setTimeout(() => snipeTick(), 500);
});
}
}
let _ghostActive = false;
let _wavesGhostPos = null;
let _ghostSuppressed = false;
const GHOST_OPACITY = 0.25;
function _forEachMesh(entity, fn) {
entity.forEach(child => {
const mis = child.render?.meshInstances || child.model?.meshInstances || [];
for (const mi of mis) if (mi.material) fn(mi);
});
}
function _ghostifyEnemy(origin) {
_forEachMesh(origin, mi => {
if (!mi.__mlAntiInvisOrig) {
mi.__mlAntiInvisOrig = mi.material;
mi.material = mi.material.clone();
}
mi.material.blendType = W.pc.BLEND_NORMAL;
mi.material.opacity = 0.3;
mi.material.depthWrite = false;
mi.material.update();
});
}
function _unghostifyEnemy(origin) {
_forEachMesh(origin, mi => {
if (mi.__mlAntiInvisOrig) {
mi.material = mi.__mlAntiInvisOrig;
delete mi.__mlAntiInvisOrig;
mi.material.update();
}
});
}
function _getCharMeshInstances() {
const player = getPlayer();
const ch = player?.findByName('CharacterHolder');
if (!ch) return [];
const petHolder = player.findByName('PetHolder');
const skipSet = new Set();
if (petHolder) { skipSet.add(petHolder); petHolder.forEach(c => skipSet.add(c)); }
const result = [];
ch.forEach(child => {
if (skipSet.has(child)) return;
const mis = child.render?.meshInstances || child.model?.meshInstances || [];
for (const mi of mis) if (mi.material) result.push(mi);
});
return result;
}
function _updateCharTransparency() {
const shouldFade = _ghostActive || featInvisible;
const opacity = !shouldFade ? 1.0 : (featInvisible ? INVIS_OPACITY : GHOST_OPACITY);
const mis = _getCharMeshInstances();
if (!mis.length) return;
for (const mi of mis) {
if (opacity < 1.0) {
if (!mi.__mlOrigMat) {
mi.__mlOrigMat = mi.material;
mi.material = mi.material.clone();
}
mi.material.blendType = W.pc.BLEND_NORMAL;
mi.material.opacity = opacity;
mi.material.depthWrite = false;
} else {
if (mi.__mlOrigMat) {
mi.material = mi.__mlOrigMat;
delete mi.__mlOrigMat;
}
}
mi.material.update();
}
}
function updateGhostEffect() {
const pt = _getPetTycoon();
const shouldGhost = featGhostMode && (!!pt?.isHoldingPet || !!_wavesGhostPos);
if (shouldGhost === _ghostActive) return;
_ghostActive = shouldGhost;
console.log('[ml] ghost:', shouldGhost ? 'active' : 'inactive');
_updateCharTransparency();
}
let antiDeathReady = false;
function hookAntiDeath() {
if (antiDeathReady) return;
const app = W.pc?.app;
if (!app) return;
app.root.find(entity => {
if (!entity.script?._scripts) return false;
for (const inst of entity.script._scripts) {
if (inst && typeof inst.deathYThreshold === 'number' && inst.deathYThreshold > -9999) {
inst.deathYThreshold = -99999;
console.log('[ml] anti-death: Y threshold → -99999 on %s', entity.name);
}
}
return false;
});
const origFire = app.fire;
app._mlOrigFire = origFire;
app.fire = function (ev, a1, a2, a3, a4, a5, a6, a7, a8) {
switch (ev) {
case 'DeathScreen:Trigger': if (featInvincible) return this; break;
case 'PlayerController:GotHit': if (featAntiKnockback) return this; break;
case 'ModeOverlay:BalanceChange':
if (_mlBatchCollecting) {
_mlBatchTotal += (typeof a1 === 'number' ? a1 : 0);
_mlBatchCount++;
clearTimeout(_mlBatchTimer);
_mlBatchTimer = setTimeout(flushBatch, 2000);
return this;
}
break;
case 'ModeOverlay:Balance':
if (_mlBatchCollecting) { _mlCachedBalance = a1; return this; }
break;
}
return origFire.call(this, ev, a1, a2, a3, a4, a5, a6, a7, a8);
};
app._mlNotifBatched = true;
antiDeathReady = true;
console.log('[ml] anti-death: ready');
}
let antiKnockbackReady = false;
function hookAntiKnockback() {
if (antiKnockbackReady || !featAntiKnockback) return;
const player = getPlayer();
const pc = getPC(player);
if (!pc) return;
pc.shockedTime = 0;
pc.knockbackDecay = 1.0;
antiKnockbackReady = true;
}
let noclipHooked = false;
function hookNoclip() {
if (noclipHooked) return;
const player = getPlayer();
const kcc = getKcc(player);
if (!kcc || !kcc.update) return;
const origUpdate = kcc.update.bind(kcc);
kcc.update = function (dt) {
if (!featNoclip) return origUpdate(dt);
const pos = this.entity.getPosition();
const preX = pos.x, preZ = pos.z;
origUpdate(dt);
const speed = this.speed || 7;
const h = this._horizontal || 0, v = this._vertical || 0;
const newPos = this.entity.getPosition();
this.entity.setPosition(preX + h * speed * dt, newPos.y, preZ + v * speed * dt);
};
noclipHooked = true;
console.log('[ml] noclip: KCC update hooked');
}
createHUD();
loadWaypoints();
if (isMobile) {
const mfc = document.createElement('div');
mfc.id = 'ml-mobile-fly';
mfc.innerHTML = '▲ ▼ FLY SPR ⊟ ';
const mfcStyle = document.createElement('style');
mfcStyle.textContent = [
'#ml-mobile-fly{position:fixed;right:2.5vmin;bottom:3vmin;z-index:15;display:none;flex-direction:column;align-items:flex-end;gap:clamp(6px,1.5vmin,12px);pointer-events:none}',
'.ml-mfly{pointer-events:auto;width:clamp(52px,12vmin,80px);height:clamp(52px,12vmin,80px);border-radius:50%;border:2px solid rgba(200,170,255,.35);background:rgba(55,45,80,.55);color:rgba(255,250,255,.85);font-size:clamp(14px,3.5vmin,22px);font-weight:700;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;cursor:pointer;user-select:none;-webkit-user-select:none;touch-action:none;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);transition:background .12s,border-color .12s,transform .08s}',
'.ml-mfly:active{transform:scale(.92);background:rgba(140,100,220,.4);border-color:rgba(200,170,255,.6)}',
'.ml-mfly.on{background:rgba(100,180,100,.4);border-color:rgba(140,230,140,.5)}',
'.ml-mcollapse{width:clamp(36px,8vmin,52px);height:clamp(36px,8vmin,52px);font-size:clamp(16px,4vmin,24px);border-color:rgba(180,180,200,.3);background:rgba(40,40,55,.55)}',
'.ml-mcollapse:active{background:rgba(100,100,140,.4)}',
'#ml-mobile-fly.collapsed .ml-mfly:not(.ml-mcollapse){display:none}',
'.ml-msprint-btn{border-color:rgba(255,180,60,.35);background:rgba(80,55,30,.55);font-size:clamp(11px,2.8vmin,16px)}',
'.ml-msprint-btn:active{background:rgba(220,160,40,.4);border-color:rgba(255,200,80,.6)}',
'.ml-msprint-btn.on{background:rgba(220,160,40,.4);border-color:rgba(255,200,80,.5)}',
].join('');
document.head.appendChild(mfcStyle);
document.body.appendChild(mfc);
const mflyToggle = $('ml-mfly-toggle');
const mflyUp = $('ml-mfly-up');
const mflyDown = $('ml-mfly-down');
const msprint = $('ml-msprint');
const mCollapse = $('ml-mfly-collapse');
let _collapseLock = false;
const toggleCollapse = () => {
if (_collapseLock) return;
_collapseLock = true;
setTimeout(() => _collapseLock = false, 200);
const collapsed = mfc.classList.toggle('collapsed');
mCollapse.textContent = collapsed ? '⊞' : '⊟';
};
mCollapse.addEventListener('touchstart', e => { e.preventDefault(); toggleCollapse(); });
mCollapse.addEventListener('click', toggleCollapse);
mflyToggle.addEventListener('touchstart', e => {
e.preventDefault();
if (!featFly) return;
const p = getPlayer(), k = getKcc(p);
if (!k) return;
if (flyActive) { flyOff(k); mflyToggle.classList.remove('on'); }
else { flyOn(k, true); mflyToggle.classList.add('on'); }
});
msprint.addEventListener('touchstart', e => {
e.preventDefault();
if (!featSprint) return;
sprinting = !sprinting;
msprint.classList.toggle('on', sprinting);
});
mflyUp.addEventListener('touchstart', e => {
e.preventDefault();
if (!featFly || !flyActive) return;
flyUp = true;
});
mflyUp.addEventListener('touchend', e => {
e.preventDefault();
flyUp = false;
});
mflyUp.addEventListener('touchcancel', () => { flyUp = false; });
mflyDown.addEventListener('touchstart', e => {
e.preventDefault();
if (!featFly || !flyActive) return;
flyDown = true;
});
mflyDown.addEventListener('touchend', e => {
e.preventDefault();
flyDown = false;
});
mflyDown.addEventListener('touchcancel', () => { flyDown = false; });
}
let _flyClickTime = 0;
$('ml-fly').addEventListener('click', () => {
if (!featFly || isMobile) return;
const now = Date.now();
if (now - _flyClickTime < 300) return;
_flyClickTime = now;
const p = getPlayer(), k = getKcc(p);
if (!k) return;
if (flyActive) flyOff(k); else flyOn(k, true);
});
$('ml-spr').addEventListener('click', () => {
if (!featSprint) return;
sprinting = !sprinting;
});
$('ml-home').addEventListener('click', () => {
if (!featWaypoints) return;
const p = getPlayer();
if (!p) return;
homePos = capturePos(p);
saveWaypoints();
flash('ml-home');
console.log('[ml] home set via click');
});
$('ml-go').addEventListener('click', () => {
if (!featWaypoints || !homePos) return;
const p = getPlayer();
if (!p) return;
backPos = capturePos(p);
teleport(p, homePos);
saveWaypoints();
flash('ml-go');
});
$('ml-back').addEventListener('click', () => {
if (!featWaypoints || !backPos) return;
const p = getPlayer();
if (!p) return;
const cur = capturePos(p);
teleport(p, backPos);
backPos = cur;
saveWaypoints();
flash('ml-back');
});
$('ml-lock').addEventListener('click', () => {
if (!featAutoLock) return;
triggerLock();
flash('ml-lock');
});
function toggleSlots(forceOpen) {
if (!featWaypoints) return;
const pop = $('ml-slots-pop');
if (!pop) return;
const open = forceOpen !== undefined ? forceOpen : !pop.classList.contains('open');
pop.classList.toggle('open', open);
if (open) {
pop.dispatchEvent(new CustomEvent('ml-rebuild'));
panelPause();
} else {
pop._resetPos?.();
panelResume();
}
}
$('ml-slots').addEventListener('click', () => { toggleSlots(); });
let _invisTimer = null;
function _toggleInvisible(on) {
clearInterval(_invisTimer);
_invisTimer = null;
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.connection?.isOpen) return;
const rawSend = _rawSend(room);
rawSend('triggerFunction', { name: on ? 'Disable' : 'Enable', value: true });
if (on) {
_invisTimer = setInterval(() => {
const nm2 = _getNetworkManager();
const r = nm2?.room;
if (!r?.connection?.isOpen || !featInvisible) { clearInterval(_invisTimer); _invisTimer = null; return; }
const rs = _rawSend(r);
rs('triggerFunction', { name: 'Disable', value: true });
}, 3000);
}
console.log('[ml] invisible: ' + (on ? 'ON (repeating)' : 'OFF'));
}
let _invisActive = false;
const INVIS_OPACITY = 0.3;
function _updateInvisEffect(on) {
if (on === _invisActive) return;
_invisActive = on;
console.log('[ml] invis effect:', on ? 'on' : 'off');
_updateCharTransparency();
}
let _proTipsCooldown = 0;
const _PT_COOLDOWN = 600000;
const _ptOpeners = ['yo','ngl','bro','pro tip','just found','anyone try','seriously','trust me','ok but','hey'];
const _ptNames = ["'Meeland Enhancement Suite'","the 'Meeland Enhancement Suite'","'MES'","the enhancement suite"];
const _ptQuality = ['is insane','so broken lol','is actually op','changes everything','is crazy good','is goated','best mod ive seen','makes the game ez','is nuts'];
const _ptLocation = ['on greasy fork','search greasy fork','check greasy fork','find it on greasy fork','its on greasy fork'];
const _ptClosers = ['fr fr','no cap','thank me later','trust','try it','you need this','100%','seriously'];
const _ptTemplates = [
(o,n,q,l,c) => `${o} ${n} ${q} ${l} ${c}`,
(o,n,q,l,c) => `${o} ${l} for ${n} ${q} ${c}`,
(o,n,q,l,c) => `${o} get ${n} ${l} ${c}`,
(o,n,q,l,c) => `${n} ${q} ${l} ${c}`,
(o,n,q,l,c) => `${o} ${n} ${l} ${q} ${c}`,
];
function _pick(arr) { return arr[Math.random() * arr.length | 0]; }
function _genProTip() {
const t = _pick(_ptTemplates);
return t(_pick(_ptOpeners), _pick(_ptNames), _pick(_ptQuality), _pick(_ptLocation), _pick(_ptClosers)).replace(/\s+/g, ' ').trim();
}
const _proTipSentMsgs = new Set();
function _trySendProTip(isJoin) {
if (!featProTips) return;
const now = Date.now();
if (now - _proTipsCooldown < _PT_COOLDOWN) return;
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.connection?.isOpen) return;
_proTipsCooldown = now;
const rawSend = _rawSend(room);
const delay = 2000 + Math.random() * 3000;
setTimeout(() => {
if (!featProTips || !room?.connection?.isOpen) return;
const msg = _genProTip();
_proTipSentMsgs.add(msg);
setTimeout(() => _proTipSentMsgs.delete(msg), 5000);
_hookProTipSuppression();
rawSend(1, msg);
_proTipsCooldown = isJoin ? Date.now() - _PT_COOLDOWN + 5000 : Date.now();
console.log('[ml] pro tip sent' + (isJoin ? ' (join)' : '') + ':', msg);
}, delay);
}
function _hookProTipSuppression() {
const app = W.pc?.app;
if (!app || app.__mlPTSuppress) return;
app.__mlPTSuppress = true;
const origFire = app.fire;
app.fire = function(event) {
if (event === 'OverlayManager:ChatMessage') {
const m = arguments[1];
if (m?.type === 0 && _proTipSentMsgs.size > 0 && _proTipSentMsgs.has(m.msg)) {
_proTipSentMsgs.delete(m.msg);
return this;
}
}
return origFire.apply(this, arguments);
};
}
function _hookProTips(room) {
if (room.__mlProTipsHooked) return;
room.__mlProTipsHooked = true;
_proTipsCooldown = 0;
_trySendProTip(true);
room.onMessage('sendChatMessage', msg => {
if (!msg || msg.type !== 0) return;
_trySendProTip();
});
}
function _towerComplete() {
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.connection?.isOpen || room.name !== 'TowerParkour') {
console.log('[ml] tower: not in TowerParkour');
return;
}
flash('ml-tc');
const rs = _rawSend(room);
for (let i = 1; i <= 10; i++) rs('checkPointTake', i);
rs('checkPointTake', 777);
console.log('[ml] tower: sent checkpoints 1-10 + 777 (instant complete)');
}
function _trackAttack(player, target, rawSend, dir, onDone) {
_ghostSuppressed = true;
const t0 = performance.now();
let hit = false;
const step = () => {
const ep = target.getPosition();
teleport(player, vec3(ep));
rawSend('p', { x: ep.x, y: ep.y, z: ep.z, w: dir });
if (performance.now() - t0 >= 50 && !hit) { rawSend('attack', 'Baton'); hit = true; }
if (performance.now() - t0 < 150) { requestAnimationFrame(step); }
else { if (!hit) rawSend('attack', 'Baton'); setTimeout(() => { _ghostSuppressed = false; }, 200); onDone?.(); }
};
step();
}
let _lastAttackTime = 0;
function attackClosestEnemy() {
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.connection?.isOpen) return;
if (room.name !== 'PetTycoon') return;
const now = Date.now();
if (now - _lastAttackTime < 100) return;
_lastAttackTime = now;
const player = getPlayer();
if (!player) return;
const holder = W.pc?.app?.root?.findByName('EnemyHolder');
if (!holder?.children?.length) {
console.log('[ml] attack: no enemies found');
return;
}
const pPos = player.getPosition();
let closest = null, closestDist = Infinity, closestName = 'Player', closestEntity = null;
for (const enemy of holder.children) {
const name = _getEnemyName(enemy);
const ePos = enemy.getPosition();
const d = pPos.distance(ePos);
if (d < closestDist) {
closestDist = d;
closest = ePos;
closestEntity = enemy;
closestName = name || 'Player';
}
}
if (!closest || !closestEntity) return;
backPos = capturePos(player);
const rawSend = _rawSend(room);
const myPos = player.getPosition();
const dir = Math.atan2(closest.x - myPos.x, closest.z - myPos.z) * (180 / Math.PI);
_trackAttack(player, closestEntity, rawSend, dir, () => {
console.log('[ml] attack: hit ' + closestName + ' (' + Math.floor(closestDist) + 'm)');
});
}
let _autoAttackCooldown = 0;
let _autoAttackTarget = null;
let _autoAttackRRIdx = 0;
let _myPetTokens = new Set();
let _myPetTokensTime = 0;
function _refreshMyPetTokens() {
const now = Date.now();
if (now - _myPetTokensTime < 10000 && _myPetTokens.size > 0) return;
const nm = _getNetworkManager();
const myId = nm?.room?.sessionId;
if (!myId) return;
const root = W.pc?.app?.root;
if (!root) return;
root.find(e => {
if (!e.script) return false;
for (const k of Object.keys(e.script)) {
const s = e.script[k];
if (s?.basePets && s.basePets.size > 0) {
s.basePets.forEach((pet, token) => {
if (pet?.owner === myId) _myPetTokens.add(token);
});
_myPetTokensTime = now;
return true;
}
}
return false;
});
}
function autoAttackPetThieves() {
if (!featAutoAttack) return;
if (!_getBasesManager()?.activeBases) return;
const now = Date.now();
if (now < _autoAttackCooldown) return;
_refreshMyPetTokens();
if (_myPetTokens.size === 0) return;
const nm = _getNetworkManager();
const room = nm?.room;
if (!room?.state?.players || !room?.connection?.isOpen) return;
const myId = room.sessionId;
const thieves = [];
room.state.players.forEach((p, sid) => {
if (sid === myId) return;
if (p.isHoldingPet && p.holdingPetToken && _myPetTokens.has(p.holdingPetToken)) {
thieves.push({ sid, name: p.username || 'Player' });
}
});
if (thieves.length === 0) {
if (_autoAttackTarget) {
console.log('[ml] auto-attack: no thieves — chase ended');
_autoAttackTarget = null;
_autoAttackRRIdx = 0;
}
return;
}
if (_autoAttackRRIdx >= thieves.length) _autoAttackRRIdx = 0;
const thief = thieves[_autoAttackRRIdx];
_autoAttackRRIdx++;
const holder = W.pc?.app?.root?.findByName('EnemyHolder');
if (!holder?.children?.length) return;
let targetEntity = null;
for (const enemy of holder.children) {
if (_getEnemyName(enemy) === thief.name) { targetEntity = enemy; break; }
}
if (!targetEntity) return;
const player = getPlayer();
if (!player) return;
if (_autoAttackTarget !== thief.name) {
backPos = capturePos(player);
console.log('[ml] auto-attack: → ' + thief.name + ' (' + thieves.length + ' thieves)');
}
_autoAttackTarget = thief.name;
_autoAttackCooldown = now + 1600;
const rawSend = _rawSend(room);
const myPos = player.getPosition();
const tPos = targetEntity.getPosition();
const dir = Math.atan2(tPos.x - myPos.x, tPos.z - myPos.z) * (180 / Math.PI);
_trackAttack(player, targetEntity, rawSend, dir);
}
window.addEventListener('keydown', e => {
const openPanel = $('ml-pets')?.classList.contains('open') ? 'pets'
: $('ml-settings')?.classList.contains('open') ? 'settings'
: $('ml-plist')?.classList.contains('open') ? 'plist'
: $('ml-dialog')?.classList.contains('open') ? 'dialog'
: $('ml-slots-pop')?.classList.contains('open') ? 'slots'
: $('ml-snipe')?.classList.contains('open') ? 'snipe'
: null;
if (openPanel) {
if (openPanel === 'snipe') {
if (e.code === 'Escape') {
e.stopPropagation();
e.preventDefault();
$('ml-snipe-close')?.click();
return;
}
const active = document.activeElement;
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA')
&& $('ml-snipe')?.contains(active)) {
e.stopPropagation();
return;
}
} else {
if (e.code === 'Escape') {
e.stopPropagation();
e.preventDefault();
if (openPanel === 'pets') $('ml-pets-close')?.click();
else if (openPanel === 'settings') $('ml-settings-close')?.click();
else if (openPanel === 'plist') $('ml-plist-close')?.click();
else if (openPanel === 'dialog') $('ml-close')?.click();
else if (openPanel === 'slots') $('ml-slots-pop-close')?.click();
return;
}
const active = document.activeElement;
if (active?.tagName === 'INPUT' || active?.tagName === 'TEXTAREA') {
e.stopPropagation();
return;
}
e.stopPropagation();
e.preventDefault();
return;
}
}
const active = document.activeElement;
if (active?.tagName === 'INPUT' || active?.tagName === 'TEXTAREA') return;
if (kbListeningRow) return;
const _simpleKeys = {
[KEYBINDS.cuddle]: () => { if (featCuddle) $('ml-tp')?.click(); },
[KEYBINDS.settings]: () => $('ml-cfg')?.click(),
[KEYBINDS.pets]: () => { if (featPets) $('ml-pets-btn')?.click(); },
[KEYBINDS.snipe]: () => $('ml-snipe-btn')?.click(),
[KEYBINDS.attack]: () => attackClosestEnemy(),
[KEYBINDS.invisible]: () => $('ml-invis')?.click(),
[KEYBINDS.towerComplete]: () => _towerComplete(),
[KEYBINDS.slots]: () => $('ml-slots')?.click(),
[KEYBINDS.help]: () => $('ml-help')?.click(),
};
const _skHandler = _simpleKeys[e.code];
if (_skHandler) { e.preventDefault(); e.stopPropagation(); _skHandler(); return; }
const player = getPlayer();
const kcc = getKcc(player);
if (!kcc) return;
if (cuddling && MOVE_KEYS.has(e.code)) {
cuddling = false;
cuddleTarget = null;
console.log('[cuddle] cancelled');
}
const numpadMatch = e.code.match(/^Numpad(\d)$/);
let slotIdx = numpadMatch ? parseInt(numpadMatch[1]) : -1;
if (slotIdx < 0 && e.location === 3) {
const alt = { Insert: 0, End: 1, ArrowDown: 2, PageDown: 3, ArrowLeft: 4, Clear: 5, ArrowRight: 6, Home: 7, ArrowUp: 8, PageUp: 9 };
slotIdx = alt[e.key] ?? -1;
}
if (slotIdx >= 0 && featWaypoints) {
const idx = slotIdx;
if (e.ctrlKey) {
e.preventDefault();
e.stopPropagation();
slots[idx] = capturePos(player);
saveWaypoints();
flash('ml-slots');
console.log(`[slot ${idx}] saved`, slots[idx]);
return;
} else if (slots[idx]) {
e.preventDefault();
e.stopPropagation();
backPos = capturePos(player);
teleport(player, slots[idx]);
saveWaypoints();
flash('ml-slots');
console.log(`[slot ${idx}] teleported to`, slots[idx]);
return;
}
}
if (e.code === KEYBINDS.fly) {
if (!featFly) return;
e.stopPropagation();
if (flyActive) {
flyUp = true;
} else {
flyOn(kcc);
}
return;
}
if (e.code === KEYBINDS.flyDown && flyActive) { flyDown = true; return; }
if (e.key === 'Shift' && !sprinting && featSprint) { sprinting = true; return; }
if (e.code === KEYBINDS.setHome) {
e.preventDefault();
if (!featWaypoints) return;
homePos = capturePos(player);
saveWaypoints();
flash('ml-home');
return;
}
if (e.code === KEYBINDS.home) {
e.preventDefault();
if (!featWaypoints) return;
if (homePos) {
backPos = capturePos(player);
teleport(player, homePos);
saveWaypoints();
flash('ml-go');
}
return;
}
if (e.code === KEYBINDS.back) {
e.preventDefault();
if (!featWaypoints) return;
if (backPos) {
const cur = capturePos(player); teleport(player, backPos); backPos = cur;
saveWaypoints();
flash('ml-back');
}
}
}, true);
window.addEventListener('keyup', e => {
if (anyPanelOpen()) {
e.stopPropagation();
return;
}
if (e.code === KEYBINDS.fly) flyUp = false;
if (e.code === KEYBINDS.flyDown) flyDown = false;
if (e.key === 'Shift' && sprinting) { sprinting = false; }
}, true);
const _htmlHud = { fly: $('ml-fly'), spr: $('ml-spr'), home: $('ml-home'), go: $('ml-go'), back: $('ml-back'), slots: $('ml-slots'), lock: $('ml-lock'), invis: $('ml-invis'), tp: $('ml-tp'), petsBtn: $('ml-pets-btn'), snipeBtn: $('ml-snipe-btn'), cfg: $('ml-cfg'), help: $('ml-help'), tc: $('ml-tc') };
_nativeHud = createNativeHUD();
if (!_nativeHud) {
const retryId = setInterval(() => {
_nativeHud = createNativeHUD();
if (_nativeHud) {
clearInterval(retryId);
Object.assign(hud, _nativeHud);
console.log('[ml] native HUD loaded (deferred)');
}
}, 500);
}
const hud = _nativeHud || _htmlHud;
const panels = { plist: $('ml-plist'), settings: $('ml-settings'), dialog: $('ml-dialog'), pets: $('ml-pets'), snipe: $('ml-snipe') };
let _hopResumed = false;
let _hopInProgress = false;
function performServerHop() {
if (_hopInProgress) return;
_hopInProgress = true;
const app = W.pc?.app;
if (!app) { _hopInProgress = false; return; }
app.fire('NetworkManager:LeaveCurrentRoom');
console.log('[ml] hop: left room');
$('ml-snipe-status').textContent = 'Hopping...';
_getNetworkManager.clear();
_getBasesManager.clear();
_getPetTycoon.clear(); _getEscapeTsunami.clear();
_findPetsManager.clear();
_serverLockedSessions.clear();
_lockDataReceived = false;
_myPetTokens = new Set(); _myPetTokensTime = 0;
_snipeHopNoMatchStart = 0;
_hopResumed = false;
setTimeout(() => {
app.fire('OverlayManager:ClickGameMode', 0);
console.log('[ml] hop: rejoining Steal a Pet');
const dismissPlay = () => {
const fml = app.root.findByName('FirstMouseLock');
if (fml && fml.enabled) {
fml.enabled = false;
app.fire('GameManager:GameResumed');
console.log('[ml] hop: dismissed PLAY screen');
}
const canvas = app.graphicsDevice?.canvas;
if (canvas && !document.pointerLockElement) {
try { canvas.requestPointerLock(); } catch (_) {}
}
_hopInProgress = false;
};
let attempts = 0;
const checkReady = setInterval(() => {
attempts++;
const nm = _getNetworkManager();
const fml = app.root.findByName('FirstMouseLock');
if ((nm?.room?.state?.players && fml?.enabled) || attempts > 30) {
clearInterval(checkReady);
dismissPlay();
}
}, 500);
}, 3000);
}
function tryHopResume() {
if (_hopResumed || _hopInProgress || !snipeAutoHop || !snipeActive) return;
const nm = _getNetworkManager();
if (!nm?.room?.state?.players) return;
const bm = _getBasesManager();
if (!bm?.activeBases) return;
const root = W.pc?.app?.root;
let petCount = 0;
if (root) {
root.find(e => {
if (!e.script) return false;
for (const k of Object.keys(e.script)) {
const s = e.script[k];
if (s?.basePets && s.basePets.size > 0) { petCount = s.basePets.size; return true; }
}
return false;
});
}
if (petCount === 0) return;
_hopResumed = true;
_snipeHopNoMatchStart = 0;
console.log('[ml] hop: new room ready (' + petCount + ' pets)');
const logEl = $('ml-snipe-log');
if (logEl) {
const entry = document.createElement('div');
entry.className = 'ml-snipe-entry';
entry.textContent = new Date().toLocaleTimeString() + ' Hopped — ' + petCount + ' pets in new room';
logEl.prepend(entry);
}
}
let _tickN = 0;
function _tick() {
requestAnimationFrame(_tick);
_tickN++;
const slow = (_tickN % 12 === 0);
if (slow) {
try { hookPetSpawn(); } catch (_) {}
try { hookAntiDisconnect(); } catch (_) {}
try { tryHopResume(); } catch (_) {}
try { hookAntiDeath(); } catch (_) {}
try { hookAntiKnockback(); } catch (_) {}
try { hookNoclip(); } catch (_) {}
try { updateGhostEffect(); } catch (_) {}
if (!freeMoneyDone && featFreeMoney) autoFarmFreeMoney();
if (featFreeStars && !_freeStarsTimer) _freeStarsStart();
if (featFreeDiamonds && !_freeDiamondsTimer) _freeDiamondsStart();
if (featAutoFountain && Date.now() - _lastFountainSend > 30000) _fountainSend();
autoCollectPetEarnings();
autoAttackPetThieves();
const ct = $('ml-carry-timer');
if (ct) {
const pt = _getPetTycoon();
const holding = !!(pt?.isHoldingPet);
const isSteal = !!_getBasesManager()?.activeBases;
if (holding && isSteal) {
if (!ct._start) ct._start = Date.now();
ct.style.display = '';
ct.textContent = '\u23F1 ' + ((Date.now() - ct._start) / 1000).toFixed(1) + 's';
} else {
if (ct._start) ct._start = 0;
ct.style.display = 'none';
}
}
}
const player = getPlayer();
if (!player) return;
if (slow && _pendingBaseCapture) {
const myBase = getMyBase();
if (myBase) {
const bp = myBase.getPosition();
backPos = { x: bp.x, y: bp.y, z: bp.z };
_pendingBaseCapture = false;
console.log('[ml] backPos set to home base (' + (_roomJoinTime ? (Date.now() - _roomJoinTime) + 'ms' : '?') + '):', bp.x.toFixed(1), bp.y.toFixed(1), bp.z.toFixed(1));
}
}
const kcc = getKcc(player);
if (!kcc) return;
const now = Date.now();
const dt = Math.min((now - prevTick) / 1000, 0.1);
prevTick = now;
const pc = getPC(player);
if (pc?.isDied) { pc.isDied = false; }
if (sprinting && !isMobile) {
const kb = W.pc?.app?.keyboard;
if (kb && !kb.isPressed(W.pc.KEY_SHIFT)) { sprinting = false; }
}
if (sprinting) {
const sprintMin = SPEED_DEFAULT * 3;
if (sprintSpeed < sprintMin) sprintSpeed = sprintMin;
if (accelEnabled) sprintSpeed = Math.min(sprintSpeed + ACCEL * dt, SPEED_CAP);
setSpeed(pc, kcc, sprintSpeed);
if (pc?.isSlowedSpeed) pc.isSlowedSpeed = false;
} else if (sprintSpeed !== SPEED_DEFAULT) {
sprintSpeed = SPEED_DEFAULT;
setSpeed(pc, kcc, SPEED_DEFAULT);
}
if (slow) {
if (hud.fly) { hud.fly.classList.toggle('on', flyActive); hud.fly.classList.toggle('disabled', !featFly); }
const mft = $('ml-mfly-toggle');
if (mft) mft.classList.toggle('on', flyActive);
if (hud.spr) { hud.spr.classList.toggle('on', sprinting); hud.spr.classList.toggle('disabled', !featSprint); }
const msp = $('ml-msprint');
if (msp) msp.classList.toggle('on', sprinting);
if (hud.home) hud.home.classList.toggle('disabled', !featWaypoints);
if (hud.go) hud.go.classList.toggle('disabled', !featWaypoints);
if (hud.back) hud.back.classList.toggle('disabled', !featWaypoints);
if (hud.slots) { const n = slots.filter(Boolean).length; hud.slots.textContent = `SLOTS ${n}/10`; hud.slots.classList.toggle('disabled', !featWaypoints); }
if (hud.tp) { hud.tp.classList.toggle('disabled', !featCuddle); hud.tp.classList.toggle('on', !!panels.plist?.classList.contains('open')); }
if (hud.petsBtn) { hud.petsBtn.classList.toggle('disabled', !featPets); hud.petsBtn.classList.toggle('on', !!panels.pets?.classList.contains('open')); }
if (hud.snipeBtn) { hud.snipeBtn.classList.toggle('on', snipeActive || !!panels.snipe?.classList.contains('open')); }
if (hud.invis) hud.invis.classList.toggle('on', featInvisible);
if (hud.cfg) hud.cfg.classList.toggle('on', !!panels.settings?.classList.contains('open'));
if (hud.help) hud.help.classList.toggle('on', !!panels.dialog?.classList.contains('open'));
const inTower = _getNetworkManager()?.room?.name === 'TowerParkour';
if (hud.tc) { hud.tc.classList.toggle('disabled', !inTower); if (_nativeHud?.tc?._native?.btn) _nativeHud.tc._native.btn.enabled = inTower; const tcHtml = $('ml-tc'); if (tcHtml) tcHtml.style.display = inTower ? '' : 'none'; }
if (featAutoLock && now - lastLockCheck > 100) {
lastLockCheck = now;
const lockBtn = getLockBtn();
if (lockBtn) {
const timeLeft = lockBtn.lockdownTimeLeft || 0;
const isActive = lockBtn.isLockdownActive || false;
if (hud.lock) {
hud.lock.classList.toggle('on', isActive && timeLeft > 0);
hud.lock.textContent = (isActive && timeLeft > 0) ? `\ud83d\udd12 ${Math.ceil(timeLeft)}s` : '\ud83d\udd13 UNLOCKED';
}
if (!isActive || timeLeft <= 0) triggerLock();
else if (timeLeft > 0 && timeLeft < 1) triggerLock();
} else if (hud.lock) {
hud.lock.textContent = '\ud83d\udd12 LOCK';
hud.lock.classList.remove('on');
}
}
if (hud.lock) hud.lock.classList.toggle('disabled', !featAutoLock);
}
if (cuddling && cuddleTarget && featCuddleFollow) {
if (kcc._horizontal || kcc._vertical) {
cuddling = false;
cuddleTarget = null;
console.log('[cuddle] cancelled (movement input)');
} else {
const tPos = cuddleTarget.getPosition?.();
if (tPos) {
kcc.gravity = 0;
kcc._velY = 0;
teleport(player, vec3(tPos));
} else {
cuddling = false;
cuddleTarget = null;
console.log('[cuddle] target lost');
}
}
}
if (!flyActive) return;
if (kcc._grounded && Date.now() - _flyStartTime > 500) { flyOff(kcc, false); return; }
if (kcc.gravity !== 0) kcc.gravity = 0;
flyVelY = kcc._velY;
const flySpeedCap = sprinting ? sprintSpeed : SPEED_CAP;
if (flyUp) {
if (flyVelY < 0) {
flyVelY = 0;
} else if (accelEnabled) {
if (flyVelY < FLY_MIN_SPEED) flyVelY = FLY_MIN_SPEED;
flyVelY = Math.min(flyVelY + ACCEL * dt, flySpeedCap);
} else {
flyVelY = flySpeedCap;
}
} else if (flyDown) {
if (flyVelY > 0) {
flyVelY = 0;
} else if (accelEnabled) {
if (flyVelY > -FLY_MIN_SPEED) flyVelY = -FLY_MIN_SPEED;
flyVelY = Math.max(flyVelY - ACCEL * dt, -flySpeedCap);
} else {
flyVelY = -flySpeedCap;
}
} else {
flyVelY = 0;
}
kcc._velY = flyVelY;
}
requestAnimationFrame(_tick);
})();