Diep.io Canvas Helper

canvas manipulation

目前为 2025-02-06 提交的版本。查看 最新版本

// ==UserScript==
// @name         Diep.io Canvas Helper
// @namespace    http://tampermonkey.net/
// @version      2.0.2
// @description  canvas manipulation
// @author       r!PsAw
// @match        https://diep.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=diep.io
// @grant        none
// @license      MIT
// ==/UserScript==
let debug_visible = true; //turn this on to draw lines to shapes & Arrows
/*
- Arrow detection √
- Shapes detection √

(Work in progress...)
- Player Detection
- different Drones Detection
- Bosses Detection
- Turrets Detection
- Text coordinates detect
- Scoreboard reader
*/

//COLOR GETTER SCRIPT (by r!Psaw, aka me :P )
let ui_color_range = {
    min: 1,
    max: 7
}

let net_color_range = {
    min: 0,
    max: 27
}

function get_style_color(property){
    return getComputedStyle(document.documentElement).getPropertyValue(property).trim();
}

//single use
//let diep_user_colors = update_your_colors();
//loop
let diep_user_colors;
function update_colors(){
    window.requestAnimationFrame(update_colors);
if(input && window.lobby_ip){
  diep_user_colors = update_your_colors();
  //console.log("updated colors:");
  //console.log(diep_user_colors);
}
}
window.requestAnimationFrame(update_colors);

function get_hex(convar){
    let diep_hex = input.get_convar(convar);
    let normal_hex = "#"+diep_hex.split("x")[1];
    return normal_hex;
}

function get_hidden(type, number){
    type === "UI"?(ui_color_range.min <= number && number <= ui_color_range.max)?null:console.log("illegal Number!"):type === "NET"?(net_color_range.min <= number && number <= net_color_range.max)?null:("illegal Number!"):console.log("illegal Type!");
    switch (type){
        case "UI":
            return get_style_color(`--uicolor${number}`);
            break
        case "NET":
            return get_style_color(`--netcolor${number}`);
            break
    }
}

function update_your_colors(){
    let temp_container = {
        background: get_hex("ren_background_color"),
        bar_background: get_hex("ren_bar_background_color"),
        border: get_hex("ren_border_color"),
        grid: get_hex("ren_grid_color"),
        healthbar_back: get_hex("ren_health_background_color"),
        healthbar_front: get_hex("ren_health_fill_color"),
        minimap: get_hex("ren_minimap_background_color"),
        minimap_border: get_hex("ren_minimap_border_color"),
        scorebar: get_hex("ren_score_bar_fill_color"),
        solid_border: get_hex("ren_stroke_solid_color"),
        xp_bar: get_hex("ren_xp_bar_fill_color"),
        ui1: get_hidden("UI", 1),
        ui2: get_hidden("UI", 2),
        ui3: get_hidden("UI", 3),
        ui4: get_hidden("UI", 4),
        ui5: get_hidden("UI", 5),
        ui6: get_hidden("UI", 6),
        ui7: get_hidden("UI", 7),
        smasher_and_dominator: get_hidden("NET", 0),
        barrels: get_hidden("NET", 1),
        body: get_hidden("NET", 2),
        blue_team: get_hidden("NET", 3),
        red_team: get_hidden("NET", 4),
        purple_team: get_hidden("NET", 5),
        green_team: get_hidden("NET", 6),
        shiny_shapes: get_hidden("NET", 7),
        square: get_hidden("NET", 8),
        triangle: get_hidden("NET", 9),
        pentagon: get_hidden("NET", 10),
        crasher: get_hidden("NET", 11),
        arena_closers_neutral_dominators: get_hidden("NET", 12),
        scoreboard_ffa_etc: get_hidden("NET", 13),
        maze_walls: get_hidden("NET", 14),
        others_ffa: get_hidden("NET", 15),
        necromancer_squares: get_hidden("NET", 16),
        fallen_bosses: get_hidden("NET", 17)
        }
     return temp_container;
};

