// ==UserScript== // @name DiepBox by Cazka // @description made with much love // @version 0.1.11 // @author Cazka#1820 // @match *://diep.io/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_addValueChangeListener // @grant GM_removeValueChangeListener // @namespace https://greasyfork.org/users/541070 // @downloadURL none // ==/UserScript== 'use strict'; /* * Feel free to use some of my code for your own multiboxing script. make sure to give credits! */ /* * C L A S S E S */ class Gui { constructor(title) { this._colors = ['#E8B18A', '#E666EA', '#9566EA', '#6690EA', '#E7D063', '#EA6666', '#92EA66', '#66EAE6']; this._buttons = []; this._notifications = []; this._title = title; this._gui; this._guiHead; this._guiBody; this._init(); this._enableShortcuts(); } _init() { const nonce = `a${Math.floor(Math.random() * 1e5)}`;; GM_addStyle(`.${nonce} button{display:block;font-family:Ubuntu;color:#fff;text-shadow:-.1em -.1em 0 #000,0 -.1em 0 #000,.1em -.1em 0 #000,.1em 0 0 #000,.1em .1em 0 #000,0 .1em 0 #000,-.1em .1em 0 #000,-.1em 0 0 #000;opacity:.8;border:0;padding:.3em .5em;width:100%;transition:all .15s}.${nonce}{top:0;left:0;position:absolute}.${nonce} button:active:not([disabled]){filter:brightness(.9)}.${nonce} button:hover:not([disabled]):not(:active){filter:brightness(1.1)}`); this._gui = document.createElement('div'); this._guiHead = document.createElement('div'); this._guiBody = document.createElement('div'); this._gui.className = `${nonce}`; this._guiBody.style.display = 'block'; document.body.appendChild(this._gui); this._gui.appendChild(this._guiHead); this._gui.appendChild(this._guiBody); this._addButton(this._guiHead, this._title, () => { if (this._guiBody.style.display === 'block') { this._guiBody.style.display = 'none'; } else { this._guiBody.style.display = 'block'; } }); } addButton(text, onclick, keyCode) { return this._addButton(this._guiBody, text, onclick, keyCode); } removeButton(button) { const index = this._buttons.findIndex((x) => x === button); if (index == -1) return; button.remove(); button.active = false; this._buttons.splice(index, 1); } reset() { for (let i = 1, n = this._buttons.length; i < n; i++) { this.removeButton(this._buttons[1]); } } _addButton(parent, text, onclick, keyCode) { const button = document.createElement('button'); button.innerHTML = text; button.keyCode = keyCode; button.onclick = onclick; button.style['background-color'] = this._colors[this._buttons.length % this._colors.length]; parent.appendChild(button); this._buttons.push(button); return button; } _enableShortcuts() { document.addEventListener('keydown', (event) => { if (document.getElementById('textInputContainer').style.display === 'block') return; this._buttons.forEach((button) => { if (button.keyCode === event.code) button.onclick(); }); }); } } class Minimap { constructor() { this._minimapWidth; this._minimapHeight; this._x00; this._y00; this._pointX; this._pointY; this._pointX_previous; this._pointY_previous; this._viewportWidth; this._viewportHeight; this._fov; this._minimapHook(); this._arrowHook(); this._viewportHook(); this._fovHook(); } get x() { return this._pointX ? (this._pointX - this._x00) / this._minimapWidth : 0; } get y() { return this._pointY ? (this._pointY - this._y00) / this._minimapHeight : 0; } get x_previous() { return this._pointX_previous ? (this._pointX_previous - this._x00) / this._minimapWidth : 0; } get y_previous() { return this._pointY_previous ? (this._pointY_previous - this._y00) / this._minimapHeight : 0; } get scale() { return { x: this._viewportWidth / this._minimapWidth, y: this._viewportHeight / this._minimapHeight, }; } get fov() { return this._fov; } _minimapHook() { let setTransformArgs; const onsetTransform = (args) => { if (args[0] === args[3]) setTransformArgs = args; }; const onstrokeRect = () => { if (setTransformArgs) { this._minimapWidth = setTransformArgs[0]; this._minimapHeight = setTransformArgs[3]; this._x00 = setTransformArgs[4]; this._y00 = setTransformArgs[5]; setTransformArgs = undefined; } }; this._ctxHook('setTransform', onsetTransform); this._ctxHook('strokeRect', onstrokeRect); } _arrowHook() { let index = 0; const stack = Array(4); let pointA; let pointB; let pointC; const calculatePos = () => { const side1 = Math.floor(Math.sqrt(Math.pow(pointA[0] - pointB[0], 2) + Math.pow(pointA[1] - pointB[1], 2))); const side2 = Math.floor(Math.sqrt(Math.pow(pointA[0] - pointC[0], 2) + Math.pow(pointA[1] - pointC[1], 2))); const side3 = Math.floor(Math.sqrt(Math.pow(pointB[0] - pointC[0], 2) + Math.pow(pointB[1] - pointC[1], 2))); if (side1 == side2 && side2 == side3) return; this._pointX_previous = this._pointX; this._pointY_previous = this._pointY; this._pointX = (pointA[0] + pointB[0] + pointC[0]) / 3; this._pointY = (pointA[1] + pointB[1] + pointC[1]) / 3; }; const onbeginPath = () => { index = 0; stack[index++] = 0; }; const onmoveTo = (args) => { if (index === 1 && stack[index - 1] === 0) { stack[index++] = 1; pointA = args; return; } index = 0; }; const onlineTo = (args) => { if (index === 2 && stack[index - 1] === 1) { stack[index++] = 2; pointB = args; return; } if (index === 3 && stack[index - 1] === 2) { stack[index++] = 2; pointC = args; return; } index = 0; }; const onfill = () => { if (index === 4 && stack[index - 1] === 2) { calculatePos(); return; } index = 0; }; this._ctxHook('beginPath', onbeginPath); this._ctxHook('moveTo', onmoveTo); this._ctxHook('lineTo', onlineTo); this._ctxHook('fill', onfill); } _viewportHook() { let setTransformArgs; const onsetTransform = (args) => { if ((args[0] / args[3]).toFixed(4) !== (unsafeWindow.innerWidth / unsafeWindow.innerHeight).toFixed(4)) return; if (args[0] >= unsafeWindow.innerWidth && args[3] >= unsafeWindow.innerHeight) return; setTransformArgs = args; }; const onfillRect = () => { if (setTransformArgs) { unsafeWindow.input.execute('ren_minimap_viewport false'); this._viewportWidth = setTransformArgs[0]; this._viewportHeight = setTransformArgs[3]; setTransformArgs = undefined; } }; this._ctxHook('setTransform', onsetTransform); this._ctxHook('fillRect', onfillRect); setTimeout(() => unsafeWindow.input.execute('ren_minimap_viewport true'), 1000); setInterval(() => { unsafeWindow.input.execute('ren_minimap_viewport true'); }, 10000); } _fovHook() { const calculateFov = (fov) => { this._fov = fov * 10; } function onstroke() { if (this.fillStyle === '#cdcdcd') { calculateFov(this.globalAlpha); } } this._ctxHook('stroke', onstroke) } _ctxHook(method, hook) { const target = window.CanvasRenderingContext2D.prototype; target[method] = new Proxy(target[method], { apply(target, thisArg, args) { args = hook.call(thisArg, args) || args; return target.apply(thisArg, args); }, }); } } class DiepGamepad { constructor() { this._axes = [0, 0, 0, 0]; this._buttons = [...Array(17)].map((x) => { return { pressed: false }; }); } set x(value) { this._axes[0] = value; } set y(value) { this._axes[1] = value; } set mx(value) { this._axes[2] = value; } set my(value) { this._axes[3] = value; } set leftMouse(value) { this._buttons[7].pressed = value; } set rightMouse(value) { this._buttons[6].pressed = value; } set connected(value) { unsafeWindow.navigator.getGamepads = () => [value ? this.toGamepad() : undefined]; } get x() { return this._axes[0]; } get y() { return this._axes[1]; } get mx() { return this._axes[2]; } get my() { return this._axes[3]; } get leftMouse() { return this._buttons[7].pressed; } get rightMouse() { return this._buttons[6].pressed; } get connected() { return unsafeWindow.navigator.getGamepads()[0] ? true : false; } toGamepad() { return { axes: this._axes, buttons: this._buttons, mapping: 'standard', }; } } class Vector { static length({ x, y }) { return Math.sqrt(x ** 2 + y ** 2); } static add(u, v) { return { x: u.x + v.x, y: u.y + v.y, } } static subtract(u, v) { return { x: u.x - v.x, y: u.y - v.y } } static scale(r, v) { return { x: r * v.x, y: r * v.y, } } static dot(u, v) { return u.x * v.x + u.y * v.y; } static distance(u, v) { return Vector.length(Vector.subtract(u, v)); } static distanceLine(a, n, p) { const r = Vector.dot(n, Vector.subtract(a, p)) / -Vector.dot(n, n); const point = Vector.add(a, Vector.scale(r, n)); const distance = Vector.distance(point, p); return { r, point, distance, } } } class Arena { static get BLOCKSIZE() { return 50; } static scale(x, y) { return { x: Math.floor(22300 * (x - 0.5) + 0.5), y: Math.floor(22300 * (y - 0.5) + 0.5), } } static unscale(x, y) { return { x: (x / 22300) + 0.5, y: (y / 22300) + 0.5 } } } class Player { constructor() { this._minimap = new Minimap(); this._gamepad = new DiepGamepad(); this._mouse = { x: 0, y: 0, }; this._inputs = { left: false, down: false, up: false, right: false, } unsafeWindow.addEventListener('mousemove', (e) => this._onmousemove(e)); unsafeWindow.addEventListener('mousedown', (e) => this._onmousedown(e)); unsafeWindow.addEventListener('mouseup', (e) => this._onmouseup(e)); unsafeWindow.addEventListener('keydown', (e) => this._onkeydown(e)); unsafeWindow.addEventListener('keyup', (e) => this._onkeyup(e)); } set useGamepad(value) { this._gamepad.connected = value; } get position() { const position = Arena.scale(this._minimap.x, this._minimap.y); const previous = Arena.scale(this._minimap.x_previous, this._minimap.y_previous); return { x: position.x, y: position.y, x_previous: previous.x, y_previous: previous.y, } } get mouse() { return this.toArenaPos(this._mouse.x, this._mouse.y); } get inputs() { return this._inputs; } toScreenPos(x, y) { const unscale = Arena.unscale(x, y); x = unscale.x; y = unscale.y; let position = this.position; position = Arena.unscale(position.x, position.y); const scale = this._minimap.scale; x -= position.x; x /= scale.x; x += 0.5; x *= unsafeWindow.innerWidth; y -= position.y; y /= scale.y; y += 0.5; y *= unsafeWindow.innerHeight; return { x, y }; } toArenaPos(x, y) { let position = this.position; position = Arena.unscale(position.x, position.y); const scale = this._minimap.scale; x /= unsafeWindow.innerWidth; x -= 0.5; x *= scale.x; x += position.x; y /= unsafeWindow.innerHeight; y -= 0.5; y *= scale.y; y += position.y; return Arena.scale(x, y); } moveTo(x, y) { const position = this.position; const deltaX = x - position.x; const deltaY = y - position.y; const length = Vector.length({ x: deltaX, y: deltaY }); if (length === 0) { this._gamepad.x = 0; this._gamepad.y = 0; return; } //max speed x = deltaX / length; y = deltaY / length; this._gamepad.x = x; this._gamepad.y = y; } lookAt(x, y) { const position = this.position; const a = window.innerHeight / 1080; const b = window.innerWidth / 1920; const c = (b < a) ? a : b; let x_axes = ((((x - position.x) / c) * this._minimap.fov) / 1200) / 1.1; let y_axes = ((((y - position.y) / c) * this._minimap.fov) / 1200) / 1.1; const length = Vector.length({ x: x_axes, y: y_axes }); if (length != 0 && length < 0.1) { x_axes *= 0.11 / length; y_axes *= 0.11 / length; } this._gamepad.mx = x_axes; this._gamepad.my = y_axes; } findBestPos(targetPosition, inputs) { const TOLERANCE = 200; //Strategies: //(1) Dont move to target if to close [removed] //(2) copy movement vector if to close [removed] //(3) predict future position //(4) make way when target moves to my direction //(5) dont do (2). create new vector through player inputs instead. this makes better predictions. const me = this.position; const target = { x: targetPosition.x, y: targetPosition.y, x_previous: targetPosition.x_previous, y_previous: targetPosition.y_previous, } const distance = Vector.distance(me, target); const targetVector = Vector.subtract({ x: target.x, y: target.y }, { x: target.x_previous, y: target.y_previous }); // (4) if (distance < 4 * TOLERANCE && Vector.length(targetVector) > 2) { const { r, point, distance } = Vector.distanceLine(target, targetVector, me); if (distance < 75 && r > 0) { return Vector.add(me, Vector.subtract(me, point)); } } // (5) if (distance < TOLERANCE) { let x = inputs.left ? -1 : 0; x += inputs.right ? 1 : 0; let y = inputs.up ? -1 : 0; y += inputs.down ? 1 : 0; return Vector.add(me, { x, y }); } // (3) return Vector.add(target, Vector.scale(50, targetVector)); } _onmousemove(e) { this._mouse.x = e.clientX; this._mouse.y = e.clientY; } _onmousedown(e) { this.onkeyDown && this.onkeyDown(e.which); } _onmouseup(e) { this.onkeyUp && this.onkeyUp(e.which); } _onkeydown(e) { switch (e.keyCode) { case 37: case 65: this._inputs.left = true; break; case 40: case 83: this._inputs.down = true; break; case 38: case 87: this._inputs.up = true; break; case 39: case 68: this._inputs.right = true; break; } this.onkeyDown && this.onkeyDown(e.keyCode); } _onkeyup(e) { switch (e.keyCode) { case 37: case 65: this._inputs.left = false; break; case 40: case 83: this._inputs.down = false; break; case 38: case 87: this._inputs.up = false; break; case 39: case 68: this._inputs.right = false; break; } this.onkeyUp && this.onkeyUp(e.keyCode); } } class MultiboxStorage { /* * items in storage: * position: [x, y, x_previous, y_previous] * mouse: [x,y] * mutex: boolean * multibox: boolean * keyDown: Number * keyUp: Number * clumpMode: String * inputs: [left, down, up, right] */ constructor() { //if the user launches this script for the first time try { this.position; this.mouse; this.multibox; this.mutex; this.keyDown; this.keyUp; this.clumpMode; this.inputs; } catch (err) { console.log('DiepBox Error: MultiboxStorage needs to be initialized'); this.reset(); } } set position(position) { GM_setValue('position', [ position.x, position.y, position.x_previous, position.y_previous ]); } set mouse(mouse) { GM_setValue('mouse', [mouse.x, mouse.y]); } set mutex(mutex) { GM_setValue('mutex', mutex ? 1 : 0); } set multibox(multibox) { GM_setValue('multibox', multibox ? 1 : 0); } set keyDown(key) { GM_setValue('keyDown', key); } set keyUp(key) { GM_setValue('keyUp', key); } set clumpMode(mode) { let m = 0; switch (mode) { case 'player': m = 1; break; case 'mouse': m = 2; break; default: throw new Error('unsupported clump mode', mode); } if (m) GM_setValue('clumpMode', m); } set inputs(inputs) { GM_setValue('inputs', [ inputs.left, inputs.down, inputs.up, inputs.right, ]); } get position() { const position = GM_getValue('position'); return { x: position[0], y: position[1], x_previous: position[2], y_previous: position[3], } } get mouse() { const mouse = GM_getValue('mouse'); return { x: mouse[0], y: mouse[1], } } get mutex() { const mutex = GM_getValue('mutex'); return mutex === 1 ? true : false; } get multibox() { const multibox = GM_getValue('multibox'); return multibox === 1 ? true : false; } get keyDown() { return GM_getValue('keyDown'); } get keyUp() { return GM_getValue('keyUp'); } get clumpMode() { const m = GM_getValue('clumpMode'); let mode = ''; switch (m) { case 1: mode = 'player'; break; case 2: mode = 'mouse'; break; default: throw new Error('unsupported clump mode', m); } return mode; } get inputs() { const inputs = GM_getValue('inputs'); return { left: inputs[0], down: inputs[1], up: inputs[2], right: inputs[3], } } reset() { this.position = { x: 0, y: 0, x_previous: 0, y_previous: 0 }; this.mouse = { x: 0, y: 0 }; this.mutex = false; this.multibox = false; this.keyDown = -1; this.keyUp = -1; this.clumpMode = 'player'; this.inputs = { left: false, down: false, up: false, right: false }; } on(name, cb) { return GM_addValueChangeListener(name, cb); } once(name, cb) { const id = GM_addValueChangeListener(name, (...args) => { cb(...args); this.off(id); }); } off(id) { GM_removeValueChangeListener(id); } } /* * D E B U G G E R */ const DEBUG = false; const debugger_mouse = document.body.appendChild(document.createElement('div')); function DEBUG_MousePosition(x, y, info = '') { if (!DEBUG) return; debugger_mouse.style.pointerEvents = 'none'; debugger_mouse.style.position = 'absolute'; debugger_mouse.style.zIndex = '99999'; debugger_mouse.style.left = `${x - 5}px`; debugger_mouse.style.top = `${y - 2}px`; debugger_mouse.innerText = '👆 ' + info; } const debugger_pos = document.body.appendChild(document.createElement('div')); const debugger_pos_prediction = document.body.appendChild(document.createElement('div')); function DEBUG_PlayerPosition(x = -100, y = -100, x_prediction = -100, y_prediction = -100, info = '') { if (!DEBUG) return; debugger_pos.style.pointerEvents = 'none'; debugger_pos.style.position = 'absolute'; debugger_pos.style.zIndex = '99999'; debugger_pos.style.left = `${x - 11}px`; debugger_pos.style.top = `${y - 12}px`; debugger_pos.innerText = '🟢 ' + info; debugger_pos_prediction.style.pointerEvents = 'none'; debugger_pos_prediction.style.position = 'absolute'; debugger_pos_prediction.style.zIndex = '99999'; debugger_pos_prediction.style.left = `${x_prediction - 11}px`; debugger_pos_prediction.style.top = `${y_prediction - 12}px`; debugger_pos_prediction.innerText = '🔵'; } const debugger_safezone = document.body.appendChild(document.createElement('div')); function DEBUG_Safezone(x = -100, y = -100, radius = 0, info = '') { if (!DEBUG) return; debugger_safezone.style.pointerEvents = 'none'; debugger_safezone.style.position = 'absolute'; debugger_safezone.style.zIndex = '99999'; debugger_safezone.style.left = `${x - 11}px`; debugger_safezone.style.top = `${x - 12}px`; debugger_safezone.style.width = `${radius}px`; debugger_safezone.style.height = `${radius}px`; debugger_safezone.style['-webkit-border-radius'] = `${radius / 2}px`; debugger_safezone.style['-moz-border-radius'] = `${radius / 2}px`; debugger_safezone.style['border-radius'] = `${radius / 2}px` debugger_safezone.style.background = 'red'; debugger_safezone.style.opacity = 0.3; } /* * H E L P E R F U N C T I O N S */ function onbtnMultibox() { this.active = !this.active; if (this.active) { storage.multibox = true; this.innerHTML = 'Multiboxing: ON'; } else { storage.multibox = false; this.innerHTML = 'Multiboxing: OFF'; } } function onbtnAfk() { this.active = !this.active; if (this.active) { player.useGamepad = true; this.position = player.position; this.mouse = player.mouse; this.innerHTML = 'AFK: ON'; } else { player.useGamepad = false; this.innerHTML = 'AFK: OFF'; } } function onbtnToggleClump() { this.active = !this.active; if (this.active) { storage.clumpMode = 'mouse'; this.innerHTML = 'Clump: Mouse'; } else { storage.clumpMode = 'player'; this.innerHTML = 'Clump: Player'; } } function onbtnDiscord() { window.open('https://discord.gg/5q2E3Sx'); } function smallBoi() { player.isMaster = false; player.useGamepad = storage.multibox; const multiboxListener = storage.on('multibox', (name, old_value, new_value, remote) => { player.useGamepad = new_value; }); const keyDownListener = storage.on('keyDown', (name, old_value, new_value, remote) => { if ([-1, 65, 83, 87, 68, 37, 40, 38, 39].includes(new_value)) return; if (DEBUG) console.log('master keyDown', new_value); if (storage.multibox) { if ([1, 32].includes(new_value)) player._gamepad.leftMouse = true; else if ([3, 16].includes(new_value)) player._gamepad.rightMouse = true; unsafeWindow.input.keyDown(new_value); } }); const keyUpListener = storage.on('keyUp', (name, old_value, new_value, remote) => { if ([-1, 65, 83, 87, 68, 37, 40, 38, 39].includes(new_value)) return; if (DEBUG) console.log('master keyUp', new_value); if (storage.multibox) { if ([1, 32].includes(new_value)) player._gamepad.leftMouse = false; else if ([3, 16].includes(new_value)) player._gamepad.rightMouse = false; unsafeWindow.input.keyUp(new_value); } }); btnForceMaster = gui.addButton('Unlock this tab', () => { storage.reset(); storage.off(multiboxListener); storage.off(keyDownListener); storage.off(keyUpListener); gui.reset(); bigBoi(); }); } function bigBoi() { player.isMaster = true; storage.mutex = true; storage.clumpMode = 'player'; storage.once('mutex', (name, old_value, new_value, remote) => { if (!new_value) { gui.reset(); smallBoi(); } }); btnMultibox = gui.addButton('Multiboxing: OFF', onbtnMultibox, 'KeyF'); btnAfk = gui.addButton('AFK: OFF', onbtnAfk, 'KeyQ'); btnToggleClump = gui.addButton('Clump: Player', onbtnToggleClump); btnDiscord = gui.addButton('Discord', onbtnDiscord); } function mainLoop() { if (!unsafeWindow.input) return; if (player.isMaster) { storage.position = player.position; storage.inputs = player.inputs; if (!btnAfk.active) storage.mouse = player.mouse; if (btnAfk.active) { if (Vector.distance(btnAfk.position, player.position) > 50) { player.moveTo(btnAfk.position.x, btnAfk.position.y); } else { player.moveTo(player.position.x, player.position.y); } player.lookAt(btnAfk.mouse.x, btnAfk.mouse.y); } } else { const clumpMode = storage.clumpMode; const mouse = storage.mouse; let position; let bestPosition; if (clumpMode === 'player') { position = storage.position; bestPosition = player.findBestPos(position, storage.inputs); } else if (clumpMode === 'mouse') { position = mouse; bestPosition = position; } else { throw new Error('Unsupported clumpMode', clumpMode); } if (storage.multibox) { player.moveTo(bestPosition.x, bestPosition.y); player.lookAt(mouse.x, mouse.y); } //Debugging const mouseScreen = player.toScreenPos(mouse.x, mouse.y); DEBUG_MousePosition(mouseScreen.x, mouseScreen.y, `(${mouse.x}, ${mouse.y})`); const playerScreen = player.toScreenPos(position.x, position.y); const bestPositionScreen = player.toScreenPos(bestPosition.x, bestPosition.y); DEBUG_PlayerPosition(playerScreen.x, playerScreen.y, bestPositionScreen.x, bestPositionScreen.y, `(${bestPosition.x}, ${bestPosition.y})`); } } /* * M A I N */ const gui = new Gui('DiepBox by Cazka'); const player = new Player(); const storage = new MultiboxStorage(); let btnForceMaster; let btnMultibox; let btnToggleClump; let btnAfk; let btnDiscord; if (storage.mutex) smallBoi(); else bigBoi(); unsafeWindow.addEventListener('unload', () => { if (player.isMaster) { storage.reset(); } }); player.onkeyDown = key => { if (player.isMaster) { storage.keyDown = key; storage.keyDown = -1; } } player.onkeyUp = key => { if (player.isMaster) { storage.keyUp = key; storage.keyUp = -1; } } // run main Loop unsafeWindow.requestAnimationFrame = new Proxy(unsafeWindow.requestAnimationFrame, { apply: function (target, thisArg, args) { mainLoop(); return target.apply(thisArg, args); }, });