// ==UserScript== // @name DiepBox by Cazka // @description made with much love // @version 0.1.2 // @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; this._buttons.splice(index, 1); button.remove(); button.active = false; } reset() { const head = this._buttons[0]; this._buttons.forEach((x, i) => { if (i === 0) return; this.removeButton(x); }); this._buttons = [head]; } _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._minimapHook(); this._arrowHook(); this._viewportHook(); } get x() { return +((this._pointX - this._x00) / this._minimapWidth).toFixed(6); } get y() { return +((this._pointY - this._y00) / this._minimapHeight).toFixed(6); } get x_previous() { return +((this._pointX_previous - this._x00) / this._minimapWidth).toFixed(6); } get y_previous() { return +((this._pointY_previous - this._y00) / this._minimapHeight).toFixed(6); } get scale() { return { x: this._viewportWidth / this._minimapWidth, y: this._viewportHeight / this._minimapHeight, }; } _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); } _ctxHook(method, callback) { const target = window.CanvasRenderingContext2D.prototype; target[method] = new Proxy(target[method], { apply(target, thisArg, args) { callback(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[5].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[5].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); } } class Player { constructor() { this._minimap = new Minimap(); this._gamepad = new DiepGamepad(); this._mouse = { x: 0, y: 0, }; 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() { return { x: this._minimap.x, y: this._minimap.y, x_previous: this._minimap.x_previous, y_previous: this._minimap.y_previous, } } get mouse() { return this.toArenaPos(this._mouse.x, this._mouse.y); } toScreenPos(x, y) { const position = this.position; 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) { const position = this.position; 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 { x, y } } moveTo(x, y) { const position = this.position; if (!position.x || !position.y) return; const deltaX = x - position.x; const deltaY = y - position.y; const length = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); if (length === 0) { this._gamepad.x = 0; this._gamepad.y = 0; return; } x = deltaX / length; y = deltaY / length; this._gamepad.x = x; this._gamepad.y = y; } lookAt(x, y) { const position = this.position; if (!position.x || !position.y) return; const deltaX = x - position.x; const deltaY = y - position.y; const length = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); x = deltaX * 1.05; y = deltaY * 1.05; if (length < 0.1) { x /= length * 9; y /= length * 9; } this._gamepad.mx = x; this._gamepad.my = y; } findBestPos(targetPosition) { const TOLERANCE = 0.009; //Strategies: //(1) Dont move to target if to close //(2) copy movement vector if to close //(3) predict future position //(4) make way when target moves to my direction? const me = this.position; const target = { x: targetPosition.x, y: targetPosition.y, x_previous: targetPosition.x_previous, y_previous: targetPosition.y_previous, } const meVector = { x: me.x - me.x_previous, y: me.y - me.y_previous } const targetVector = { x: target.x - target.x_previous, y: target.y - target.y_previous } const directionVector = { x: target.x - me.x, y: target.y - me.y } if (Vector.length(directionVector) < TOLERANCE) { // (2) me.x += targetVector.x; me.y += targetVector.y; return me; } // (3) target.x += targetVector.x * 25; target.y += targetVector.y * 25; return target; } _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) { this.onkeyDown && this.onkeyDown(e.keyCode); } _onkeyup(e) { 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 */ constructor() { //if the user launches this script for the first time try { this.mutex; } catch (err) { 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); } 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'); } 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; } 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) { 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 = '👆'; } 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) { 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 = '🟢'; 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 = '🔵'; } /* * 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 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, 87, 65, 83, 68].includes(new_value)) return; if (DEBUG) console.log('master keyDown', new_value); if (storage.multibox) { if (new_value === 1) player._gamepad.leftMouse = true; else if (new_value === 3) player._gamepad.rightMouse = true; unsafeWindow.input.keyDown(new_value); } }); const keyUpListener = storage.on('keyUp', (name, old_value, new_value, remote) => { if ([-1, 87, 65, 83, 68].includes(new_value)) return; if (DEBUG) console.log('master keyUp', new_value); if (storage.multibox) { if (new_value === 1) player._gamepad.leftMouse = false; else if (new_value === 3) 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.once('mutex', (name, old_value, new_value, remote) => { if (!new_value) { gui.reset(); smallBoi(); } }); btnMultibox = gui.addButton('Multiboxing: OFF', onbtnMultibox, 'KeyF'); } function mainLoop() { if (!unsafeWindow.input) return; if (player.isMaster) { storage.position = player.position; storage.mouse = player.mouse; } else { const position = storage.position; const bestPosition = player.findBestPos(position); const mouse = storage.mouse; 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); 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); } } /* * M A I N */ const gui = new Gui('DiepBox by Cazka'); const player = new Player(); const storage = new MultiboxStorage(); let btnForceMaster; let btnMultibox; 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); }, });