//Actual script:
//variables
const crx = CanvasRenderingContext2D.prototype;
let methods = [
    'beginPath',
    'setTransform',
    'drawImage',
    'arc',
    'moveTo',
    'lineTo',
    'fill',
    'fillRect',
    'fillText',
    'stroke',
    'strokeRect',
    'strokeText',
    'clearRect'
];
let patterns = {
    arc: ['setTransform', 'arc', 'fill'],
    triangle: ["setTransform", "moveTo", "lineTo", "lineTo", "fill"],
    square: ["setTransform", "moveTo", "lineTo", "lineTo", "lineTo", "fill"],
    pentagon: ["setTransform", "moveTo", "lineTo", "lineTo", "lineTo", "lineTo", "fill"],
}

let bosses = {
    fallen_booster: {x: null, y: null},
    fallen_ol: {x: null, y: null},
    necromancer: {x: null, y: null},
    guardian: {x: null, y: null}
}
let drones = {
    over: [], //overlord, overseer, manager
    necro: [],
    battleship: [],
    base: [],
    summoner: [],
    guardian: []
}
let shapes = {
    squares: [],
    crashers: [],
    triangles: [],
    pentagons: [],
}
let arrows = {
    leader: {
        moveTo: [0, 0],
        lineTo1: [0, 0],
        lineTo2: [0, 0],
        center: [0, 0]
    },
    minimap: {
        moveTo: [0, 0],
        lineTo1: [0, 0],
        lineTo2: [0, 0],
        center: [0, 0]
    },
    dimension: {//invisible arrow, used to determine minimap size
        moveTo: [0, 0],
        lineTo1: [0, 0],
        lineTo2: [0, 0],
        center: [0, 0]
    },
}

let placeholder = [// script will work with this data, to store it in the actual one. This is done, because canvas api is retarded
    bosses,
    drones,
    shapes,
    arrows
];

//classes
class Proxy_communicator {
    constructor(){
        this.last = null;
        this.order = [];
        this.lines = []; //store xy from lineTo's here, because lineTo proxy class can only save 1 at the time
        this.transform = [0, 0, 0, 0, 0, 0];
    }
    announce(proxy_class){
        if(is_beginPath(proxy_class.name)){
            this.last = proxy_class;
            this.order = [];
            return
        }

        this.last = proxy_class;
        this.order.push(proxy_class.name);

        /*
        console.log(`
        announced:
        last ${this.last.name}
        order ${this.order}
        `);
        */

    }
    has_pattern(pattern){
        //console.log('used pattern:');
        //console.log(pattern);
        //console.log('current order:');
        //console.log(this.order);

        let o_l = this.order.length;
        let p_l = pattern.length;

        /*
        console.log(`
        order length: ${o_l}
        pattern length: ${p_l}
        `);
        */

        let counter = 0;
        if(o_l != p_l){
            return false;
        }

        //console.log('first condition met!');

        for(let i = 0; i < o_l; i++){
            /*
            console.log(`
            i ${i}
            pattern[i] ${pattern[i]}
            this.order[i] ${this.order[i]}
            counter ${counter}
            `);
            */

            if(pattern[i] === this.order[i]){
                counter++;
            }
        }

        //console.log(`final ${counter === p_l}`);

        return (counter === p_l);
    }
}

class Proxy_class {
    constructor(method){
        this.name = method;
        this.calls = 0;
        this.target = null;
        this.thisArgs = null;
        this.args = null;
        this.screenXY = [null, null];
    }
    update_tta(target, thisArgs, args){
        if(is_beginPath(this.name)){
            //console.log(`beginPath detected, resetting and quitting...`);

            reset_calls();
            return
        }

        /*
        console.log(`
        update_tta called on Proxy_class.name ${this.name}
        with arguments:
        ${target}
        ${thisArgs}
        ${args}
        `);
        */

        this.target = target;
        this.thisArgs = thisArgs;
        this.args = args;
    }
}

const communicator = new Proxy_communicator();
let method_classes = [];

//functions

function is_beginPath(name){
    //console.log(`is_beginPath called with ${name}`);

    return (name === 'beginPath');
}

function start_proxies(){
    //console.log('start_proxies called');

    methods.forEach(start_proxy);
}

function define_current(method){
    /*
    console.log(`
    define_current called with
    ${method}
    returning argument:
    ${method_classes.find((element) => element.name === method)}
    `);
    */

    return method_classes.find((element) => element.name === method);
}

