// ==UserScript== // @name Meeland Enhancement Suite // @namespace meeland-script // @version 5.0.0 // @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'; if (!window.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 autoRefresh = true; let refreshInterval = 10; let kbListeningRow = null; let petFilter = ''; 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, refreshInterval, autoRefresh, featFly, featSprint, featWaypoints, featCuddle, featCuddleFollow, featPets, keybinds: { ...KEYBINDS }, petFilter, 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('refreshInterval', v => refreshInterval = v); assign('autoRefresh', v => autoRefresh = 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('petFilter', v => petFilter = 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 = () => window.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) => { const el = $(id), ve = $(valId); if (el) el.value = v; if (ve) ve.textContent = 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']; const MOVE_KEYS = new Set(['KeyW','KeyA','KeyS','KeyD','ArrowUp','ArrowDown','ArrowLeft','ArrowRight']); const setSpeed = (pc, kcc, v) => { if (pc) { pc.currentSpeed = v; pc.speed = v; } if (kcc.speed !== undefined) kcc.speed = v; }; 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 = 0; kcc.gravity = 0; kcc._velY = 0; } 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.

Sorry for the long gap between updates — a rating helps if it works for you. ⭐

Features

Keyboard Shortcuts

All keybindings are rebindable via Settings → Keybindings.

Pet Browser

Installation

  1. Install a userscript manager:
  2. Click Install above
  3. Load any Meeland game — the script activates automatically

Supported Sites

