// ==UserScript==
// @name Meeland Enhancement Suite
// @namespace meeland-script
// @version 5.9.1
// @match *://*/*
// @run-at document-end
// @license MIT
// @grant none
// @description Meeland.io cheat script with auto-lock, speed boost, infinite jump, teleportation & more! Works on CrazyGames, twoplayergames.org, meeland.io, iogames.onl, sprunki-game.io, gameflare.com and other sites hosting Meeland
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
const W = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
if (!W.pc?.app?.root) return;
const STORE_KEY = 'ml_wp';
const CFG_KEY = 'ml_cfg';
let ACCEL = 6;
let FLY_MIN_SPEED = 10;
let SPEED_CAP = 100;
let GRAVITY = -18;
let SPEED_DEFAULT = 7;
let flyActive = false;
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 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 featFastSync = false;
let featFreeMoney = true;
let featAutoCollect = true;
let autoCollectInterval = 30;
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',
};
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, featFastSync, featFreeMoney, featAutoCollect,
autoCollectInterval,
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('featFastSync', v => featFastSync = v);
assign('featFreeMoney', v => featFreeMoney = v);
assign('featAutoCollect', v => featAutoCollect = v);
assign('autoCollectInterval', v => autoCollectInterval = 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();
const getPlayer = () => W.pc?.app?.root?.findByName('Player') ?? null;
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); } };
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 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-fastsync','ml-f-freemoney','ml-f-autocollect'];
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);
}
function saveWaypoints() {
try {
localStorage.setItem(STORE_KEY, JSON.stringify({
home: homePos ? vec3(homePos) : null,
back: backPos ? vec3(backPos) : null,
slots: slots.map(s => s ? vec3(s) : null),
}));
} 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) {
flyActive = true;
flyUp = true;
flyVelY = FLY_MIN_SPEED;
kcc.gravity = 0;
kcc._velY = FLY_MIN_SPEED;
}
function flyOff(kcc, resetVel = true) {
flyActive = false;
flyUp = false;
flyDown = false;
flyVelY = 0;
kcc.gravity = GRAVITY;
if (resetVel) kcc._velY = 0;
}
const DESCRIPTION = `
Meeland Script — Cheat & Enhancement Suite
General-purpose cheat script for Meeland.io. Works across all game modes — Meeland Hub , Escape Waves , Steal a Pet , and Obby Tower — on all supported platforms.
Designed for PC. Mobile has basic support (collapsible HUD) but keybinds require a keyboard.
This script is 100% free and always will be. If you have a suggestion, bug report, or something you'd like changed — awesome, leave it in a positive review and it'll get looked at. Dropping a 1-star review over a misunderstanding or a missing feature is the fastest way to make sure nothing gets done about it. Good reviews keep development alive. ⭐
Features
🚀 Fly Hack — Press Space to activate. Hold Space to ascend, F to descend. Throttle-based acceleration with configurable speed cap and accel rate. Auto-lands on ground contact.
⚡ Speed Hack — Hold Shift for accelerating speed boost. Configurable cap and base speed.
📍 Waypoints — Q saves home, \` teleports to home, Z toggles back position. 10 save slots via Ctrl+Numpad 0-9 , recall with Numpad 0-9 .
🐾 Cuddle Panel — J opens a player list sorted by distance. Click a player to teleport to them. Continuous cuddle follows the target in real time (cancelled by moving).
🐕 Pet Browser — K opens a full pet table with sortable columns (name, mutation, rarity, owner, worth, income, distance). Filter pets with a powerful search supporting AND, OR, and brackets. Teleport to any pet or grab wild/others' pets directly from the table. Configurable auto-refresh interval.
🔒 Auto-Lock Base — Automatically locks your base in Steal a Pet when the lockdown timer expires. Shows lock status and countdown in the HUD.
🛡️ Anti-Disconnect — Prevents getting kicked when entering another player's locked base by spoofing your position to the server.
💀 Anti-Death — Immune to falling off the map and wave kills in Escape Waves.
💪 Anti-Knockback — Immune to attacks and knockback from other players.
👻 Noclip — Walk through walls and obstacles. Off by default.
📡 Fast Sync — 4× faster position updates to the server. Off by default.
💰 Free Daily Money — Instantly collects all free daily coins without watching ads. Runs automatically once the game connects — no interaction needed.
🐾 Auto-Collect Pet Earnings — Automatically claims accumulated income from all your base pets every 30 seconds. No need to walk over the pads.
⚙️ Settings Panel — M opens settings. Adjust speed cap, accel rate, base speed, cuddle panel refresh interval, and toggle individual features on/off. Reset to defaults with one click.
🎮 Rebindable Keybindings — All hotkeys can be rebound from the settings panel. Changes persist across sessions.
🖱️ Draggable & Resizable Panels — All panels (cuddle, settings, keybinds, pets, help) can be dragged by their headers and resized. Positions reset on close.
Keyboard Shortcuts
All keybindings are rebindable via Settings → Keybindings.
Space — Toggle fly / fly up
F (hold) — Fly down
Shift (hold) — Speed hack
Q — Set home waypoint
\` — Teleport to home
Z — Teleport back (toggle)
J — Cuddle panel (player list)
K — Pet browser
M — Settings panel
? — This help dialog
Ctrl+Numpad 0-9 — Save position to slot
Numpad 0-9 — Recall position from slot
Pet Browser
Click column headers to sort (name, mutation, rarity, owner, worth, income, distance)
Use the search bar to filter — words are AND'd, use OR for alternatives, brackets to group
➜ button teleports to a pet
✋ button grabs a pet (teleports, buys, returns, drops at your position)
Grabbed pets show ✔ for 5 seconds
Toggle auto-refresh and adjust interval (1-10s) in the header
Installation
Install a userscript manager:
Click Install above
Load any Meeland game — the script activates automatically
Supported Sites
✅ meeland.io
✅ CrazyGames
✅ twoplayergames.org
✅ iogames.onl
✅ sprunki-game.io
✅ gameflare.com
✅ Any site embedding Meeland in an iframe
Privacy
✅ Client-side only — no data collected, no external requests
Disclaimer
For educational and entertainment purposes. Use at your own risk.
`;
function createHUD() {
if ($('ml-hud')) return;
const s = document.createElement('style');
s.textContent = [
'#ml-hud{position:fixed;bottom:14px;right:14px;display:flex;flex-wrap:wrap-reverse;justify-content:flex-end;align-items:flex-end;gap:5px;z-index:99999;pointer-events:none;font-family:monospace;user-select:none;max-width:calc(100vw - 28px)}',
'#ml-hud-toggle{display:none;pointer-events:auto;cursor:pointer;background:rgba(0,0,0,.6);color:#daa520;font-weight:900;font-size:11px;border:1px solid rgba(218,165,32,.4);border-radius:99px;padding:3px 8px;font-family:monospace;line-height:1}',
'#ml-hud.collapsed .ml-b{display:none}',
'#ml-hud.collapsed #ml-hud-toggle{display:inline-block}',
'@media (pointer:coarse),(max-width:768px){#ml-hud{bottom:4px;right:4px;gap:3px;max-width:calc(100vw - 8px)}#ml-hud-toggle{display:inline-block}.ml-b{font-size:9px;padding:2px 4px}}',
'.ml-b{padding:2px 5px;border-radius:99px;font-size:11px;font-weight:700;letter-spacing:0;background:rgba(0,0,0,.45);color:#daa520;transition:color .12s,background .12s;position:relative}',
'.ml-b.act{pointer-events:auto;cursor:pointer}',
'.ml-b.act:hover{color:#fff;background:rgba(255,255,255,.1)}',
'.ml-b.on{color:#fff;background:rgba(60,220,140,.55)}',
'.ml-b.fresh{color:#fff;background:rgba(60,220,140,.75)}',
'.ml-b.disabled{color:rgba(255,60,60,.45);text-decoration:line-through;text-decoration-color:rgba(255,40,40,.7);text-decoration-thickness:2px;background:rgba(120,0,0,.25)}',
'#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}',
'#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:thin;scrollbar-color:rgba(160,120,240,.25) transparent}',
'#ml-plist-body::-webkit-scrollbar{width:4px}',
'#ml-plist-body::-webkit-scrollbar-thumb{background:rgba(160,120,240,.3);border-radius:4px}',
'#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:thin;scrollbar-color:rgba(160,120,240,.25) transparent}',
'#ml-settings-body::-webkit-scrollbar{width:4px}',
'#ml-settings-body::-webkit-scrollbar-thumb{background:rgba(160,120,240,.3);border-radius:4px}',
'.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:thin;scrollbar-color:rgba(140,100,220,.2) transparent}',
'#ml-pets-body::-webkit-scrollbar{width:4px}',
'#ml-pets-body::-webkit-scrollbar-thumb{background:rgba(140,100,220,.25);border-radius:4px}',
'#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}',
].join('');
document.head.appendChild(s);
const h = document.createElement('div');
h.id = 'ml-hud';
h.innerHTML = 'ML FLY (SPC) SPR (Shft) SETHOME (Q) HOME (`) BACK (Z) SLOTS (0/10) 🔒 LOCK CUDDLE (J) PETS (K) ⚙ (M) ? ';
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',
};
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);
[plist, settings, d, petsPanel].forEach(el => {
['mousedown','mouseup','click','pointerdown','pointerup','mousemove','mouseover','mouseenter','mouseleave','wheel','contextmenu','pointermove','pointerover','pointerenter'].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';
handle.addEventListener('mousedown', e => {
if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT' || e.target.tagName === 'LABEL') return;
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 = e.clientX - rect.left;
oy = e.clientY - rect.top;
e.preventDefault();
});
window.addEventListener('mousemove', e => {
if (!dragging) return;
panel.style.left = Math.max(0, Math.min(e.clientX - ox, window.innerWidth - 40)) + 'px';
panel.style.top = Math.max(0, Math.min(e.clientY - oy, window.innerHeight - 40)) + 'px';
}, true);
window.addEventListener('mouseup', () => {
if (dragging) { dragging = false; handle.style.cursor = 'grab'; }
}, 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(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, featFastSync, featFreeMoney, featAutoCollect];
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 = null;
try { username = enemy.script?.enemy?.usernameEntity?.element?.text; } catch (_) {}
if (!username) {
const ue = enemy.findByName?.('Username');
if (ue?.element?.text) username = ue.element.text;
}
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 = player.getPosition().clone();
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;
let name = null;
try { name = child.script?.enemy?.usernameEntity?.element?.text; } catch (_) {}
if (!name) { const ue = child.findByName?.('Username'); if (ue?.element?.text) name = ue.element.text; }
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;
}
let _pmCache = null;
let _pmCacheTime = 0;
function getPetsManager() {
const now = Date.now();
if (_pmCache && now - _pmCacheTime < 5000 && (_pmCache.activePets || _pmCache.basePets)) return _pmCache;
const pmEntity = W.pc?.app?.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) {
const root = W.pc?.app?.root;
if (!root) return null;
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;
});
}
_pmCache = pm || null;
_pmCacheTime = now;
return _pmCache;
}
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 {
const mutNames = ['Golden', 'Diamond', 'Emerald', 'Rainbow', 'Galaxy'];
for (const mn of mutNames) {
if (pet.findByName?.(`Mutation ${mn} Effect`)) { mutation = mn; break; }
}
} 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(); // token → expiry timestamp
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(() => {
const tgt = app.graphicsDevice?.canvas || document;
const opts = { code: 'KeyX', key: 'x', keyCode: 88, which: 88, bubbles: true, cancelable: true };
tgt.dispatchEvent(new KeyboardEvent('keydown', opts));
setTimeout(() => {
tgt.dispatchEvent(new KeyboardEvent('keyup', opts));
console.log(`[ml] drop pet at origin`);
grabbedTokens.set(tokenStr, Date.now() + 5000);
renderPetTable();
}, 50);
}, 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();
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 resumeGame() {
const app = W.pc?.app;
const canvas = app?.graphicsDevice?.canvas;
if (canvas) canvas.requestPointerLock();
if (app) app.fire('GameManager:GameResumed');
}
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();
if (document.pointerLockElement) document.exitPointerLock();
} else {
stopPetRefresh();
petsPanel._resetPos?.();
resumeGame();
}
}
$('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();
});
function toggleCuddlePanel(forceOpen) {
const wasOpen = plist.classList.contains('open');
const open = forceOpen !== undefined ? forceOpen : !wasOpen;
plist.classList.toggle('open', open);
if (open) {
refreshPlayerList();
if (document.pointerLockElement) document.exitPointerLock();
if (!wasOpen && autoRefresh) startRefreshTimer();
} else if (wasOpen) {
stopRefreshTimer();
plist._resetPos?.();
resumeGame();
}
}
const origRequestPointerLock = Element.prototype.requestPointerLock;
Element.prototype.requestPointerLock = function() {
if (plist.classList.contains('open') || settings.classList.contains('open') || petsPanel.classList.contains('open') || d.classList.contains('open')) return;
return origRequestPointerLock.call(this);
};
const anyPanelOpen = () => plist.classList.contains('open') || settings.classList.contains('open') || petsPanel.classList.contains('open') || d.classList.contains('open');
document.addEventListener('pointerlockchange', e => {
if (anyPanelOpen() && !document.pointerLockElement) {
e.stopImmediatePropagation();
}
if (document.pointerLockElement) {
const app = W.pc?.app;
if (app) app.fire('GameManager:GameResumed');
console.log('[guard] pointer lock active → forced GameResumed');
}
}, true);
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-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 && document.pointerLockElement) document.exitPointerLock();
else if (!open) { kbListeningRow = null; settings._resetPos?.(); resumeGame(); }
}
$('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; featFastSync = false; featFreeMoney = true; featAutoCollect = 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-fastsync', get: () => featFastSync, set: v => { featFastSync = 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; } },
];
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}`);
});
});
$('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-cfg': ['⚙', 'settings'],
};
for (const [id, [label, action]] of Object.entries(map)) {
const el = $(id);
if (!el) continue;
if (!action) { el.textContent = label + ' (Shft)'; continue; }
el.textContent = label + ' (' + keyCodeLabel(KEYBINDS[action]) + ')';
}
}
updateHUDBadgeLabels();
$('ml-hud-toggle').addEventListener('click', e => {
if (e.target._wasDragged) { e.target._wasDragged = false; return; }
const hud = $('ml-hud');
const wasCollapsed = hud.classList.contains('collapsed');
hud.classList.toggle('collapsed');
if (wasCollapsed) {
hud.style.left = '';
hud.style.top = '';
hud.style.right = '';
hud.style.bottom = '';
}
});
(function() {
const toggle = $('ml-hud-toggle');
const hud = $('ml-hud');
let dragging = false, ox = 0, oy = 0, moved = false;
toggle.addEventListener('pointerdown', e => {
dragging = true; moved = false;
const rect = hud.getBoundingClientRect();
ox = e.clientX - rect.left;
oy = e.clientY - rect.top;
toggle.setPointerCapture(e.pointerId);
});
toggle.addEventListener('pointermove', e => {
if (!dragging) return;
moved = true;
hud.style.left = Math.max(0, Math.min(e.clientX - ox, window.innerWidth - 40)) + 'px';
hud.style.top = Math.max(0, Math.min(e.clientY - oy, window.innerHeight - 40)) + 'px';
hud.style.right = 'auto';
hud.style.bottom = 'auto';
});
toggle.addEventListener('pointerup', () => {
dragging = false;
if (moved) toggle._wasDragged = true;
});
})();
$('ml-help').addEventListener('click', () => {
d.classList.add('open');
if (document.pointerLockElement) document.exitPointerLock();
});
$('ml-close').addEventListener('click', () => { d.classList.remove('open'); d.querySelector('#ml-inner')._resetPos?.(); resumeGame(); });
d.addEventListener('click', e => { if (e.target === d) { d.classList.remove('open'); d.querySelector('#ml-inner')._resetPos?.(); resumeGame(); } });
d.addEventListener('wheel', e => { e.stopPropagation(); e.preventDefault(); d.scrollTop += e.deltaY; }, { passive: false });
document.addEventListener('click', e => {
if (plist.classList.contains('open') && !plist.contains(e.target) && e.target.id !== 'ml-tp') {
toggleCuddlePanel(false);
}
if (settings.classList.contains('open') && !settings.contains(e.target) && e.target.id !== 'ml-cfg') {
toggleSettings(false);
}
if (petsPanel.classList.contains('open') && !petsPanel.contains(e.target) && e.target.id !== 'ml-pets-btn') {
togglePetsPanel(false);
}
});
}
// ── Free Daily Money ──────────────────────────────────
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;
// Wait until player is connected to a room
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 + ')');
}
// ── Auto-Collect Pet Earnings ─────────────────────────
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);
}
}
// Release cached balance update after coins reach target (~1.5s)
if (_mlCachedBalance !== null && app?._mlOrigFire) {
const bal = _mlCachedBalance;
_mlCachedBalance = null;
setTimeout(() => {
app._mlOrigFire.call(app, 'ModeOverlay:Balance', bal);
}, 1500);
}
} else if (_mlCachedBalance !== null) {
// No earnings but balance was cached — release immediately
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;
const root = app.root;
// Hook app.fire once to batch BalanceChange notifications
if (!app._mlNotifBatched) {
app._mlNotifBatched = true;
const origFire = app.fire;
app._mlOrigFire = origFire;
app.fire = function() {
if (_mlBatchCollecting) {
if (arguments[0] === 'ModeOverlay:BalanceChange') {
const amt = arguments[1];
_mlBatchTotal += (typeof amt === 'number' ? amt : 0);
_mlBatchCount++;
clearTimeout(_mlBatchTimer);
_mlBatchTimer = setTimeout(flushBatch, 2000);
return app;
}
if (arguments[0] === 'ModeOverlay:Balance') {
_mlCachedBalance = arguments[1];
return app;
}
}
return origFire.apply(this, arguments);
};
}
// Find PetsManager
let pm = null;
const pmEntity = root.findByName('PetsManager');
if (pmEntity?.script) {
for (const k of Object.keys(pmEntity.script)) {
const s = pmEntity.script[k];
if (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?.basePets) { pm = s; return true; }
}
return false;
});
}
if (!pm?.basePets || pm.basePets.size === 0) return;
const nm = root.findByName('NetworkManager')?.script?.networkManager;
if (!nm?.room?.sessionId) return;
const myId = nm.room.sessionId;
// Start batching before claims
_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');
// Fallback flush if no server responses arrive
_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 => {
if (!Array.isArray(data)) return;
if (petSpawnData.size > PET_SPAWN_CAP) {
const excess = petSpawnData.size - PET_SPAWN_CAP + data.length;
const it = petSpawnData.keys();
for (let i = 0; i < excess; i++) petSpawnData.delete(it.next().value);
}
for (const p of data) {
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');
}
// ── Auto-Lock Base ──────────────────────────────────────
function getMyBase() {
const app = W.pc?.app;
if (!app?.root) return null;
const basesEntity = app.root.findByName('Bases');
const basesScript = basesEntity?.script?.petTycoonBasesManager;
if (!basesScript?.activeBases) return null;
const nm = app.root.findByName('NetworkManager')?.script?.networkManager;
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;
}
function triggerLock() {
const btn = getLockBtn();
if (!btn || typeof btn.onTriggerEnter !== 'function') return false;
const player = getPlayer();
if (!player) return false;
const wasTrig = btn.hasTriggered;
btn.onTriggerEnter(player);
console.log('[ml] auto-lock: triggered (wasTriggered=%s)', wasTrig);
return true;
}
let lastLockCheck = 0;
// ── Anti-Disconnect ─────────────────────────────────────
// Server kicks players whose position is inside a locked base.
// Spoof outgoing position to last safe location when inside one.
let lastSafePos = null;
const BASE_RADIUS_SQ = 20 * 20;
function isInsideLockedBase(x, z) {
const basesScript = W.pc?.app?.root?.findByName('Bases')?.script?.petTycoonBasesManager;
if (!basesScript?.activeBases) return false;
const myId = W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager?.room?.sessionId || W.pc?.sessionId;
for (const entry of basesScript.activeBases) {
if (!entry?.entity || entry.data?.sessionId === myId) continue;
const lockBtn = entry.entity.findByName('LockdownButton')?.script?.lockdownButton;
if (!lockBtn?.isLockdownActive || !(lockBtn.lockdownTimeLeft > 0)) continue;
const bp = entry.entity.getPosition();
const dx = x - bp.x, dz = z - bp.z;
if (dx * dx + dz * dz < BASE_RADIUS_SQ) return true;
}
return false;
}
function hookAntiDisconnect() {
const nm = W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager;
const room = nm?.room;
if (!room || room.__mlSendHooked) return;
room.__mlSendHooked = true;
const origSend = room.send.bind(room);
room.send = function (type, message) {
if (type === 'p' && message) {
if (isInsideLockedBase(message.x, message.z)) {
const safe = lastSafePos || (() => {
const b = getMyBase()?.getPosition();
return b ? { x: b.x, y: b.y + 1, z: b.z } : null;
})();
if (safe) return origSend(type, { x: safe.x, y: safe.y, z: safe.z, w: message.w });
} else {
lastSafePos = { x: message.x, y: message.y, z: message.z };
}
}
return origSend(type, message);
};
console.log('[ml] anti-disconnect: position spoof active');
}
// ── Anti-Death ──────────────────────────────────────────
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.fire = function (event, ...args) {
if (event === 'DeathScreen:Trigger') return this;
if (event === 'PlayerController:GotHit' && featAntiKnockback) return this;
return origFire.call(this, event, ...args);
};
antiDeathReady = true;
console.log('[ml] anti-death: ready');
}
// ── Anti-Knockback ──────────────────────────────────────
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;
}
// ── Noclip ──────────────────────────────────────────────
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');
}
// ── Fast Sync ───────────────────────────────────────────
let fastSyncReady = false;
function hookFastSync() {
if (fastSyncReady) return;
const player = getPlayer();
const pc = getPC(player);
if (!pc) return;
pc._origBroadcastInterval = pc.broadcastInterval;
fastSyncReady = true;
}
// ── Network Spy ───────────────────────────────────────
let netSpyHooked = false;
function hookNetSpy() {
if (netSpyHooked) return;
const nm = W.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager;
if (!nm?.room) return;
netSpyHooked = true;
const NOISY = new Set(['positions', 'animSetBooleans', 'animSetTrigger']);
const origOnMessage = nm.room.onMessage.bind(nm.room);
nm.room.onMessage = function (type, cb) {
return origOnMessage(type, function (...args) {
if (!NOISY.has(type)) {
console.log('[ml] NET ←', type, args.length === 1 ? args[0] : args);
}
return cb(...args);
});
};
const prevSend = nm.room.send;
nm.room.send = function (type, message) {
if (type !== 'p' && !NOISY.has(type)) {
console.log('[ml] NET →', type, message);
}
return prevSend.call(this, type, message);
};
console.log('[ml] net-spy: active');
}
createHUD();
loadWaypoints();
window.addEventListener('keydown', e => {
const active = document.activeElement;
if (active?.tagName === 'INPUT' || active?.tagName === 'TEXTAREA') return;
if (kbListeningRow) return;
if (e.code === KEYBINDS.cuddle) {
e.preventDefault();
if (featCuddle) $('ml-tp')?.click();
return;
}
if (e.code === KEYBINDS.settings) {
e.preventDefault();
$('ml-cfg')?.click();
return;
}
if (e.code === KEYBINDS.pets) {
e.preventDefault();
if (featPets) $('ml-pets-btn')?.click();
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)$/);
if (numpadMatch && featWaypoints) {
const idx = parseInt(numpadMatch[1]);
if (e.ctrlKey) {
e.preventDefault();
e.stopPropagation();
slots[idx] = player.getPosition().clone();
saveWaypoints();
flash('ml-slots');
console.log(`[slot ${idx}] saved`, slots[idx]);
return;
} else if (slots[idx]) {
e.preventDefault();
e.stopPropagation();
backPos = player.getPosition().clone();
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 = player.getPosition().clone();
saveWaypoints();
flash('ml-home');
return;
}
if (e.code === KEYBINDS.home) {
e.preventDefault();
if (!featWaypoints) return;
if (homePos) {
backPos = player.getPosition().clone();
teleport(player, homePos);
saveWaypoints();
flash('ml-go');
}
return;
}
if (e.code === KEYBINDS.back) {
e.preventDefault();
if (!featWaypoints) return;
if (backPos) {
const cur = player.getPosition().clone(); teleport(player, backPos); backPos = cur;
saveWaypoints();
flash('ml-back');
}
}
}, true);
window.addEventListener('keyup', e => {
if (e.code === KEYBINDS.fly) flyUp = false;
if (e.code === KEYBINDS.flyDown) flyDown = false;
if (e.key === 'Shift' && sprinting) { sprinting = false; }
}, true);
const hud = { fly: $('ml-fly'), spr: $('ml-spr'), home: $('ml-home'), go: $('ml-go'), back: $('ml-back'), slots: $('ml-slots'), lock: $('ml-lock'), tp: $('ml-tp'), petsBtn: $('ml-pets-btn'), cfg: $('ml-cfg'), help: $('ml-help') };
const panels = { plist: $('ml-plist'), settings: $('ml-settings'), dialog: $('ml-dialog'), pets: $('ml-pets') };
setInterval(() => {
hookPetSpawn();
hookAntiDisconnect();
hookAntiDeath();
hookAntiKnockback();
hookNoclip();
hookFastSync();
hookNetSpy();
if (!freeMoneyDone && featFreeMoney) autoFarmFreeMoney();
autoCollectPetEarnings();
const player = getPlayer();
if (!player) return;
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 (fastSyncReady && pc) {
pc.broadcastInterval = featFastSync ? Math.round(pc._origBroadcastInterval / 4) : pc._origBroadcastInterval;
}
if (sprinting) {
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 (hud.fly) { hud.fly.classList.toggle('on', flyActive); hud.fly.classList.toggle('disabled', !featFly); }
if (hud.spr) { hud.spr.classList.toggle('on', sprinting); hud.spr.classList.toggle('disabled', !featSprint); }
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.cfg) hud.cfg.classList.toggle('on', !!panels.settings?.classList.contains('open'));
if (hud.help) hud.help.classList.toggle('on', !!panels.dialog?.classList.contains('open'));
// Auto-lock base: check every ~100ms
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 (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) {
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) { flyOff(kcc, false); return; }
if (kcc.gravity !== 0) kcc.gravity = 0;
flyVelY = kcc._velY;
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, SPEED_CAP);
} else {
flyVelY = SPEED_CAP;
}
} 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, -SPEED_CAP);
} else {
flyVelY = -SPEED_CAP;
}
} else {
flyVelY = 0;
}
kcc._velY = flyVelY;
}, 16);
})();