moomoo.io visual script + clickgui (Insert to open).
// ==UserScript==
// @name Bullet
// @namespace Violentmonkey Scripts
// @icon
// @version 2.0
// @match *://moomoo.io/*
// @match *://sandbox.moomoo.io/*
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// @author wun1zzz
// @description moomoo.io visual script + clickgui (Insert to open).
// ==/UserScript==
(function () {
'use strict';
/* =========================================================
STATE / PERSISTENCE
========================================================= */
const DEFAULT_MODULES = {
restyle: true,
watermark: true,
stats: true,
timer: true,
keystrokes: true
};
const PRESETS = {
'Default': '#ffffff',
'Blood': '#ff3b3b',
'Ocean': '#3d9dff',
'Toxic': '#5cff6e',
'Royal': '#a066ff',
'Amber': '#ffb03d'
};
let modules = Object.assign({}, DEFAULT_MODULES, GM_getValue('bullet_modules_v2', {}));
let accent = GM_getValue('bullet_accent_v2', '#ffffff');
function persistModules() { GM_setValue('bullet_modules_v2', modules); }
function persistAccent(c) { GM_setValue('bullet_accent_v2', c); }
function applyAccent(c) {
accent = c;
document.documentElement.style.setProperty('--bullet-accent', c);
persistAccent(c);
}
/* =========================================================
GLOBAL GUI STYLE
========================================================= */
const guiStyle = document.createElement('style');
guiStyle.textContent = `
:root { --bullet-accent: ${accent}; }
#bullet-gui {
position: fixed;
top: 90px;
left: 90px;
width: 380px;
z-index: 999999;
background: rgba(14,14,17,0.94);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
font-family: 'Segoe UI', Arial, sans-serif;
color: #e9e9ec;
user-select: none;
display: none;
overflow: hidden;
}
#bullet-gui.open { display: block; }
#bullet-gui .bg-header {
padding: 12px 14px;
display: flex;
align-items: baseline;
gap: 8px;
cursor: move;
border-bottom: 1px solid rgba(255,255,255,0.07);
background: linear-gradient(180deg, rgba(255,255,255,0.04), transparent);
}
#bullet-gui .bg-title {
font-weight: 700;
font-size: 15px;
letter-spacing: 1px;
color: var(--bullet-accent);
}
#bullet-gui .bg-sub { font-size: 11px; color: #77777d; }
#bullet-gui .bg-close {
margin-left: auto;
cursor: pointer;
color: #77777d;
font-size: 13px;
padding: 2px 6px;
border-radius: 4px;
}
#bullet-gui .bg-close:hover { color: #fff; background: rgba(255,255,255,0.08); }
#bullet-gui .bg-body {
display: grid;
grid-template-columns: 1fr 140px;
}
#bullet-gui .bg-col-left {
padding: 10px 6px;
border-right: 1px solid rgba(255,255,255,0.06);
}
#bullet-gui .bg-col-right { padding: 10px; }
#bullet-gui .bg-cat-label {
font-size: 10px;
letter-spacing: 1.5px;
color: #6c6c72;
padding: 4px 8px 8px 8px;
}
#bullet-gui .bg-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 8px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
}
#bullet-gui .bg-row:hover { background: rgba(255,255,255,0.05); }
#bullet-gui .bg-toggle {
width: 32px;
height: 18px;
border-radius: 10px;
background: rgba(255,255,255,0.12);
position: relative;
transition: background 0.15s ease;
flex-shrink: 0;
}
#bullet-gui .bg-toggle::after {
content: '';
position: absolute;
top: 2px; left: 2px;
width: 14px; height: 14px;
border-radius: 50%;
background: #d4d4d8;
transition: transform 0.15s ease, background 0.15s ease;
}
#bullet-gui .bg-row.on .bg-toggle { background: rgba(255,255,255,0.2); }
#bullet-gui .bg-row.on .bg-toggle::after {
transform: translateX(14px);
background: var(--bullet-accent);
}
#bullet-gui .bg-theme-label {
font-size: 10px;
letter-spacing: 1.5px;
color: #6c6c72;
margin-bottom: 8px;
}
#bullet-gui input[type="color"] {
width: 100%;
height: 28px;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 6px;
background: transparent;
cursor: pointer;
padding: 0;
margin-bottom: 10px;
}
#bullet-gui .bg-presets {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 6px;
}
#bullet-gui .bg-preset {
width: 100%;
aspect-ratio: 1;
border-radius: 6px;
border: 1.5px solid rgba(255,255,255,0.15);
cursor: pointer;
}
#bullet-gui .bg-preset:hover { border-color: #fff; }
#bullet-gui .bg-footer {
padding: 8px 12px;
font-size: 10px;
color: #62626a;
border-top: 1px solid rgba(255,255,255,0.06);
text-align: center;
}
`;
document.head.appendChild(guiStyle);
/* =========================================================
BUILD GUI DOM
========================================================= */
const gui = document.createElement('div');
gui.id = 'bullet-gui';
gui.innerHTML = `
<div class="bg-header">
<span class="bg-title">BULLET</span>
<span class="bg-sub">v2.0</span>
<span class="bg-close">✕</span>
</div>
<div class="bg-body">
<div class="bg-col-left">
<div class="bg-cat-label">HUD</div>
<div class="bg-row" data-mod="restyle"><span>UI Restyle</span><div class="bg-toggle"></div></div>
<div class="bg-row" data-mod="watermark"><span>Watermark</span><div class="bg-toggle"></div></div>
<div class="bg-row" data-mod="stats"><span>FPS & Ping</span><div class="bg-toggle"></div></div>
<div class="bg-row" data-mod="timer"><span>Session Timer</span><div class="bg-toggle"></div></div>
<div class="bg-row" data-mod="keystrokes"><span>Keystrokes</span><div class="bg-toggle"></div></div>
</div>
<div class="bg-col-right">
<div class="bg-theme-label">ACCENT</div>
<input type="color" id="bg-color-picker" value="${accent}">
<div class="bg-theme-label">PRESETS</div>
<div class="bg-presets" id="bg-presets"></div>
</div>
</div>
<div class="bg-footer">Insert — toggle menu</div>
`;
document.body.appendChild(gui);
const presetsEl = gui.querySelector('#bg-presets');
Object.entries(PRESETS).forEach(([name, color]) => {
const sw = document.createElement('div');
sw.className = 'bg-preset';
sw.title = name;
sw.style.background = color;
sw.addEventListener('click', () => {
applyAccent(color);
gui.querySelector('#bg-color-picker').value = color;
});
presetsEl.appendChild(sw);
});
gui.querySelector('#bg-color-picker').addEventListener('input', (e) => applyAccent(e.target.value));
gui.querySelector('.bg-close').addEventListener('click', () => closeGui());
(function makeDraggable() {
const header = gui.querySelector('.bg-header');
let dragging = false, sx, sy, ox, oy;
header.addEventListener('mousedown', (e) => {
if (e.target.classList.contains('bg-close')) return;
dragging = true;
sx = e.clientX; sy = e.clientY;
const r = gui.getBoundingClientRect();
ox = r.left; oy = r.top;
});
window.addEventListener('mousemove', (e) => {
if (!dragging) return;
gui.style.left = (ox + e.clientX - sx) + 'px';
gui.style.top = (oy + e.clientY - sy) + 'px';
});
window.addEventListener('mouseup', () => dragging = false);
})();
function openGui() { gui.classList.add('open'); }
function closeGui() { gui.classList.remove('open'); }
function toggleGui() { gui.classList.toggle('open'); }
function isTyping() {
const id = (document.activeElement && document.activeElement.id || '').toLowerCase();
return id === 'chatbox' || id === 'allianceinput';
}
document.addEventListener('keydown', (e) => {
if (e.keyCode === 45 && !isTyping()) toggleGui(); // Insert
});
/* =========================================================
MODULES
========================================================= */
const mounted = {};
function mountRestyle() {
const style = document.createElement('style');
style.id = 'bullet-restyle-style';
style.textContent = `
#leaderboard, #chatHolder, #storeHolder,
#allianceHolder, #itemInfoHolder, #mainMenu, .menuCard,
#chatButton, #storeButton, #allianceButton, .uiElement,
.actionBarItem, .gameButton, #ageBar {
background-color: rgba(10, 10, 10, 0.9) !important;
border-radius: 12px !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5) !important;
backdrop-filter: none !important;
}
#mapDisplay {
border-radius: 12px !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
background:
linear-gradient(to bottom, transparent 49%, #3d81b8 49%, #3d81b8 51%, transparent 51%),
linear-gradient(to bottom,
#fff 0%, #fff 17%,
#b5d86c 17%, #b5d86c 83%,
#e1c66d 83%, #e1c66d 100%
) !important;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5) !important;
}
#enterGame {
background-color: #fff !important;
color: #000 !important;
border-radius: 10px !important;
font-weight: bold !important;
border: none !important;
cursor: pointer;
}
#nameInput, #serverBrowser, .storeInput {
background-color: #151515 !important;
border-radius: 8px !important;
border: 1px solid #333 !important;
color: #fff !important;
padding: 8px !important;
}
#ageBarBody {
background-color: var(--bullet-accent) !important;
border-radius: 10px !important;
transition: width 0.25s ease-out !important;
}
#gameName {
color: #fff !important;
text-shadow: 2px 2px 10px rgba(0,0,0,0.5) !important;
}
#ot-sdk-btn-container, #promoImgHolder, .adCard, #moomooio_728x90_home {
display: none !important;
}
`;
document.head.appendChild(style);
mounted.restyle = style;
const updateLogo = () => {
const logo = document.getElementById('gameName');
if (logo) logo.innerText = 'BULLET';
};
const checker = setInterval(() => {
if (document.getElementById('mainMenu')) {
updateLogo();
clearInterval(checker);
}
}, 100);
mounted.restyleChecker = checker;
}
function unmountRestyle() {
if (mounted.restyle) mounted.restyle.remove();
if (mounted.restyleChecker) clearInterval(mounted.restyleChecker);
mounted.restyle = null;
}
function mountWatermark() {
const wm = document.createElement('div');
wm.id = 'bullet-watermark';
wm.innerHTML = 'Bullet <span style="font-size:14px;opacity:.8;">by wun1zz 2.0</span>';
Object.assign(wm.style, {
position: 'fixed', top: '15px', left: '15px', zIndex: '10000',
fontFamily: '"Hammersmith One", sans-serif', fontSize: '26px',
color: 'var(--bullet-accent)', pointerEvents: 'none',
textShadow: '-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000'
});
document.body.appendChild(wm);
mounted.watermark = wm;
}
function unmountWatermark() {
if (mounted.watermark) mounted.watermark.remove();
mounted.watermark = null;
}
function mountStats() {
const box = document.createElement('div');
box.id = 'bullet-stats';
Object.assign(box.style, {
position: 'fixed', top: '48px', left: '15px', zIndex: '10000',
fontFamily: '"Hammersmith One", sans-serif', fontSize: '15px',
color: 'var(--bullet-accent)', pointerEvents: 'none',
textShadow: '-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000'
});
box.innerHTML = 'FPS: 0 | Ping: 0 ms';
document.body.appendChild(box);
let lastTime = performance.now(), frames = 0, fps = 0, raf;
function loop() {
frames++;
const now = performance.now();
if (now - lastTime >= 1000) { fps = frames; frames = 0; lastTime = now; }
const pingEl = document.getElementById('pingDisplay');
const ping = pingEl ? pingEl.innerText.replace(/[^\d]/g, '') : '?';
box.innerHTML = `FPS: ${fps} | Ping: ${ping} ms`;
raf = requestAnimationFrame(loop);
}
loop();
mounted.stats = box;
mounted.statsStop = () => cancelAnimationFrame(raf);
}
function unmountStats() {
if (mounted.statsStop) mounted.statsStop();
if (mounted.stats) mounted.stats.remove();
mounted.stats = null;
}
function mountTimer() {
const el = document.createElement('div');
el.id = 'bullet-session-timer';
Object.assign(el.style, {
position: 'fixed', top: '70px', left: '15px', color: 'var(--bullet-accent)',
fontSize: '13px', fontFamily: 'sans-serif', background: 'rgba(0,0,0,0.4)',
padding: '2px 8px', borderRadius: '6px', zIndex: '9999'
});
el.innerHTML = 'Session: 00:00';
document.body.appendChild(el);
const start = performance.now();
const pad = n => String(n).padStart(2, '0');
let raf;
function update() {
const t = Math.floor((performance.now() - start) / 1000);
const h = Math.floor(t / 3600), m = Math.floor((t % 3600) / 60), s = t % 60;
el.innerHTML = h > 0 ? `Session: ${pad(h)}:${pad(m)}:${pad(s)}` : `Session: ${pad(m)}:${pad(s)}`;
raf = requestAnimationFrame(update);
}
update();
mounted.timer = el;
mounted.timerStop = () => cancelAnimationFrame(raf);
}
function unmountTimer() {
if (mounted.timerStop) mounted.timerStop();
if (mounted.timer) mounted.timer.remove();
mounted.timer = null;
}
function mountKeystrokes() {
const style = document.createElement('style');
style.id = 'bullet-ks-style';
style.textContent = `
#ks-container {
position: fixed; z-index: 999998; cursor: move; user-select: none;
display: grid; grid-template-columns: repeat(6, 22px); grid-gap: 4px;
padding: 6px; background: rgba(0,0,0,0.4); border-radius: 8px;
font-family: 'Arial', sans-serif; border: 1px solid rgba(255,255,255,0.05);
}
.ks-key {
background: rgba(0,0,0,0.7); color: #fff; border-radius: 4px;
display: flex; flex-direction: column; align-items: center; justify-content: center;
height: 42px; border: 1px solid rgba(255,255,255,0.1); pointer-events: none;
}
#ks-KeyW { grid-column: 3 / span 2; }
#ks-KeyA { grid-column: 1 / span 2; }
#ks-KeyS { grid-column: 3 / span 2; }
#ks-KeyD { grid-column: 5 / span 2; }
#ks-Mouse0 { grid-column: 1 / span 3; }
#ks-Mouse2 { grid-column: 4 / span 3; }
#ks-Space { grid-column: 1 / span 6; height: 26px; }
.ks-key.active { background: var(--bullet-accent) !important; color: #000 !important; transform: scale(0.9); }
.ks-letter { font-size: 12px; font-weight: bold; pointer-events: none; }
.ks-count { font-size: 9px; opacity: .8; pointer-events: none; margin-top: -1px; }
`;
document.head.appendChild(style);
mounted.ksStyle = style;
const container = document.createElement('div');
container.id = 'ks-container';
container.style.top = GM_getValue('ks_y', '120px');
container.style.left = GM_getValue('ks_x', '20px');
const savedCounts = GM_getValue('ks_counts_data', {});
const keys = [
{id:'KeyW',txt:'W'},{id:'KeyA',txt:'A'},{id:'KeyS',txt:'S'},{id:'KeyD',txt:'D'},
{id:'Mouse0',txt:'LMB'},{id:'Mouse2',txt:'RMB'},{id:'Space',txt:'SPACE'}
];
container.innerHTML = keys.map(k => {
const count = savedCounts[k.id] || 0;
return `<div id="ks-${k.id}" class="ks-key"><span class="ks-letter">${k.txt}</span><span class="ks-count">${count}</span></div>`;
}).join('');
document.body.appendChild(container);
mounted.ks = container;
const currentCounts = { ...savedCounts };
function updateState(code, isDown) {
const el = document.getElementById(`ks-${code}`);
if (!el) return;
if (isDown) {
if (!el.classList.contains('active')) {
currentCounts[code] = (currentCounts[code] || 0) + 1;
el.querySelector('.ks-count').textContent = currentCounts[code];
el.classList.add('active');
GM_setValue('ks_counts_data', currentCounts);
}
} else {
el.classList.remove('active');
}
}
const onKeydown = e => updateState(e.code, true);
const onKeyup = e => updateState(e.code, false);
const onMousedown = e => { if (e.button === 0 || e.button === 2) updateState(`Mouse${e.button}`, true); };
const onMouseup = e => { if (e.button === 0 || e.button === 2) updateState(`Mouse${e.button}`, false); };
const onContext = e => { if (e.target.closest('#ks-container')) e.preventDefault(); };
const onReset = e => { if (e.shiftKey && e.altKey && e.code === 'KeyR') { GM_setValue('ks_counts_data', {}); location.reload(); } };
window.addEventListener('keydown', onKeydown);
window.addEventListener('keyup', onKeyup);
window.addEventListener('mousedown', onMousedown);
window.addEventListener('mouseup', onMouseup);
window.addEventListener('contextmenu', onContext);
window.addEventListener('keydown', onReset);
let isDragging = false, sx, sy, ox, oy;
container.onmousedown = (e) => {
isDragging = true; sx = e.clientX; sy = e.clientY;
ox = container.offsetLeft; oy = container.offsetTop;
};
const onMove = (e) => {
if (!isDragging) return;
container.style.left = (ox + e.clientX - sx) + 'px';
container.style.top = (oy + e.clientY - sy) + 'px';
};
const onUp = () => {
if (isDragging) {
isDragging = false;
GM_setValue('ks_x', container.style.left);
GM_setValue('ks_y', container.style.top);
}
};
window.addEventListener('mousemove', onMove);
window.addEventListener('mouseup', onUp);
mounted.ksCleanup = () => {
window.removeEventListener('keydown', onKeydown);
window.removeEventListener('keyup', onKeyup);
window.removeEventListener('mousedown', onMousedown);
window.removeEventListener('mouseup', onMouseup);
window.removeEventListener('contextmenu', onContext);
window.removeEventListener('keydown', onReset);
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onUp);
};
}
function unmountKeystrokes() {
if (mounted.ksCleanup) mounted.ksCleanup();
if (mounted.ks) mounted.ks.remove();
if (mounted.ksStyle) mounted.ksStyle.remove();
mounted.ks = null;
}
const MODULE_FNS = {
restyle: { mount: mountRestyle, unmount: unmountRestyle },
watermark: { mount: mountWatermark, unmount: unmountWatermark },
stats: { mount: mountStats, unmount: unmountStats },
timer: { mount: mountTimer, unmount: unmountTimer },
keystrokes: { mount: mountKeystrokes, unmount: unmountKeystrokes }
};
function setModule(id, on) {
modules[id] = on;
persistModules();
const row = gui.querySelector(`.bg-row[data-mod="${id}"]`);
if (row) row.classList.toggle('on', on);
if (on) MODULE_FNS[id].mount(); else MODULE_FNS[id].unmount();
}
gui.querySelectorAll('.bg-row').forEach(row => {
const id = row.dataset.mod;
row.classList.toggle('on', !!modules[id]);
row.addEventListener('click', () => setModule(id, !modules[id]));
if (modules[id]) MODULE_FNS[id].mount();
});
/* =========================================================
CORE (always-on) TWEAKS
========================================================= */
const originalGetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.getContext = function (type, attribs) {
const ctx = originalGetContext.call(this, type, attribs);
if (ctx && type === '2d') ctx.imageSmoothingEnabled = false;
return ctx;
};
function patchMinimap() {
const mapCanvas = document.getElementById('mapDisplay');
if (!mapCanvas) return;
const ctx = mapCanvas.getContext('2d');
const desc = Object.getOwnPropertyDescriptor(CanvasRenderingContext2D.prototype, 'fillStyle');
Object.defineProperty(ctx, 'fillStyle', {
get() { return desc.get.call(this); },
set(v) {
if (v === '#fff' || v === '#ffffff') { desc.set.call(this, '#ff3b3b'); return; }
if (typeof v === 'string' && v.startsWith('rgba(255,255,255')) { desc.set.call(this, 'rgba(80,230,110,0.9)'); return; }
desc.set.call(this, v);
}
});
}
const mapChecker = setInterval(() => {
if (document.getElementById('mapDisplay')) { patchMinimap(); clearInterval(mapChecker); }
}, 200);
})();