Privacy

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:12px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:linear-gradient(170deg,rgba(15,20,35,.94) 0%,rgba(20,28,50,.96) 50%,rgba(12,16,30,.97) 100%);backdrop-filter:blur(24px) saturate(1.4);-webkit-backdrop-filter:blur(24px) saturate(1.4);border:1px solid rgba(100,180,255,.22);box-shadow:0 0 0 1px rgba(60,140,255,.08),0 4px 24px rgba(0,0,0,.7),0 12px 48px rgba(0,0,0,.5),inset 0 1px 0 rgba(140,200,255,.15),inset 0 -1px 0 rgba(0,0,0,.3),0 0 20px rgba(60,140,255,.06);user-select:none}', '#ml-plist.open{display:flex;flex-direction:column}', '#ml-plist-head{padding:11px 14px 9px;border-bottom:1px solid rgba(80,160,255,.2);display:flex;align-items:center;justify-content:space-between;background:linear-gradient(180deg,rgba(40,100,200,.12) 0%,rgba(30,70,150,.04) 100%)}', '#ml-plist-title{font-size:12px;font-weight:800;color:rgba(200,225,255,.95);text-transform:uppercase;letter-spacing:.12em;text-shadow:0 0 8px rgba(80,160,255,.3),0 1px 2px rgba(0,0,0,.5)}', '#ml-plist-timer{font-size:10px;color:rgba(120,180,255,.55);font-variant-numeric:tabular-nums;margin-left:6px;text-shadow:0 0 4px rgba(60,140,255,.2)}', '#ml-plist-refresh{cursor:pointer;background:linear-gradient(180deg,rgba(50,110,200,.2) 0%,rgba(40,90,170,.12) 100%);border:1px solid rgba(80,160,255,.28);color:rgba(170,210,255,.8);font-size:12px;padding:3px 9px;border-radius:6px;font-family:inherit;transition:all .15s;text-shadow:0 1px 2px rgba(0,0,0,.3)}', '#ml-plist-refresh:hover{background:linear-gradient(180deg,rgba(50,120,220,.35) 0%,rgba(40,100,190,.2) 100%);color:#e0edff;border-color:rgba(80,170,255,.45);box-shadow:0 0 10px rgba(60,140,255,.15)}', '#ml-plist-close{cursor:pointer;background:none;border:none;color:rgba(180,210,255,.45);font-size:18px;padding:0 4px;font-family:monospace;line-height:1;transition:all .15s}', '#ml-plist-close:hover{color:#ff6b6b;text-shadow:0 0 8px rgba(255,80,80,.4)}', '#ml-plist-body{overflow-y:auto;flex:1;padding:4px 8px 6px;scrollbar-width:thin;scrollbar-color:rgba(80,160,255,.2) transparent}', '#ml-plist-body::-webkit-scrollbar{width:5px}', '#ml-plist-body::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(60,140,255,.25),rgba(80,160,255,.15));border-radius:3px}', '#ml-plist-empty{color:rgba(140,190,255,.45);text-align:center;padding:20px 8px;font-size:12px;font-style:italic;text-shadow:0 1px 2px rgba(0,0,0,.3)}', '.ml-prow{display:flex;align-items:center;gap:8px;padding:6px 9px;margin:2px 0;border-radius:7px;cursor:pointer;transition:all .15s;border:1px solid transparent;background:rgba(20,35,60,.25)}', '.ml-prow:hover{background:linear-gradient(90deg,rgba(40,90,180,.2) 0%,rgba(50,110,200,.12) 100%);border-color:rgba(80,160,255,.2);box-shadow:0 0 8px rgba(60,140,255,.08),inset 0 0 12px rgba(60,140,255,.04)}', '.ml-prow:active{background:rgba(50,110,200,.28);transform:scale(.98);border-color:rgba(80,160,255,.3)}', '.ml-pnum{font-size:10px;color:rgba(100,170,255,.6);font-weight:700;min-width:16px;text-align:right;font-variant-numeric:tabular-nums;text-shadow:0 0 4px rgba(60,140,255,.15)}', '.ml-pinfo{flex:1;overflow:hidden}', '.ml-pname{font-size:12.5px;font-weight:600;color:rgba(230,240,255,.95);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3;text-shadow:0 1px 3px rgba(0,0,0,.4)}', '.ml-pdist{font-size:10px;color:rgba(120,180,255,.6);line-height:1.2;text-shadow:0 1px 2px rgba(0,0,0,.3)}', '.ml-parrow{color:rgba(80,160,255,.3);font-size:14px;transition:all .15s;text-shadow:0 0 4px rgba(60,140,255,.1)}', '.ml-prow:hover .ml-parrow{color:rgba(100,180,255,.75);transform:translateX(3px);text-shadow:0 0 8px rgba(60,140,255,.3)}', '#ml-settings{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:340px;max-height:80vh;display:none;z-index:100002;pointer-events:auto;overflow:hidden;border-radius:4px;font-family:"Courier New",Courier,monospace;user-select:none}', '#ml-settings.open{display:flex;flex-direction:column}', '#ml-settings-bg{position:absolute;inset:0;border-radius:4px;background:linear-gradient(175deg,#10141e 0%,#171d2c 15%,#1a2236 30%,#1e2840 50%,#1a2236 70%,#171d2c 85%,#10141e 100%);box-shadow:inset 0 2px 0 rgba(160,200,255,.18),inset 0 -2px 0 rgba(0,0,0,.5),inset 2px 0 0 rgba(100,160,240,.06),inset -2px 0 0 rgba(100,160,240,.06),0 0 0 1px rgba(70,130,220,.3),0 0 0 3px rgba(10,15,25,.8),0 0 0 4px rgba(70,130,220,.15),0 20px 60px rgba(0,0,0,.75),0 0 30px rgba(40,100,200,.08);border:none}', '#ml-settings-bg::before{content:"";position:absolute;inset:0;border-radius:4px;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(100,170,255,.018) 2px,rgba(100,170,255,.018) 3px),repeating-linear-gradient(90deg,transparent,transparent 40px,rgba(100,170,255,.012) 40px,rgba(100,170,255,.012) 41px);pointer-events:none}', '#ml-settings-bg::after{content:"";position:absolute;inset:0;border-radius:4px;background:radial-gradient(ellipse at 30% 0%,rgba(60,140,255,.12) 0%,transparent 45%),radial-gradient(ellipse at 70% 100%,rgba(40,100,200,.08) 0%,transparent 40%),radial-gradient(circle at 50% 50%,rgba(50,120,220,.02) 0%,transparent 70%);pointer-events:none}', '#ml-settings-head{position:relative;padding:14px 18px 11px;border-bottom:2px solid rgba(60,140,255,.25);display:flex;align-items:center;justify-content:space-between;background:linear-gradient(180deg,rgba(50,110,200,.14) 0%,rgba(30,70,150,.05) 60%,transparent 100%);box-shadow:0 1px 0 rgba(60,140,255,.1)}', '#ml-settings-title{font-size:14px;font-weight:800;color:rgba(180,215,255,.95);letter-spacing:.2em;text-transform:uppercase;text-shadow:0 0 14px rgba(60,140,255,.4),0 0 28px rgba(60,140,255,.15),0 1px 2px rgba(0,0,0,.6)}', '#ml-settings-close{cursor:pointer;background:none;border:none;color:rgba(160,200,255,.4);font-size:20px;padding:0 4px;font-family:monospace;line-height:1;transition:all .15s}', '#ml-settings-close:hover{color:#ff5555;text-shadow:0 0 12px rgba(255,60,60,.5)}', '#ml-settings-reset{cursor:pointer;background:linear-gradient(180deg,rgba(50,100,180,.18) 0%,rgba(35,75,140,.1) 100%);border:1px solid rgba(60,140,255,.3);color:rgba(160,205,255,.7);font-size:9px;padding:4px 12px;border-radius:2px;font-family:"Courier New",Courier,monospace;letter-spacing:.12em;text-transform:uppercase;transition:all .15s;text-shadow:0 0 6px rgba(60,140,255,.2);box-shadow:0 0 0 1px rgba(60,140,255,.05)}', '#ml-settings-reset:hover{color:#e0edff;background:linear-gradient(180deg,rgba(50,110,200,.3) 0%,rgba(40,90,170,.18) 100%);border-color:rgba(60,150,255,.5);box-shadow:0 0 12px rgba(60,140,255,.15),0 0 0 1px rgba(60,140,255,.1)}', '#ml-settings-body{position:relative;overflow-y:auto;flex:1;padding:10px 14px 14px;scrollbar-width:thin;scrollbar-color:rgba(60,140,255,.25) transparent}', '#ml-settings-body::-webkit-scrollbar{width:4px}', '#ml-settings-body::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(60,140,255,.3),rgba(80,160,255,.15));border-radius:2px}', '.ml-sgroup{margin:0 0 8px}', '.ml-slabel{font-size:9.5px;color:rgba(80,160,255,.7);text-transform:uppercase;letter-spacing:.2em;padding:8px 2px 4px;font-weight:800;border-bottom:1px solid rgba(60,140,255,.15);text-shadow:0 0 8px rgba(60,140,255,.2);background:linear-gradient(90deg,rgba(60,140,255,.04) 0%,transparent 80%)}', '.ml-srow{display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-radius:3px;transition:all .12s;border:1px solid transparent}', '.ml-srow:hover{background:linear-gradient(90deg,rgba(40,100,200,.12) 0%,rgba(50,110,200,.06) 100%);border-color:rgba(60,140,255,.08)}', '.ml-srow-label{font-size:11.5px;color:rgba(200,220,250,.85);flex:1;text-shadow:0 1px 2px rgba(0,0,0,.4)}', '.ml-srow-value{font-size:10.5px;color:rgba(100,175,255,.75);font-variant-numeric:tabular-nums;min-width:34px;text-align:right;margin-right:6px;text-shadow:0 0 6px rgba(60,140,255,.15)}', '.ml-toggle{position:relative;width:34px;height:18px;border-radius:2px;background:linear-gradient(180deg,rgba(25,35,55,.9) 0%,rgba(20,30,48,.95) 100%);border:1px solid rgba(60,130,220,.3);cursor:pointer;transition:all .18s;flex-shrink:0;box-shadow:inset 0 1px 3px rgba(0,0,0,.4),0 0 0 1px rgba(60,140,255,.05)}', '.ml-toggle::after{content:"";position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:2px;background:linear-gradient(180deg,rgba(100,160,240,.3) 0%,rgba(80,140,220,.2) 100%);transition:all .18s;box-shadow:0 1px 4px rgba(0,0,0,.4)}', '.ml-toggle.on{background:linear-gradient(180deg,rgba(30,70,140,.6) 0%,rgba(25,60,120,.7) 100%);border-color:rgba(60,150,255,.55);box-shadow:inset 0 1px 3px rgba(0,0,0,.3),0 0 8px rgba(60,140,255,.12)}', '.ml-toggle.on::after{left:18px;background:linear-gradient(180deg,rgba(120,190,255,.95) 0%,rgba(80,160,255,.85) 100%);box-shadow:0 0 10px rgba(60,150,255,.5),0 0 20px rgba(60,140,255,.2)}', '.ml-slider{-webkit-appearance:none;appearance:none;width:84px;height:4px;border-radius:2px;background:linear-gradient(90deg,rgba(30,50,80,.6) 0%,rgba(40,70,120,.4) 100%);outline:none;cursor:pointer;border:1px solid rgba(60,140,255,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}', '.ml-slider::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:2px;background:linear-gradient(180deg,rgba(140,200,255,.9) 0%,rgba(100,170,255,.75) 100%);border:1px solid rgba(60,150,255,.6);box-shadow:0 0 8px rgba(60,150,255,.35),0 0 16px rgba(60,140,255,.12),0 1px 3px rgba(0,0,0,.4);cursor:pointer}', '.ml-slider::-moz-range-thumb{width:14px;height:14px;border-radius:2px;background:linear-gradient(180deg,rgba(140,200,255,.9) 0%,rgba(100,170,255,.75) 100%);border:1px solid rgba(60,150,255,.6);box-shadow:0 0 8px rgba(60,150,255,.35),0 0 16px rgba(60,140,255,.12),0 1px 3px rgba(0,0,0,.4);cursor:pointer}', '#ml-keybinds{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:320px;max-height:70vh;display:none;z-index:100003;pointer-events:auto;overflow:hidden;border-radius:4px;font-family:"Courier New",Courier,monospace;user-select:none}', '#ml-keybinds.open{display:flex;flex-direction:column}', '#ml-keybinds-bg{position:absolute;inset:0;border-radius:4px;background:linear-gradient(175deg,#10141e 0%,#171d2c 15%,#1a2236 30%,#1e2840 50%,#1a2236 70%,#171d2c 85%,#10141e 100%);box-shadow:inset 0 2px 0 rgba(160,200,255,.18),inset 0 -2px 0 rgba(0,0,0,.5),0 0 0 1px rgba(70,130,220,.3),0 0 0 3px rgba(10,15,25,.8),0 0 0 4px rgba(70,130,220,.15),0 20px 60px rgba(0,0,0,.75)}', '#ml-keybinds-head{position:relative;padding:14px 18px 11px;border-bottom:2px solid rgba(60,140,255,.25);display:flex;align-items:center;justify-content:space-between;background:linear-gradient(180deg,rgba(50,110,200,.14) 0%,rgba(30,70,150,.05) 60%,transparent 100%)}', '#ml-keybinds-title{font-size:14px;font-weight:800;color:rgba(180,215,255,.95);letter-spacing:.2em;text-transform:uppercase;text-shadow:0 0 14px rgba(60,140,255,.4),0 1px 2px rgba(0,0,0,.6)}', '#ml-keybinds-close{cursor:pointer;background:none;border:none;color:rgba(160,200,255,.4);font-size:20px;padding:0 4px;font-family:monospace;line-height:1;transition:all .15s}', '#ml-keybinds-close:hover{color:#ff5555;text-shadow:0 0 12px rgba(255,60,60,.5)}', '#ml-keybinds-body{position:relative;overflow-y:auto;flex:1;padding:10px 14px 14px;scrollbar-width:thin;scrollbar-color:rgba(60,140,255,.25) transparent}', '.ml-krow{display:flex;align-items:center;justify-content:space-between;padding:7px 8px;border-radius:3px;cursor:pointer;transition:all .12s;border:1px solid transparent}', '.ml-krow:hover{background:linear-gradient(90deg,rgba(40,100,200,.12) 0%,rgba(50,110,200,.06) 100%);border-color:rgba(60,140,255,.08)}', '.ml-krow.listening{background:rgba(255,180,40,.1);border-color:rgba(255,180,40,.35)}', '.ml-krow-action{font-size:11.5px;color:rgba(200,220,250,.85);text-shadow:0 1px 2px rgba(0,0,0,.4)}', '.ml-krow-key{font-size:11px;color:rgba(100,175,255,.8);font-weight:700;padding:2px 8px;border-radius:3px;background:rgba(30,50,80,.5);border:1px solid rgba(60,140,255,.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,.2);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:4px;font-family:"Courier New",Courier,monospace;user-select:none}', '#ml-pets.open{display:flex;flex-direction:column}', '#ml-pets-bg{position:absolute;inset:0;border-radius:4px;background:linear-gradient(175deg,#10141e 0%,#171d2c 15%,#1a2236 30%,#1e2840 50%,#1a2236 70%,#171d2c 85%,#10141e 100%);box-shadow:inset 0 2px 0 rgba(160,200,255,.18),inset 0 -2px 0 rgba(0,0,0,.5),0 0 0 1px rgba(70,130,220,.3),0 0 0 3px rgba(10,15,25,.8),0 0 0 4px rgba(70,130,220,.15),0 20px 60px rgba(0,0,0,.75)}', '#ml-pets-bg::before{content:"";position:absolute;inset:0;border-radius:4px;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(100,170,255,.018) 2px,rgba(100,170,255,.018) 3px);pointer-events:none}', '#ml-pets-head{position:relative;padding:12px 16px 10px;border-bottom:2px solid rgba(60,140,255,.25);display:flex;align-items:center;justify-content:space-between;background:linear-gradient(180deg,rgba(50,110,200,.14) 0%,transparent 100%)}', '#ml-pets-title{font-size:14px;font-weight:800;color:rgba(180,215,255,.95);letter-spacing:.2em;text-transform:uppercase;text-shadow:0 0 14px rgba(60,140,255,.4),0 1px 2px rgba(0,0,0,.6)}', '#ml-pets-count{font-size:10px;color:rgba(100,170,255,.5);margin-left:8px;letter-spacing:.1em}', '#ml-pets-close{cursor:pointer;background:none;border:none;color:rgba(160,200,255,.4);font-size:20px;padding:0 4px;font-family:monospace;line-height:1;transition:all .15s}', '#ml-pets-close:hover{color:#ff5555;text-shadow:0 0 12px rgba(255,60,60,.5)}', '#ml-pets-filter{position:relative;padding:8px 14px;border-bottom:1px solid rgba(60,140,255,.12)}', '#ml-pets-search{width:100%;background:rgba(20,30,50,.6);border:1px solid rgba(60,140,255,.2);color:rgba(200,225,255,.9);font-size:11px;padding:5px 10px;border-radius:3px;font-family:inherit;outline:none;box-sizing:border-box}', '#ml-pets-search:focus{border-color:rgba(60,150,255,.45);box-shadow:0 0 8px rgba(60,140,255,.12)}', '#ml-pets-search::placeholder{color:rgba(100,160,255,.35)}', '#ml-pets-body{position:relative;overflow-y:auto;flex:1;padding:0;scrollbar-width:thin;scrollbar-color:rgba(60,140,255,.25) transparent}', '#ml-pets-body::-webkit-scrollbar{width:4px}', '#ml-pets-body::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(60,140,255,.3),rgba(80,160,255,.15));border-radius:2px}', '#ml-ptable{width:100%;border-collapse:collapse;font-size:11px}', '#ml-ptable th{position:sticky;top:0;background:linear-gradient(180deg,rgba(25,35,55,.98),rgba(20,30,48,.95));color:rgba(100,170,255,.7);font-size:9.5px;text-transform:uppercase;letter-spacing:.15em;padding:7px 8px;text-align:left;cursor:pointer;border-bottom:1px solid rgba(60,140,255,.2);white-space:nowrap;transition:color .12s;z-index:1}', '#ml-ptable th:hover{color:rgba(140,200,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(60,140,255,.06);color:rgba(200,220,250,.8);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:140px}', '#ml-ptable tr:hover td{background:rgba(40,90,180,.12)}', '#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(160,120,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(60,140,255,.2);border:1px solid rgba(60,140,255,.3);color:rgba(140,200,255,.8);font-size:11px;padding:2px 6px;border-radius:3px;cursor:pointer;transition:all .12s;line-height:1}', '.pet-tp:hover{background:rgba(60,140,255,.4);color:#fff;border-color:rgba(80,170,255,.6)}', '.pet-grab{background:rgba(60,200,60,.2);border:1px solid rgba(60,200,60,.3);color:rgba(140,255,140,.8);font-size:11px;padding:2px 6px;border-radius:3px;cursor:pointer;transition:all .12s;line-height:1;margin-left:3px}', '.pet-grab:hover{background:rgba(60,200,60,.4);color:#fff;border-color:rgba(80,230,80,.6)}', '#ml-pets-empty{color:rgba(140,190,255,.45);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 = 'MLFLY (SPC)SPR (Shft)SETHOME (Q)HOME (`)BACK (Z)SLOTS (0/10)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 = '
Players10s
No other players found
'; document.body.appendChild(plist); const settings = document.createElement('div'); settings.id = 'ml-settings'; settings.innerHTML = `
Settings
Movement
Acceleration
Speed Cap${SPEED_CAP}
Accel Rate${ACCEL}
Base Speed${SPEED_DEFAULT}
Cuddle Panel
Auto-Refresh
Refresh Interval10
Features
Fly Hack
Speed Hack
Waypoints
Cuddle Panel
Continuous Cuddle
Pet Browser
`; document.body.appendChild(settings); const kbPanel = document.createElement('div'); kbPanel.id = 'ml-keybinds'; 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) + '
'; } kbPanel.innerHTML = '
Keybindings
' + rows + '
'; } buildKeybindsHTML(); document.body.appendChild(kbPanel); const petsPanel = document.createElement('div'); petsPanel.id = 'ml-pets'; petsPanel.innerHTML = '
Pets
' + petRefreshInterval + 's
Words are AND\u2019d together. Use OR for alternatives, brackets to group.
'; document.body.appendChild(petsPanel); [plist, settings, d, kbPanel, 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(kbPanel, $('ml-keybinds-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-interval', 'ml-sv-interval', refreshInterval); const ae = $('ml-s-accel-en'); if (ae) ae.classList.toggle('on', accelEnabled); const ar = $('ml-s-autorefresh'); if (ar) ar.classList.toggle('on', autoRefresh); const featVals = [featFly, featSprint, featWaypoints, featCuddle, featCuddleFollow, featPets]; FEAT_IDS.forEach((id, i) => { const el = $(id); if (el) el.classList.toggle('on', featVals[i]); }); } syncSettingsUI(); function getPlayers() { const holder = window.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}
${p.dist}m
`; 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 = window.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 = window.pc?.sessionId; if (myId) map[myId] = 'Yours'; const nm = window.pc?.app?.root?.findByName('NetworkManager')?.script?.networkManager; if (nm?.sessionId && !map[nm.sessionId]) map[nm.sessionId] = 'Yours'; const holder = window.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 = window.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 = window.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 petSortCol = 'income'; let petSortDir = -1; 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' ? '' : ``; }); 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' ? `` : ''; html += ``; }); html += '
${labels[c]}
${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'}${grabBtn}
'; 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; } 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 = window.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 = window.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') || kbPanel.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') || kbPanel.classList.contains('open') || petsPanel.classList.contains('open') || d.classList.contains('open'); document.addEventListener('pointerlockchange', e => { if (anyPanelOpen() && !document.pointerLockElement) { e.stopImmediatePropagation(); } }, 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) { settings._resetPos?.(); resumeGame(); } } $('ml-cfg').addEventListener('click', () => toggleSettings()); $('ml-settings-close').addEventListener('click', () => toggleSettings(false)); const DEFAULTS = { ACCEL: 6, SPEED_CAP: 100, SPEED_DEFAULT: 7, refreshInterval: 10, petRefreshInterval: 1 }; $('ml-settings-reset').addEventListener('click', () => { ACCEL = DEFAULTS.ACCEL; SPEED_CAP = DEFAULTS.SPEED_CAP; SPEED_DEFAULT = DEFAULTS.SPEED_DEFAULT; refreshInterval = DEFAULTS.refreshInterval; petRefreshInterval = DEFAULTS.petRefreshInterval; accelEnabled = true; autoRefresh = true; featFly = true; featSprint = true; featWaypoints = true; featCuddle = true; featCuddleFollow = true; featPets = true; Object.assign(KEYBINDS, DEFAULT_KEYBINDS); syncSettingsUI(); 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-interval',valId: 'ml-sv-interval', apply: v => { refreshInterval = 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 = v; s.apply(v); saveSettings(); }); }); const arToggle = $('ml-s-autorefresh'); if (arToggle) { arToggle.addEventListener('click', () => { autoRefresh = !autoRefresh; arToggle.classList.toggle('on', autoRefresh); saveSettings(); const timerEl = $('ml-plist-timer'); if (!autoRefresh) { stopRefreshTimer(); if (timerEl) timerEl.textContent = 'off'; } else if (plist.classList.contains('open')) { startRefreshTimer(); } }); } 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); } }, ]; 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 toggleKeybinds(forceOpen) { const open = forceOpen !== undefined ? forceOpen : !kbPanel.classList.contains('open'); kbPanel.classList.toggle('open', open); if (open) { buildKeybindsHTML(); if (document.pointerLockElement) document.exitPointerLock(); } else { kbListeningRow = null; kbPanel._resetPos?.(); resumeGame(); } } kbPanel.addEventListener('click', e => { if (e.target.closest('#ml-keybinds-close')) { toggleKeybinds(false); return; } const row = e.target.closest('.ml-krow'); if (!row) return; kbPanel.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-s-keybinds').addEventListener('click', () => { settings.classList.remove('open'); toggleKeybinds(true); }); $('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 (kbPanel.classList.contains('open') && !kbPanel.contains(e.target)) { toggleKeybinds(false); } if (petsPanel.classList.contains('open') && !petsPanel.contains(e.target) && e.target.id !== 'ml-pets-btn') { togglePetsPanel(false); } }); } const petSpawnData = new Map(); const PET_SPAWN_CAP = 2000; function hookPetSpawn() { const app = window.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'); } 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 digitMatch = e.code.match(/^Digit(\d)$/); if (digitMatch) { if (!featWaypoints) return; const idx = parseInt(digitMatch[1]); const flashSlot = () => flash('ml-slots'); if (e.shiftKey) { e.preventDefault(); e.stopPropagation(); slots[idx] = player.getPosition().clone(); saveWaypoints(); flashSlot(); console.log(`[slot ${idx}] saved`, slots[idx]); } else if (slots[idx]) { e.preventDefault(); e.stopPropagation(); backPos = player.getPosition().clone(); teleport(player, slots[idx]); saveWaypoints(); flashSlot(); console.log(`[slot ${idx}] teleported to`, slots[idx]); } return; } if (e.code === KEYBINDS.fly) { e.stopPropagation(); if (!featFly) return; 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'), 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(); 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 (sprinting) { const kb = window.pc?.app?.keyboard; if (kb && !kb.isPressed(window.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); } 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 (Shft+0-9)`; 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')); 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); })();