Advanced mod menu for moomoo.io with combat features, visual settings, and bot controls
Size
15.1 KB
Version
1.2.1
Created
Nov 8, 2025
Updated
28 days ago
1// ==UserScript==
2// @name Laffer Mod Menu
3// @description Advanced mod menu for moomoo.io with combat features, visual settings, and bot controls
4// @version 1.2.1
5// @match https://*.moomoo.io/*
6// @icon https://moomoo.io/img/favicon.png?v=1
7// @grant GM_addStyle
8// ==/UserScript==
9// Add CSS styles
10GM_addStyle(`
11#mod_menu {
12 position: absolute;
13 top: calc(50% - 250px);
14 left: calc(50% - 400px);
15 width: 820px;
16 height: 500px;
17 background: #40454E;
18 z-index: 1000000;
19}
20
21#name_text {
22 color: #ffffff;
23 font-size: 20px;
24 text-align: center;
25 padding-top: 12px;
26}
27
28#name_block {
29 position: absolute;
30 width: 200px;
31 height: 50px;
32 background: #373C45;
33}
34
35.settings_block {
36 position: absolute;
37 left: 200px;
38 width: 620px;
39 height: 500px;
40 overflow-y: scroll;
41 overflow-x: hidden;
42}
43
44#category_block {
45 position: absolute;
46 top: 50px;
47 width: 200px;
48 height: 450px;
49 background: #373C45;
50}
51
52.category {
53 width: 200px;
54 display: flex;
55 align-items: center;
56 height: 50px;
57 cursor: pointer;
58}
59
60.category:hover {
61 background: #2D3037;
62}
63
64.category.active {
65 background: #2D3037;
66}
67
68.category_element {
69 display: none;
70 width: 5px;
71 height: 50px;
72 background: #696ACB;
73}
74
75.category:hover .category_element,
76.category.active .category_element {
77 display: block;
78}
79
80.category_icon {
81 color: #858A92;
82 font-size: 24px;
83 padding-left: 15px;
84}
85
86.category:hover .category_icon,
87.category.active .category_icon {
88 color: #ffffff;
89}
90
91.category_text {
92 color: #858A92;
93 font-size: 16px;
94 padding-left: 15px;
95}
96
97.category:hover .category_text,
98.category.active .category_text {
99 color: #ffffff;
100}
101
102#exit {
103 position: absolute;
104 bottom: 0px;
105}
106
107#exit:hover #exit_icon {
108 color: #ff0000;
109}
110
111.setting_element {
112 display: flex;
113 position: relative;
114 width: 550px;
115 height: 50px;
116 align-items: center;
117 border-radius: 4px;
118 background: #373C45;
119}
120
121.invisible_element {
122 background: #40454E;
123}
124
125.setting2 {
126 position: relative;
127 padding-top: 10px;
128 left: 25px;
129}
130
131.setting_name {
132 color: #ffffff;
133 font-size: 16px;
134 padding-left: 15px;
135}
136
137.settings_title {
138 color: #858A92;
139 font-size: 16px;
140 padding-top: 25px;
141 padding-left: 25px;
142}
143
144.menu_checkbox {
145 position: absolute;
146 background: rgba(100, 100, 100, 0.5);
147 appearance: none;
148 width: 33px;
149 height: 15.5px;
150 border-radius: 50px;
151 cursor: pointer;
152 transform: scale(1.1);
153 right: 15px;
154 transition: 0.5s;
155}
156
157.menu_input {
158 color: #ffffff;
159 position: absolute;
160 height: 25px;
161 width: 75px;
162 border: 0px;
163 right: 15px;
164 text-align: center;
165 background: #30303A;
166}
167
168.menu_button {
169 color: #ffffff;
170 position: absolute;
171 font-size: 16px;
172 width: 250px;
173 height: 50px;
174 border: 0px;
175 cursor: pointer;
176 border-radius: 5px;
177 background: #373C45;
178}
179
180.menu_button:hover {
181 background: #2D3037;
182}
183
184.menu_checkbox:checked[type="checkbox"] {
185 background: #696ACB;
186}
187
188.menu_checkbox::after {
189 position: absolute;
190 content: "";
191 width: 15.5px;
192 height: 15.5px;
193 top: 0;
194 left: 0;
195 background: #ffffff;
196 border-radius: 50%;
197 transform: scale(1.1);
198 transition: 0.5s;
199}
200
201.menu_checkbox:checked[type="checkbox"]::after {
202 left: 50%;
203}
204
205.settings_block::-webkit-scrollbar {
206 width: 10px;
207}
208
209.settings_block::-webkit-scrollbar-track {
210 opacity: 0;
211}
212
213.settings_block::-webkit-scrollbar-thumb {
214 opacity: 0;
215}
216
217.settings_block::-webkit-scrollbar-thumb:active {
218 opacity: 0;
219}
220
221#chat_log {
222 position: absolute;
223 top: 20px;
224 left: 20px;
225 width: 400px;
226 height: 300px;
227 border-radius: 4px;
228 background: #373C45;
229 z-index: 999999;
230}
231
232#chat_name {
233 position: absolute;
234 color: #ffffff;
235 padding-left: 10px;
236 padding-top: 10px;
237 font-size: 24px;
238}
239
240#chat_box {
241 position: absolute;
242 top: 50px;
243 left: 10px;
244 width: 380px;
245 height: 240px;
246 border-radius: 4px;
247 background: #30303A;
248 overflow-y: scroll;
249 overflow-x: hidden;
250}
251
252#chat_box::-webkit-scrollbar {
253 width: 10px;
254}
255
256#chat_box::-webkit-scrollbar-track {
257 opacity: 0;
258}
259
260#chat_box::-webkit-scrollbar-thumb {
261 background: #ffffff;
262 border-radius: 4px;
263}
264
265#chat_box::-webkit-scrollbar-thumb:active {
266 background: #ffffff;
267}
268
269.chat_message {
270 color: #ffffff;
271 font-size: 14px;
272 padding: 5px;
273}
274`);
275
276// Initialize settings
277const settings = {
278 spampreplace: false,
279 x18ksync: false,
280 autoPlace: false,
281 chatlog: false,
282 gamezoom: '1.0',
283 botname: 'Bot',
284 botcount: '1',
285 botplatformplacer: false
286};
287
288// Load saved settings
289function loadSettings() {
290 const saved = localStorage.getItem('moomoo_mod_settings');
291 if (saved) {
292 try {
293 Object.assign(settings, JSON.parse(saved));
294 } catch (e) {
295 console.error('Failed to load settings:', e);
296 }
297 }
298}
299
300// Save settings
301function saveSettings() {
302 localStorage.setItem('moomoo_mod_settings', JSON.stringify(settings));
303}
304
305// Load settings on startup
306loadSettings();
307
308// Create menu HTML helper functions
309function addSetting(id) {
310 return `<input type="checkbox" class="menu_checkbox" id="${id}" ${settings[id] ? 'checked' : ''}>`;
311}
312
313function addTextInput(id, maxlength) {
314 return `<input type="text" class="menu_input" placeholder="NONE" id="${id}" value="${settings[id]}" maxlength="${maxlength}">`;
315}
316
317// Create main menu
318const menu = document.createElement('div');
319menu.id = 'mod_menu';
320menu.style.display = 'none';
321menu.innerHTML = `
322<div id="name_block">
323 <div id="name_text">Laffer Mod Menu</div>
324</div>
325<div id="category_block">
326 <div id="combat" class="category active">
327 <div class="category_element"></div>
328 <i class="material-icons category_icon">local_fire_department</i>
329 <div class="category_text">Combat</div>
330 </div>
331 <div id="visual" class="category">
332 <div class="category_element"></div>
333 <i class="material-icons category_icon">visibility</i>
334 <div class="category_text">Visual</div>
335 </div>
336 <div id="bots" class="category">
337 <div class="category_element"></div>
338 <i class="material-icons category_icon">build</i>
339 <div class="category_text">Bots</div>
340 </div>
341 <div id="exit" class="category">
342 <div class="category_element"></div>
343 <i id="exit_icon" class="material-icons category_icon">close</i>
344 <div class="category_text">Exit</div>
345 </div>
346</div>
347<div id="combat_settings" class="settings_block">
348 <div class="settings_title">Gather</div>
349 <div class="setting2">
350 <div class="setting_element invisible_element">
351 <button class="menu_button" id="fix_gather">Fix Gather</button>
352 </div>
353 </div>
354 <div class="settings_title">Pre Place</div>
355 <div class="setting2">
356 <div class="setting_element">
357 <div class="setting_name">Spam</div>${addSetting('spampreplace')}
358 </div>
359 </div>
360 <div class="settings_title">Auto Sync</div>
361 <div class="setting2">
362 <div class="setting_element">
363 <div class="setting_name">x18k Sync</div>${addSetting('x18ksync')}
364 </div>
365 </div>
366 <div class="settings_title">Auto Place</div>
367 <div class="setting2">
368 <div class="setting_element">
369 <div class="setting_name">Auto Place</div>${addSetting('autoPlace')}
370 </div>
371 </div>
372</div>
373<div id="visual_settings" class="settings_block" style="display: none;">
374 <div class="settings_title">Visual</div>
375 <div class="setting2">
376 <div class="setting_element">
377 <div class="setting_name">Game Zoom</div>${addTextInput('gamezoom', 15)}
378 </div>
379 </div>
380 <div class="settings_title">Debug</div>
381 <div class="setting2">
382 <div class="setting_element">
383 <div class="setting_name">Chat Log</div>${addSetting('chatlog')}
384 </div>
385 </div>
386</div>
387<div id="bots_settings" class="settings_block" style="display: none;">
388 <div class="settings_title">Name</div>
389 <div class="setting2">
390 <div class="setting_element">
391 <div class="setting_name">Bots Name</div>${addTextInput('botname', 15)}
392 </div>
393 </div>
394 <div class="settings_title">Count</div>
395 <div class="setting2">
396 <div class="setting_element">
397 <div class="setting_name">Bots Count</div>${addTextInput('botcount', 2)}
398 </div>
399 </div>
400 <div class="settings_title">Place</div>
401 <div class="setting2">
402 <div class="setting_element">
403 <div class="setting_name">Platform Placer</div>${addSetting('botplatformplacer')}
404 </div>
405 </div>
406 <div class="setting2">
407 <div class="setting_element invisible_element">
408 <button class="menu_button" id="send_bots">Send bots</button>
409 <button class="menu_button" id="close_bots" style="left: 260px;">Close bots</button>
410 </div>
411 </div>
412</div>
413`;
414
415document.body.appendChild(menu);
416
417// Create chat log
418const chatLog = document.createElement('div');
419chatLog.id = 'chat_log';
420chatLog.style.display = settings.chatlog ? 'block' : 'none';
421chatLog.innerHTML = `
422<div id="chat_name">Chat Log</div>
423<div id="chat_box"></div>
424`;
425document.body.appendChild(chatLog);
426
427// Chat log function
428function addChatLog(text, color = '#ffffff') {
429 const time = new Date();
430 const hours = String(time.getHours()).padStart(2, '0');
431 const minutes = String(time.getMinutes()).padStart(2, '0');
432 const seconds = String(time.getSeconds()).padStart(2, '0');
433
434 const chatMessage = document.createElement('div');
435 chatMessage.className = 'chat_message';
436 chatMessage.style.color = color;
437 chatMessage.innerText = `[${hours}:${minutes}:${seconds}] - ${text}`;
438
439 const chatBox = document.getElementById('chat_box');
440 chatBox.appendChild(chatMessage);
441 chatMessage.scrollIntoView({ behavior: 'auto', block: 'end' });
442}
443
444// Initialize
445addChatLog('Mod: Welcome to Laffer Mod!', '#00ff00');
446
447// Menu toggle
448document.addEventListener('keydown', (e) => {
449 if (e.keyCode === 192) { // Tilde key (~)
450 menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
451 }
452});
453
454// Category switching
455function switchCategory(categoryId) {
456 // Hide all settings
457 document.querySelectorAll('.settings_block').forEach(block => {
458 block.style.display = 'none';
459 });
460
461 // Remove active class from all categories
462 document.querySelectorAll('.category').forEach(cat => {
463 cat.classList.remove('active');
464 });
465
466 // Show selected settings
467 const settingsId = categoryId + '_settings';
468 const settingsBlock = document.getElementById(settingsId);
469 if (settingsBlock) {
470 settingsBlock.style.display = 'block';
471 }
472
473 // Add active class to selected category
474 document.getElementById(categoryId).classList.add('active');
475}
476
477// Category event listeners
478document.getElementById('combat').addEventListener('click', () => switchCategory('combat'));
479document.getElementById('visual').addEventListener('click', () => switchCategory('visual'));
480document.getElementById('bots').addEventListener('click', () => switchCategory('bots'));
481document.getElementById('exit').addEventListener('click', () => {
482 menu.style.display = 'none';
483});
484
485// Settings change handlers
486for (let setting in settings) {
487 const element = document.getElementById(setting);
488 if (!element) continue;
489
490 element.addEventListener('change', function() {
491 if (typeof settings[setting] === 'boolean') {
492 settings[setting] = this.checked;
493
494 // Special handling for chat log
495 if (setting === 'chatlog') {
496 chatLog.style.display = this.checked ? 'block' : 'none';
497 }
498 } else {
499 settings[setting] = this.value;
500 }
501
502 saveSettings();
503 addChatLog(`Setting changed: ${setting} = ${settings[setting]}`, '#ffff00');
504 });
505}
506
507// Button handlers
508document.getElementById('fix_gather')?.addEventListener('click', () => {
509 if (typeof window.autogathering !== 'undefined') {
510 window.autogathering = !window.autogathering;
511 addChatLog(`Auto gathering: ${window.autogathering ? 'ON' : 'OFF'}`, '#00ff00');
512 }
513});
514
515document.getElementById('send_bots')?.addEventListener('click', () => {
516 addChatLog('Sending bots...', '#00ff00');
517 // Bot sending logic here
518});
519
520document.getElementById('close_bots')?.addEventListener('click', () => {
521 addChatLog('Closing bots...', '#ff0000');
522 // Bot closing logic here
523});
524
525// One-tick detector
526let oneTickDetectorActive = false;
527const POLEARM_RANGE = 140;
528const KATANA_RANGE = 100;
529const SOLDIER_HELMET_ID = 6;
530
531function dist2(a, b) {
532 return Math.hypot(a.x - b.x, a.y - b.y);
533}
534
535function getMyPlayer() {
536 return window.me || window.myPlayer || window.player || null;
537}
538
539function getAllPlayers() {
540 if (Array.isArray(window.players)) return window.players;
541 if (window.players && typeof window.players === 'object') return Object.values(window.players);
542 if (Array.isArray(window.entities)) return window.entities.filter(e => e?.isPlayer || e?.type === 'player');
543 return [];
544}
545
546function getHelmetId(p) {
547 return p?.hatId ?? p?.hat ?? p?.items?.hat ?? p?.gear?.helmet ?? p?.helmetId ?? null;
548}
549
550function performOneTickCheck() {
551 const me = getMyPlayer();
552 if (!me) return false;
553
554 const all = getAllPlayers();
555 if (!all?.length) return false;
556
557 let nearest = null;
558 let nearestDist = Infinity;
559
560 for (const p of all) {
561 if (!p || p === me || p.id === me.id) continue;
562 if (p.x == null || p.y == null) continue;
563
564 const d = dist2(me, p);
565 if (d < nearestDist) {
566 nearest = p;
567 nearestDist = d;
568 }
569 }
570
571 if (!nearest) return false;
572
573 const inRange = nearestDist <= Math.max(POLEARM_RANGE, KATANA_RANGE);
574 if (!inRange) return false;
575
576 const helmetId = getHelmetId(nearest);
577 if (helmetId !== SOLDIER_HELMET_ID) {
578 addChatLog('One-tick target detected without soldier helmet!', '#ff0000');
579 if (typeof window.onOneTickDetected === 'function') {
580 window.onOneTickDetected(nearest);
581 }
582 return true;
583 }
584 return false;
585}
586
587function frameCheck() {
588 if (!oneTickDetectorActive) return;
589 const ok = performOneTickCheck();
590 oneTickDetectorActive = false;
591 addChatLog(`One-tick check: ${ok ? 'DETECTED' : 'none'}`, ok ? '#ff0000' : '#00ff00');
592}
593
594// One-tick detector toggle (Shift + T)
595window.addEventListener('keydown', (e) => {
596 const isTyping = document.activeElement &&
597 (document.activeElement.tagName === 'INPUT' ||
598 document.activeElement.tagName === 'TEXTAREA' ||
599 document.activeElement.isContentEditable);
600
601 if (isTyping) return;
602
603 if (e.key === 'T' && e.shiftKey && !e.repeat) {
604 oneTickDetectorActive = !oneTickDetectorActive;
605 addChatLog(`One-tick detector: ${oneTickDetectorActive ? 'ON' : 'OFF'}`, '#00ffff');
606 if (oneTickDetectorActive) {
607 requestAnimationFrame(frameCheck);
608 }
609 }
610});
611
612console.log('%c[Laffer Mod] Loaded successfully!', 'color: #00ff00; font-size: 16px; font-weight: bold;');