function start_proxy(method){
    //console.log(`start_proxy called with ${method}`);

    //define class outside proxies, to avoid repetition
    let current = new Proxy_class(method);
    method_classes.push(current);

    //console.log(`${current} created a new Proxy_class & pushed inside method_classes, check:`);
    //console.log(method_classes);

    crx[method] = new Proxy(crx[method], {
        apply(target, thisArgs, args) {
            /*
            if(diep_user_colors && thisArgs.fillStyle === diep_user_colors.triangle){
                console.log(`color detected at ${method}. Args: ${args}`);
            }
            */
            current = define_current(method);

            //console.log(`current redefined inside proxy: ${method} to ${current}`);

            current.update_tta(target, thisArgs, args);
            current.calls++;
            communicator.announce(current);

            handle_proxy(method, {target: target, thisArgs: thisArgs, args: args});
            //logic
            return Reflect.apply(target, thisArgs, args);
        }
    });
}

function handle_proxy(type, values){
    //console.log(`working on proxy ${type}`);
    let current = define_current(type);
    let x, y;
    switch(type){
        case 'setTransform':
            communicator.setTransform = values.args;
            break
        case 'fill':
            if(communicator.has_pattern(patterns.arc)){
                x = communicator.setTransform[4];
                y = communicator.setTransform[5];
                let target = define_current('arc');
                target.screenXY = [x, y];
                debug({x:x,y:y});
                //console.log(`arc updated coords ${target.screenXY}`);
            }else if(communicator.has_pattern(patterns.triangle)){
                //console.log(values.thisArgs.fillStyle);
                //console.log(values.thisArgs.globalAlpha);
                if(is_arrow(values.thisArgs)){
                    switch(true){
                        case (values.thisArgs.globalAlpha === 0.3499999940395355):
                            update_arrow('leader');
                            break
                        case (values.thisArgs.globalAlpha > 0.9):
                            update_arrow('minimap');
                            break
                    }
                }

                if(is_shape('triangle', values.thisArgs)){
                    //console.log('TRIANGLE FOUND');
                    update_shape('triangle');
                }

                if(is_shape('crasher', values.thisArgs)){
                    //console.log('TRIANGLE FOUND');
                    update_shape('crasher');
                }
                //drone detection logic...
            }else if(communicator.has_pattern(patterns.square)){
                if(is_shape('square', values.thisArgs)){
                    //console.log('SQUARE FOUND');
                    update_shape('square');
                }
                //drone detection logic...
            }else if(communicator.has_pattern(patterns.pentagon)){
                if(is_shape('pentagon', values.thisArgs)){
                    //console.log('PENTAGON FOUND');
                    update_shape('pentagon');
                }
            }
            break
        case 'moveTo':
            current.screenXY = [values.args[0], values.args[1]];
            //console.log(`moveTo updated coords ${current.screenXY}`);
            //logic for shape & arrow recognition
            break
        case 'lineTo':
            current.screenXY = [values.args[0], values.args[1]];
            //console.log(`lineTo updated coords ${current.screenXY}`);
            communicator.lines.push(current.screenXY);
            //console.log('lineTo updated inside communicator:');
            //console.log(communicator.lines);
            //logic for shape & arrow recognition
            break
        case 'drawImage':
            x = communicator.setTransform[4];
            y = communicator.setTransform[5];
            current.screenXY = [x, y];
            break
        default:{
            //add logic
        }
    }
}

function is_arrow(context){
    return (context.fillStyle === '#000000');
}

function is_shape(type, context){
    if(diep_user_colors){
        return (diep_user_colors[type] === context.fillStyle);
    }
    return false;
}

function get_average(points){
    let result = [0, 0];
    for(let point of points){
        result[0] += point[0];
        result[1] += point[1];
    }
    result[0] /= points.length;
    result[1] /= points.length;
    return result;
}

function update_shape(type){
    let plural = type + 's'; //triangle = shapes.triangles
    //basically constructing the shape from information stored
    let moveTo = define_current('moveTo').screenXY;
    let points = [moveTo];
    let point_num = 1;
    let shape = new Map(); //used a map instead of Array, because I can't push a key with a value
    shape.set('moveTo', moveTo);

    for(let line of communicator.lines){
        points.push(line);
        shape.set(`lineTo${point_num}`, line);
        point_num++;
    }
    //console.log(points);
    shape.set('center', get_average(points));
    debug({x: shape.get('center')[0], y: shape.get('center')[1]});
    //console.log(shape);
    //adding the new made shape inside global array of shapes
    placeholder.shapes[plural].push(shape);
    //console.log(shapes[plural]);
}

