Bonk.io Arrow Trajectory

Bonk.io script to draws out an arrow's full trajectory when aiming

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         Bonk.io Arrow Trajectory
// @version      2.7
// @description  Bonk.io script to draws out an arrow's full trajectory when aiming
// @author       Triton
// @icon         https://bonk.io/graphics/tt/favicon-32x32.png
// @match        https://bonk.io/*
// @match        https://bonk.io/gameframe-release.html
// @run-at       document-end
// @grant        none
// @namespace    https://greasyfork.org/users/1015072
// ==/UserScript==

(function () {
    'use strict';

    function injector(func) {
        if (window.location.hostname === "bonk.io" && window.location.pathname !== "/gameframe-release.html") {
            const waitForFrame = setInterval(() => {
                const frame = document.getElementById("maingameframe");
                if (frame && frame.contentWindow && frame.contentDocument) {
                    const script = document.createElement("script");
                    script.textContent = `(${func.toString()})();`;
                    frame.contentDocument.head.appendChild(script);
                    clearInterval(waitForFrame);
                }
            }, 500);
        } else {
            func();
        }
    }

    injector(function () {
        const scope = window;
        if (!scope.PIXI || !scope.PIXI.Graphics) return;

        //Constants
        const BASE_RADIUS_PPM = 19.709589041095892;

        const PHYSICS = {
            GRAVITY: 197,
            VEL_MULT: 592,
            VEL_BASE: 301
        };

        const CONFIG = {
            MAX_CHARGE: 1.5,
            KEY: "KeyZ",
            POINTS: 50,
            SIM_TIME: 5.0, //Set higher to show longer trajectory
            DRAW_ENEMIES: false, //Set to true to see enemy trajectories

            //Your trajectory settings
            LOCAL: {
                COLOR: 0xD3D3D3, //Sets trajectory color
                THICKNESS: 2,
                ALPHA: 0.1, //Sets trajectory transparency
            },

            //Enemy trajectory settings (ignore if DRAW_ENEMIES is false)
            ENEMY: {
                COLOR: 0xD3D3D3,
                THICKNESS: 2,
                ALPHA: 0.1,
            }
        };

        let myUserName = null;
        let myPlayerContainer = null;
        let gameWorld = null;
        let myPlayerID = -1;
        let arcGraphics = null;
        let isHoldingKey = false;
        let currentPPM = BASE_RADIUS_PPM;
        let pixiObjectId = 0;

        //Websocket hook
        const originalSend = scope.WebSocket.prototype.send;
        scope.WebSocket.prototype.send = function (args) {
            if (typeof args === "string" && args.startsWith('42[12,')) {
                try {
                    const json = JSON.parse(args.substring(2));
                    myUserName = json[1].userName;
                } catch (e) { }
            }
            return originalSend.apply(this, arguments);
        };

        //PIXI hook
        const originalDrawCircle = scope.PIXI.Graphics.prototype.drawCircle;
        scope.PIXI.Graphics.prototype.drawCircle = function (...args) {
            const radius = args[2];
            const parent = this.parent;

            setTimeout(() => {
                if (parent && parent.visible) {
                    if (parent._bonkId === undefined) parent._bonkId = pixiObjectId++;

                    let foundName = null;
                    if (parent.children) {
                        for (let i = 0; i < parent.children.length; i++) {
                            const c = parent.children[i];
                            if (c && c._text) { foundName = c._text; break; }
                        }
                    }

                    if (foundName) {
                        if (!myUserName) {
                            try {
                                const topBar = window.parent.document.getElementById("maingameframe").contentWindow.document.getElementById("pretty_top_name");
                                if (topBar) myUserName = topBar.textContent.trim();
                            } catch (e) {
                                console.error(`Username Error: ${e}`);
                            }
                        }

                        if (myUserName && foundName.trim() === myUserName.trim()) {
                            myPlayerContainer = parent;
                            myPlayerID = parent._bonkId;

                            if (parent.parent) {
                                gameWorld = parent.parent;
                            }

                            if (radius > 5 && radius < 60) currentPPM = radius;
                        }
                    }
                }
            }, 0);

            return originalDrawCircle.apply(this, args);
        };

        function getGlobalTransform(obj) {
            if (!obj || !obj.transform) return { x: 0, y: 0, rot: 0 };
            obj.updateTransform();
            const wt = obj.transform.worldTransform;
            const globalPos = obj.getGlobalPosition();
            return {
                x: globalPos.x,
                y: globalPos.y,
                rot: Math.atan2(wt.b, wt.a)
            };
        }

        document.addEventListener("keydown", (e) => {
            if (e.code === CONFIG.KEY) isHoldingKey = true;
        });
        document.addEventListener("keyup", (e) => {
            if (e.code === CONFIG.KEY) isHoldingKey = false;
        });

        const originalRAF = scope.requestAnimationFrame;

        scope.requestAnimationFrame = function (callback) {
            if (!arcGraphics && scope.PIXI) {
                arcGraphics = new scope.PIXI.Graphics();
            }

            if (gameWorld && gameWorld.transform && arcGraphics) {

                if (arcGraphics.parent !== gameWorld) {
                    gameWorld.addChild(arcGraphics);
                }

                arcGraphics.clear();

                const children = gameWorld.children;

                for (let i = 0; i < children.length; i++) {
                    const playerObj = children[i];

                    if (!playerObj || !playerObj.visible || !playerObj.children) continue;

                    let isMe = false;
                    let isPlayer = false;

                    for (let j = 0; j < playerObj.children.length; j++) {
                        const child = playerObj.children[j];
                        if (child && child._text) {
                            isPlayer = true;
                            if (myUserName && child._text.trim() === myUserName.trim()) {
                                isMe = true;
                            }
                            break;
                        }
                    }

                    if (!isPlayer) continue;

                    if (!isMe && !CONFIG.DRAW_ENEMIES) continue;

                    let aimerChild = null;
                    for (let j = 0; j < playerObj.children.length; j++) {
                        const c = playerObj.children[j];
                        if (c.constructor.name === 'e' || c.constructor.name === 'h') {
                            aimerChild = c;
                            break;
                        }
                    }

                    const arrowVisible = aimerChild && aimerChild.visible && aimerChild.alpha > 0.1;

                    let shouldDraw = false;

                    if (isMe) {
                        shouldDraw = isHoldingKey && arrowVisible;
                    } else {
                        shouldDraw = arrowVisible;
                    }

                    if (shouldDraw) {
                        const settings = isMe ? CONFIG.LOCAL : CONFIG.ENEMY;
                        arcGraphics.lineStyle(settings.THICKNESS, settings.COLOR, settings.ALPHA);

                        if (!playerObj._bonkChargeStart) {
                            playerObj._bonkChargeStart = Date.now();
                        }

                        const scale = currentPPM / BASE_RADIUS_PPM;
                        const G_term = PHYSICS.GRAVITY * scale;
                        const h = Math.min(((Date.now() - playerObj._bonkChargeStart) / 1000.0), CONFIG.MAX_CHARGE);
                        const V_total = (PHYSICS.VEL_MULT * h + PHYSICS.VEL_BASE) * scale;

                        const tf = getGlobalTransform(aimerChild);
                        const n = -tf.rot;
                        const cos_n = Math.cos(n);
                        const sin_n = Math.sin(n);

                        const startLocal = gameWorld.toLocal({ x: tf.x, y: tf.y });
                        arcGraphics.moveTo(startLocal.x, startLocal.y);

                        const dt = CONFIG.SIM_TIME / CONFIG.POINTS;

                        for (let k = 1; k <= CONFIG.POINTS; k++) {
                            const t = k * dt;
                            const dx = V_total * cos_n * t;
                            const dy = (V_total * sin_n * t) - (G_term * Math.pow(t, 2));

                            const pLoc = gameWorld.toLocal({
                                x: tf.x + dx,
                                y: tf.y - dy
                            });

                            arcGraphics.lineTo(pLoc.x, pLoc.y);
                        }
                    } else {
                        playerObj._bonkChargeStart = 0;
                    }
                }
            }

            return originalRAF.call(scope, callback);
        };
    });
})();