function update_arrow(type){
    let moveTo = define_current('moveTo');
    let points = [moveTo.screenXY, communicator.lines[0], communicator.lines[1]];
    placeholder.arrows[type].moveTo = points[0];
    placeholder.arrows[type].lineTo1 = points[1];
    placeholder.arrows[type].lineTo2 = points[2];
    placeholder.arrows[type].center = get_average(points);
    debug({x: placeholder.arrows[type].center[0], y: placeholder.arrows[type].center[1]});
    //let ctx = canvas.getContext('2d');
    //ctx.fillRect(Arrows[type].center[0], Arrows[type].center[1], 150, 150);
    //console.log(Arrows[type]);
}

function placeholder_apply() {
    // Ensure `placeholder` properties exist before copying
    if (!placeholder.bosses) placeholder.bosses = {};
    if (!placeholder.shapes) placeholder.shapes = { squares: [], crashers: [], triangles: [], pentagons: [] };
    if (!placeholder.drones) placeholder.drones = { over: [], necro: [], battleship: [], base: [], summoner: [], guardian: [] };
    if (!placeholder.arrows) placeholder.arrows = { leader: {}, minimap: {}, dimension: {} };

    // Manually deep copy objects to avoid JSON issues
    bosses = structuredClone(placeholder.bosses);
    shapes = structuredClone(placeholder.shapes);
    drones = structuredClone(placeholder.drones);
    arrows = structuredClone(placeholder.arrows);

    // Reset placeholder arrays properly
    for (let key in placeholder) {
        if (typeof placeholder[key] === 'object' && placeholder[key] !== null) {
            for (let key2 in placeholder[key]) {
                if (Array.isArray(placeholder[key][key2])) {
                    placeholder[key][key2] = []; // Reset nested arrays
                }
            }
        }
    }
}

function reset_coords(){
    window.requestAnimationFrame(reset_coords);
    //console.log('called reset_coords');
    placeholder_apply();

    method_classes.forEach(reset_coord);
}
window.requestAnimationFrame(reset_coords);

function reset_coord(method_class){
    //console.log(`called reset_coord with ${method_class}`);

    method_class.screenXY = [null, null];
}

function reset_calls(){
    //console.log(`called reset_calls`);

    communicator.lines = [];
    method_classes.forEach(reset_call);
}

function reset_call(method_class){
    //console.log(`called reset_call with ${method_class}`);

    method_class.calls = 0;
}

function array_or_map(object){
    let answer = 'neither';
    (object instanceof Map) ? answer = 'Map' : (object instanceof Array)? answer = 'Array' : null;
    return answer;
}

function debug(to, from = {x: canvas.width/2, y: canvas.height/2}){
    if(!debug_visible){
        return;
    }
    let ctx = canvas.getContext('2d');
    ctx.moveTo(from.x, from.y);
    ctx.lineTo(to.x, to.y);
    ctx.stroke();
}

/* === external operations === */
function calculate_distance(x1, y1, x2, y2){
    return Math.sqrt((x2 - x1)^2 + (y2 - y1)^2);
}

function get_closest(array){
    let pos = {
        x: canvas.width/2,
        y: canvas.height/2
    };
    let smallest = {
        element: null,
        distance: canvas.width + canvas.height
    }
    let l = array.length;
    for(let i = 0; i < l; i++){
        let target = array[i];
        if(array_or_map(target) === 'Map'){
            target = array[i].get('center');
        }
        let d = calculate_distance(pos.x, pos.y, target[0], target[1]);
        if(smallest.distance > d){
            smallest.element = target;
            smallest.distance = d;
        }
    }
    return smallest.element;
}

function find_text(text, mode = 'strict', save = 'no'){
    //Note: text that is being updated very quickly (like Lvl 1 Tank) will not get detected properly
    let current = define_current('fillText');
    let result;
    if(mode === 'strict'){
        (current.args[0]===text) ? result = current.args[0] : null;
    }else{
        (current.args[0].includes(text)) ? result = current.args[0] : null;
    }
    return result;
}

//init
start_proxies();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址