Greasy Fork

diepAPI

https://github.com/Cazka/diepAPI

当前为 2022-03-06 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/433681/1025072/diepAPI.js

// ==UserScript==
// @name         diepAPI
// @description  https://github.com/Cazka/diepAPI
// @version      2.1.1
// @author       Cazka
// @match        https://diep.io/*
// @icon         https://www.google.com/s2/favicons?domain=diep.io
// @namespace    https://greasyfork.org/users/541070
// @run-at       document-start
// @grant        none
// ==/UserScript==
(() => {
    if (window.diepAPI) return;

    var diepAPI;
    /******/ (() => {
        // webpackBootstrap
        /******/ 'use strict';
        /******/ // The require scope
        /******/ var __webpack_require__ = {};
        /******/
        /************************************************************************/
        /******/ /* webpack/runtime/define property getters */
        /******/ (() => {
            /******/ // define getter functions for harmony exports
            /******/ __webpack_require__.d = (exports, definition) => {
                /******/ for (var key in definition) {
                    /******/ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
                        /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
                        /******/
                    }
                    /******/
                }
                /******/
            };
            /******/
        })();
        /******/
        /******/ /* webpack/runtime/hasOwnProperty shorthand */
        /******/ (() => {
            /******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
            /******/
        })();
        /******/
        /******/ /* webpack/runtime/make namespace object */
        /******/ (() => {
            /******/ // define __esModule on exports
            /******/ __webpack_require__.r = (exports) => {
                /******/ if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                    /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
                    /******/
                }
                /******/ Object.defineProperty(exports, '__esModule', { value: true });
                /******/
            };
            /******/
        })();
        /******/
        /************************************************************************/
        var __webpack_exports__ = {};
        // ESM COMPAT FLAG
        __webpack_require__.r(__webpack_exports__);

        // EXPORTS
        __webpack_require__.d(__webpack_exports__, {
            CanvasKit: () => /* reexport */ CanvasKit,
            Vector: () => /* reexport */ Vector,
            arena: () => /* reexport */ arena,
            arenaScaling: () => /* reexport */ arenaScaling,
            entityManager: () => /* reexport */ entityManager,
            game: () => /* reexport */ game,
            gamepad: () => /* reexport */ gamepad,
            minimap: () => /* reexport */ minimap,
            player: () => /* reexport */ player,
        }); // CONCATENATED MODULE: ./src/vector.ts

        class Vector {
            x;
            y;
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            /**
             * ( u1 ) + ( v1 ) = ( u1 + v1 )
             * ( u2 )   ( v2 )   ( u2 + v2 )
             */
            static add(u, v) {
                return new Vector(u.x + v.x, u.y + v.y);
            }
            /**
             * ( u1 ) - ( v1 ) = ( u1 - v1 )
             * ( u2 )   ( v2 )   ( u2 - v2 )
             */
            static subtract(u, v) {
                return new Vector(u.x - v.x, u.y - v.y);
            }
            /**
             * ( u1 ) * ( v1 ) = ( u1 * v1 )
             * ( u2 )   ( v2 )   ( u2 * v2 )
             */
            static multiply(u, v) {
                return new Vector(u.x * v.x, u.y * v.y);
            }
            /**
             * ( u1 ) / ( v1 ) = ( u1 / v1 )
             * ( u2 )   ( v2 )   ( u2 / v2 )
             */
            static divide(u, v) {
                return new Vector(u.x / v.x, u.y / v.y);
            }
            /**
             * r * ( v1 ) = ( r * v1 )
             *     ( v2 )   ( r * v2 )
             */
            static scale(r, v) {
                return new Vector(r * v.x, r * v.y);
            }
            static round(v) {
                return new Vector(Math.round(v.x), Math.round(v.y));
            }
            static len(v) {
                return Math.sqrt(v.x ** 2 + v.y ** 2);
            }
            static distance(u, v) {
                return Vector.len(Vector.subtract(u, v));
            }
            /**
             * Calculates the [centroid](https://en.wikipedia.org/wiki/Centroid)
             */
            static centroid(...vertices) {
                const sum = vertices.reduce((acc, vec) => Vector.add(acc, vec), new Vector(0, 0));
                const centroid = Vector.scale(1 / vertices.length, sum);
                return centroid;
            }
            /**
             * Calcutes the radius from a set of vertices that are placed on a circle
             */
            static radius(...vertices) {
                const centroid = Vector.centroid(...vertices);
                const distance = vertices.reduce((acc, vec) => acc + Vector.distance(centroid, vec), 0);
                const radius = distance / vertices.length;
                return radius;
            }
        } // CONCATENATED MODULE: ./src/canvas_kit.ts

        class CanvasKit {
            /**
             * The consumer will be called before
             */
            static hook(method, consumer) {
                const target = window.CanvasRenderingContext2D.prototype;
                target[method] = new Proxy(target[method], {
                    apply(target, thisArg, args) {
                        if (thisArg.canvas.className !== 'CanvasKit-bypass') consumer(target, thisArg, args);
                        return Reflect.apply(target, thisArg, args);
                    },
                });
            }
            /**
             * replaces the function. Use `return Reflect.apply(target, thisArg, args);` in
             * your function to call the original function.
             */
            static replace(method, func) {
                const target = window.CanvasRenderingContext2D.prototype;
                target[method] = new Proxy(target[method], {
                    apply(target, thisArg, args) {
                        if (thisArg.canvas.className !== 'CanvasKit-bypass') return func(target, thisArg, args);
                        return Reflect.apply(target, thisArg, args);
                    },
                });
            }
            /**
             * The consumer will be called before.
             */
            static hookRAF(consumer) {
                window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, {
                    apply(target, thisArg, args) {
                        consumer();
                        return Reflect.apply(target, thisArg, args);
                    },
                });
            }
            /**
             * If you want to a canvas then create it with this method.
             */
            static createCanvas() {
                const canvas = document.createElement('canvas');
                canvas.className = 'CanvasKit-bypass';
                canvas.style.pointerEvents = 'none';
                canvas.style.position = 'fixed';
                canvas.style['z-index'] = 1;
                canvas.style.top = '0px';
                canvas.style.left = '0px';
                canvas.style.right = '0px';
                canvas.style.bottom = '0px';
                canvas.style.width = '100%';
                canvas.style.height = '100%';
                return canvas;
            }
        } // CONCATENATED MODULE: ./src/diep_gamepad.ts

        class DiepGamepad {
            #axes;
            #buttons;
            connected;
            /**
             * Emulates a Gampad
             * when `gamepad.connected` is set to `true` the game will
             * ignore following keyboard inputs:
             * 		W, A, S, D, upArrow, leftArrow, downArrow, rightArray
             *      leftMouse, rightMouse, Spacebar, Shift,
             *      MouseMovement to change tank angle
             * these are also the only keys we emulate with this gamepad
             *
             */
            constructor() {
                this.#axes = [0, 0, 0, 0];
                this.#buttons = [...Array(17)].map((x) => {
                    return { pressed: false };
                });
                this.connected = false;
                //window.navigator.getGamepads = () => [this.connected ? this.#toGamepad() : undefined];
                window.navigator.getGamepads = new Proxy(window.navigator.getGamepads, {
                    apply: (target, thisArg, args) => {
                        if (this.connected) return [this.#toGamepad()];
                        return Reflect.apply(target, thisArg, args);
                    },
                });
            }
            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;
            }
            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;
            }
            #toGamepad() {
                return {
                    axes: this.#axes,
                    buttons: this.#buttons,
                    mapping: 'standard',
                };
            }
        }
        const gamepad = new DiepGamepad(); // CONCATENATED MODULE: ./src/event_emitter.ts

        class EventEmitter extends EventTarget {
            /**
             *
             * @param {string} eventName The name of the event
             * @param  {...any} args The arguments that will be passed to the listener
             */
            emit(eventName, ...args) {
                this.dispatchEvent(new CustomEvent(eventName, { detail: args }));
            }
            /**
             *
             * @param {string} eventName The name of the event
             * @param {EventCallback} listener The callback function
             */
            on(eventName, listener) {
                this.addEventListener(eventName, (e) => Reflect.apply(listener, this, e.detail));
            }
            /**
             *
             * @param {string} eventName The name of the event
             * @param {EventCallback} listener The callback function
             */
            once(eventName, listener) {
                this.addEventListener(eventName, (e) => Reflect.apply(listener, this, e.detail), { once: true });
            }
            /**
             *
             * @param {string} eventName The name of the event
             * @param {EventCallback} listener The callback function
             */
            off(eventName, listener) {
                this.removeEventListener(eventName, listener);
            }
        } // CONCATENATED MODULE: ./src/game.ts

        class Game extends EventEmitter {
            #ready = false;
            constructor() {
                super();
                CanvasKit.hookRAF(() => this.#onframe());
            }
            #onframe() {
                if (!this.#ready && window.input !== undefined) {
                    this.#ready = true;
                    this.#onready();
                }
                super.emit('frame');
            }
            #onready() {
                setTimeout(() => super.emit('ready'), 100);
            }
        }
        const game = new Game(); // CONCATENATED MODULE: ./src/minimap.ts

        class Minimap {
            #minimapDim = new Vector(1, 1);
            #minimapPos = new Vector(0, 0);
            #viewportDim = new Vector(1, 1);
            #viewportPos = new Vector(0, 0);
            /**
             * @description The position of the arrow normalized to the range [0,1]
             */
            #arrowPos = new Vector(0.5, 0.5);
            #drawViewport = false;
            constructor() {
                game.once('ready', () => {
                    window.input.set_convar('ren_minimap_viewport', 'true');
                    window.input.set_convar = new Proxy(window.input.set_convar, {
                        apply: (target, thisArg, args) => {
                            if (args[0] === 'ren_minimap_viewport') this.#drawViewport = args[1];
                            else Reflect.apply(target, thisArg, args);
                        },
                    });
                });
                this._minimapHook();
                this._viewportHook();
                this._arrowHook();
            }
            get minimapDim() {
                return this.#minimapDim;
            }
            get minimapPos() {
                return this.#minimapPos;
            }
            get viewportDim() {
                return this.#viewportDim;
            }
            get viewportPos() {
                return this.#viewportPos;
            }
            get arrowPos() {
                return this.#arrowPos;
            }
            drawViewport(value) {
                this.#drawViewport = value;
            }
            _minimapHook() {
                CanvasKit.hook('strokeRect', (target, thisArg, args) => {
                    const transform = thisArg.getTransform();
                    this.#minimapDim = new Vector(transform.a, transform.d);
                    this.#minimapPos = new Vector(transform.e, transform.f);
                });
            }
            _viewportHook() {
                CanvasKit.replace('fillRect', (target, thisArg, args) => {
                    const transform = thisArg.getTransform();
                    if (
                        Math.round((transform.a / transform.d) * 10_000) !==
                        Math.round((window.innerWidth / window.innerHeight) * 10_000)
                    ) {
                        return Reflect.apply(target, thisArg, args);
                    }
                    if (transform.a >= window.innerWidth && transform.d >= window.innerHeight) {
                        return Reflect.apply(target, thisArg, args);
                    }
                    this.#viewportDim = new Vector(transform.a, transform.d);
                    this.#viewportPos = new Vector(transform.e, transform.f);
                    if (this.#drawViewport) return Reflect.apply(target, thisArg, args);
                });
            }
            _arrowHook() {
                let index = 0;
                let pointA;
                let pointB;
                let pointC;
                const calculatePos = () => {
                    const side1 = Math.round(Vector.distance(pointA, pointB));
                    const side2 = Math.round(Vector.distance(pointA, pointC));
                    const side3 = Math.round(Vector.distance(pointB, pointC));
                    if (side1 === side2 && side2 === side3) return;
                    const centroid = Vector.centroid(pointA, pointB, pointC);
                    const arrowPos = Vector.subtract(centroid, this.#minimapPos);
                    const position = Vector.divide(arrowPos, this.#minimapDim);
                    this.#arrowPos = position;
                };
                CanvasKit.hook('beginPath', (target, thisArg, args) => {
                    index = 1;
                });
                CanvasKit.hook('moveTo', (target, thisArg, args) => {
                    if (index === 1) {
                        index++;
                        pointA = new Vector(args[0], args[1]);
                        return;
                    }
                    index = 0;
                });
                CanvasKit.hook('lineTo', (target, thisArg, args) => {
                    if (index === 2) {
                        index++;
                        pointB = new Vector(args[0], args[1]);
                        return;
                    }
                    if (index === 3) {
                        index++;
                        pointC = new Vector(args[0], args[1]);
                        return;
                    }
                    index = 0;
                });
                CanvasKit.hook('fill', (target, thisArg, args) => {
                    if (index === 4) {
                        index++;
                        calculatePos();
                        return;
                    }
                    index = 0;
                });
            }
        }
        const minimap = new Minimap(); // CONCATENATED MODULE: ./src/camera.ts

        class Camera {
            get position() {
                const center = Vector.add(minimap.viewportPos, Vector.scale(0.5, minimap.viewportDim));
                const cameraPos = Vector.subtract(center, minimap.minimapPos);
                const normalized = Vector.divide(cameraPos, minimap.minimapDim);
                return arena.scale(normalized);
            }
        }
        const camera = new Camera(); // CONCATENATED MODULE: ./src/arena_scaling.ts

        class ArenaScaling {
            #scalingFactor = 1;
            constructor() {
                CanvasKit.hook('stroke', (target, thisArg, args) => {
                    if (thisArg.fillStyle === '#cdcdcd' && thisArg.globalAlpha !== 0) {
                        this.#scalingFactor = thisArg.globalAlpha * 10;
                    }
                });
            }
            get scalingFactor() {
                return this.#scalingFactor;
            }
            get windowRatio() {
                return Math.max(window.innerWidth / 1920, window.innerHeight / 1080);
            }
            get fov() {
                return this.#scalingFactor / this.windowRatio;
            }
            /**
             *
             * @param {Vector} v The vector in canvas units
             * @returns {Vector} The vector in arena units
             */
            toArenaUnits(v) {
                return Vector.scale(1 / this.#scalingFactor, v);
            }
            /**
             *
             * @param {Vector} v The vector in arena units
             * @returns {Vector} The vector in canvas units
             */
            toCanvasUnits(v) {
                return Vector.scale(this.#scalingFactor, v);
            }
            /**
             * Will translate coordinates from canvas to arena
             * @param {Vector} canvasPos The canvas coordinates
             * @returns {Vector} The `canvasPos` translated to arena coordinates
             */
            toArenaPos(canvasPos) {
                const direction = Vector.subtract(
                    canvasPos,
                    this.screenToCanvas(new Vector(window.innerWidth / 2, window.innerHeight / 2))
                );
                const scaled = this.toArenaUnits(direction);
                const arenaPos = Vector.add(scaled, camera.position);
                return arenaPos;
            }
            /**
             * Will translate coordinates from arena to canvas
             * @param {Vector} arenaPos The arena coordinates
             * @returns {Vector} The `arenaPos` translated to canvas coordinates
             */
            toCanvasPos(arenaPos) {
                const direction = Vector.subtract(arenaPos, camera.position);
                const scaled = this.toCanvasUnits(direction);
                const canvasPos = Vector.add(
                    scaled,
                    this.screenToCanvas(new Vector(window.innerWidth / 2, window.innerHeight / 2))
                );
                return canvasPos;
            }
            screenToCanvasUnits(n) {
                return n * window.devicePixelRatio;
            }
            canvasToScreenUnits(n) {
                return n / window.devicePixelRatio;
            }
            /**
             * Will translate coordinates from screen to canvas
             * @param v The screen coordinates
             * @returns The canvas coordinates
             */
            screenToCanvas(v) {
                return Vector.scale(window.devicePixelRatio, v);
            }
            /**
             * Will translate coordinates from canvas to screen
             * @param v The canvas coordinates
             * @returns the screen coordinates
             */
            canvasToScreen(v) {
                return Vector.scale(1 / window.devicePixelRatio, v);
            }
        }
        const arenaScaling = new ArenaScaling(); // CONCATENATED MODULE: ./src/arena.ts

        class Arena {
            #size = 1;
            constructor() {
                game.on('frame', () => {
                    const ratio = Vector.divide(minimap.minimapDim, minimap.viewportDim);
                    const arenaDim = Vector.multiply(
                        ratio,
                        arenaScaling.screenToCanvas(new Vector(window.innerWidth, window.innerHeight))
                    );
                    const arenaSize = Vector.round(arenaScaling.toArenaUnits(arenaDim));
                    this.#size = arenaSize.x;
                });
            }
            /**
             * @returns {number} The Arena size in arena units
             */
            get size() {
                return this.#size;
            }
            //These methods are not much used. can be moved to playerMovement.mjs where its currently only used.
            /**
             *
             * @param {Vector} vector The vector in [0, 1] coordinates
             * @returns {Vector} The scaled vector in [-Arena.size/2, Arena.size/2] coordinates
             */
            scale(vector) {
                const scale = (value) => Math.round(this.#size * (value - 0.5));
                return new Vector(scale(vector.x), scale(vector.y));
            }
            /**
             *
             * @param {Vector} vector - The scaled vector in [-Arena.size/2, Arena.size/2] coordinates
             * @returns {Vector} The unscaled vector in [0, 1] coordinates
             */
            unscale(vector) {
                const unscale = (value) => value / this.#size + 0.5;
                return new Vector(unscale(vector.x), unscale(vector.y));
            }
        }
        const arena = new Arena(); // CONCATENATED MODULE: ./src/movement.ts

        class Movement {
            #position = new Vector(0, 0);
            #velocity = new Vector(0, 0);
            /*
             * used for average velocity calculation
             */
            #velocitySamplesSize = 10;
            #velocitySamples = [];
            #velocitySamplesIndex = 0;
            #velocityLastNow = performance.now();
            get position() {
                return this.#position;
            }
            /**
             * Velocity in [diep_]units / second
             */
            get velocity() {
                return this.#velocity;
            }
            /**
             * Predict where this object will be after `time`
             * @param time The time in ms.
             */
            predictPos(time) {
                const duration = (time + performance.now() - this.#velocityLastNow) / 1000;
                return Vector.add(this.#position, Vector.scale(duration, this.#velocity));
            }
            updatePos(newPos) {
                this.#updateVelocity(newPos);
                this.#position = newPos;
            }
            #updateVelocity(newPos) {
                const now = performance.now();
                const time = (now - this.#velocityLastNow) / 1000;
                if (time === 0) return;
                this.#velocityLastNow = now;
                const velocity = Vector.scale(1 / time, Vector.subtract(newPos, this.#position));
                // add current velocity to our samples array
                this.#velocitySamples[this.#velocitySamplesIndex++] = velocity;
                this.#velocitySamplesIndex %= this.#velocitySamplesSize;
                // calculate the average velocity
                this.#velocity = Vector.scale(
                    1 / this.#velocitySamples.length,
                    this.#velocitySamples.reduce((acc, x) => Vector.add(acc, x))
                );
            }
        } // CONCATENATED MODULE: ./src/player_movement.ts

        class PlayerMovement extends Movement {
            /**
             * Using the minimap arrow to get the player position and velocity
             */
            constructor() {
                super();
                game.on('frame', () => super.updatePos(arena.scale(minimap.arrowPos)));
            }
        }
        const playerMovement = new PlayerMovement(); // CONCATENATED MODULE: ./src/player.ts

        const sleep = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
        class Player extends EventEmitter {
            #isDead = true;
            #mouseLock = false;
            #mouseCanvasPos = new Vector(0, 0);
            #mousePos = new Vector(0, 0);
            #gamemode = window.localStorage.gamemode;
            #level = 1;
            #tank = 'Tank';
            constructor() {
                super();
                game.once('ready', () => {
                    //Check dead or alive
                    game.on('frame', () => {
                        const isDead = !window.input.should_prevent_unload();
                        if (this.#isDead == isDead) return;
                        this.#isDead = isDead;
                        if (this.#isDead) this.#ondead();
                        else this.#onspawn();
                    });
                    //update mouse position
                    game.on('frame', () => {
                        this.#mousePos = arenaScaling.toArenaPos(this.#mouseCanvasPos);
                    });
                    //Mouse events
                    const canvas = document.getElementById('canvas');
                    canvas.onmousemove = new Proxy(canvas.onmousemove, {
                        apply: (target, thisArg, args) => {
                            if (this.#mouseLock) return;
                            this.#onmousemove(args[0]);
                            return Reflect.apply(target, thisArg, args);
                        },
                    });
                    canvas.onmousedown = new Proxy(canvas.onmousedown, {
                        apply: (target, thisArg, args) => {
                            if (this.#mouseLock) return;
                            this.#onmousedown(args[0]);
                            return Reflect.apply(target, thisArg, args);
                        },
                    });
                    canvas.onmouseup = new Proxy(canvas.onmouseup, {
                        apply: (target, thisArg, args) => {
                            if (this.#mouseLock) return;
                            this.#onmouseup(args[0]);
                            return Reflect.apply(target, thisArg, args);
                        },
                    });
                    //Key events
                    window.onkeydown = new Proxy(window.onkeydown, {
                        apply: (target, thisArg, args) => {
                            this.#onkeydown(args[0]);
                            return Reflect.apply(target, thisArg, args);
                        },
                    });
                    window.onkeyup = new Proxy(window.onkeyup, {
                        apply: (target, thisArg, args) => {
                            this.#onkeyup(args[0]);
                            return Reflect.apply(target, thisArg, args);
                        },
                    });
                    // tank and level event listener
                    CanvasKit.hook('fillText', (target, thisArg, args) => {
                        const text = args[0];
                        const match = text.match(/^Lvl (\d+) (\w*)$/);
                        if (match == null) {
                            return;
                        }
                        const newLevel = Number(match[1]);
                        const newTank = match[2];
                        // make sure to trigger events for all levels in between.
                        while (newLevel > this.#level + 1) {
                            super.emit('level', ++this.#level);
                        }
                        if (newLevel !== this.#level) super.emit('level', newLevel);
                        if (newTank !== this.#tank) super.emit('tank', newTank);
                        this.#level = newLevel;
                        this.#tank = match[2];
                    });
                });
            }
            get position() {
                return playerMovement.position;
            }
            get velocity() {
                return playerMovement.velocity;
            }
            get mouse() {
                return this.#mousePos;
            }
            get isDead() {
                return this.#isDead;
            }
            get gamemode() {
                return this.#gamemode;
            }
            get level() {
                return this.#level;
            }
            get tank() {
                return this.#tank;
            }
            /**
             * Predict where this object will be after `time`
             * @param time The time in ms
             */
            predictPos(time) {
                return playerMovement.predictPos(time);
            }
            /**
             * Fun function to upgrade to octo
             * @async
             */
            async octoBuild() {
                this.keyDown('k');
                await this.upgrade_stat(4, 7);
                await this.upgrade_stat(5, 7);
                await this.upgrade_stat(6, 7);
                await this.upgrade_stat(7, 7);
                await this.upgrade_stat(8, 5);
                await this.upgrade_tank(1);
                await this.upgrade_tank(2);
                await this.upgrade_tank(1);
                this.keyUp('k');
            }
            async #ondead() {
                await sleep(50);
                super.emit('dead');
            }
            async #onspawn() {
                this.#gamemode = window.localStorage.gamemode;
                await sleep(50);
                super.emit('spawn');
            }
            useGamepad(value) {
                gamepad.connected = value;
            }
            keyDown(key) {
                if (typeof key == 'string') {
                    if (key.length != 1) throw new Error(`diepAPI: Unsupported key: ${key}`);
                    key = key.toUpperCase().charCodeAt(0);
                }
                window.input.keyDown(key);
                this.#onkeydown({ keyCode: key });
            }
            keyUp(key) {
                if (typeof key == 'string') {
                    if (key.length != 1) throw new Error(`diepAPI: Unsupported key: ${key}`);
                    key = key.toUpperCase().charCodeAt(0);
                }
                window.input.keyUp(key);
                this.#onkeyup({ keyCode: key });
            }
            async keyPress(key) {
                this.keyDown(key);
                await sleep(200);
                this.keyUp(key);
                await sleep(10);
            }
            async spawn(name, attempts = 0) {
                if (!this.#isDead) return;
                if (name !== undefined) document.getElementById('textInput').value = name;
                await this.keyPress(13);
                await sleep(250);
                await this.spawn(name, attempts + 1);
            }
            async upgrade_stat(id, level) {
                if (id < 1 || id > 8) throw `diepAPI: ${id} is not a supported stat`;
                this.keyDown(85);
                for (let i = 0; i < level; i++) {
                    await this.keyPress(48 + id);
                }
                this.keyUp(85);
                await sleep(250);
            }
            async upgrade_tank(index) {
                index -= 1;
                const x_index = index % 2;
                const y_index = Math.floor(index / 2);
                const x = arenaScaling.screenToCanvasUnits(arenaScaling.windowRatio * (x_index * 115 + 97.5));
                const y = arenaScaling.screenToCanvasUnits(arenaScaling.windowRatio * (y_index * 110 + 120));
                this.#mouseLock = true;
                window.input.mouse(x, y);
                await this.keyPress(1);
                // wait 200 ms before disabling mouselock
                await sleep(200);
                this.#mouseLock = false;
                // wait 1500 ms for the animation to finish
                await sleep(1500);
            }
            moveTo(arenaPos) {
                if (gamepad.connected) {
                    const direction = Vector.subtract(arenaPos, this.position);
                    const distance = Vector.len(direction);
                    if (distance === 0) {
                        gamepad.x = 0;
                        gamepad.y = 0;
                        return;
                    }
                    //max speed
                    const velocity = Vector.scale(1 / distance, direction);
                    gamepad.x = velocity.x;
                    gamepad.y = velocity.y;
                } else {
                    const direction = Vector.subtract(arenaPos, this.position);
                    if (direction.x > 0) {
                        this.keyUp('a');
                        this.keyDown('d');
                    } else if (direction.x < 0) {
                        this.keyUp('d');
                        this.keyDown('a');
                    } else {
                        this.keyUp('a');
                        this.keyUp('d');
                    }
                    if (direction.y > 0) {
                        this.keyUp('w');
                        this.keyDown('s');
                    } else if (direction.y < 0) {
                        this.keyUp('s');
                        this.keyDown('w');
                    } else {
                        this.keyUp('w');
                        this.keyUp('s');
                    }
                }
            }
            lookAt(arenaPos) {
                const position = arenaScaling.toCanvasPos(arenaPos);
                window.input.mouse(position.x, position.y);
                this.#onmousemove({ clientX: position.x, clientY: position.y });
            }
            #onmousemove(e) {
                this.#mouseCanvasPos = arenaScaling.screenToCanvas(new Vector(e.clientX, e.clientY));
                if (gamepad.connected) {
                    const arenaPos = arenaScaling.toArenaPos(this.#mouseCanvasPos);
                    const direction = Vector.subtract(arenaPos, this.position);
                    let axes = Vector.scale(arenaScaling.fov / 1200 / 1.1, direction);
                    const length = Vector.len(axes);
                    if (length !== 0 && length < 0.15) {
                        axes = Vector.scale(0.15 / length, axes);
                    }
                    gamepad.mx = axes.x;
                    gamepad.my = axes.y;
                }
            }
            #onmousedown(e) {
                if (gamepad.connected) this.#onkeydown({ keyCode: e.which });
            }
            #onmouseup(e) {
                if (gamepad.connected) this.#onkeyup({ keyCode: e.which });
            }
            #onkeydown(e) {
                super.emit('keydown', e.keyCode);
                if (gamepad.connected) {
                    switch (e.keyCode) {
                        case 37:
                        case 65:
                            gamepad.x = -1;
                            break;
                        case 40:
                        case 83:
                            gamepad.y = 1;
                            break;
                        case 38:
                        case 87:
                            gamepad.y = -1;
                            break;
                        case 39:
                        case 68:
                            gamepad.x = 1;
                            break;
                        case 1:
                        case 32:
                            gamepad.leftMouse = true;
                            break;
                        case 3:
                        case 16:
                            gamepad.rightMouse = true;
                            break;
                    }
                }
            }
            #onkeyup(e) {
                super.emit('keyup', e.keyCode);
                if (gamepad.connected) {
                    switch (e.keyCode) {
                        case 37:
                        case 65:
                            gamepad.x = 0;
                            break;
                        case 40:
                        case 83:
                            gamepad.y = 0;
                            break;
                        case 38:
                        case 87:
                            gamepad.y = 0;
                            break;
                        case 39:
                        case 68:
                            gamepad.x = 0;
                            break;
                        case 1:
                        case 32:
                            gamepad.leftMouse = false;
                            break;
                        case 3:
                        case 16:
                            gamepad.rightMouse = false;
                            break;
                    }
                }
            }
        }
        const player = new Player(); // CONCATENATED MODULE: ./src/entity.ts

        var EntityType;
        (function (EntityType) {
            EntityType[(EntityType['Player'] = 0)] = 'Player';
            EntityType[(EntityType['Bullet'] = 1)] = 'Bullet';
            EntityType[(EntityType['Drone'] = 2)] = 'Drone';
            EntityType[(EntityType['Trap'] = 3)] = 'Trap';
            EntityType[(EntityType['Square'] = 4)] = 'Square';
            EntityType[(EntityType['Triangle'] = 5)] = 'Triangle';
            EntityType[(EntityType['Pentagon'] = 6)] = 'Pentagon';
            EntityType[(EntityType['AlphaPentagon'] = 7)] = 'AlphaPentagon';
            EntityType[(EntityType['Crasher'] = 8)] = 'Crasher';
            EntityType[(EntityType['UNKNOWN'] = 9)] = 'UNKNOWN';
        })(EntityType || (EntityType = {}));
        var EntityColor;
        (function (EntityColor) {
            EntityColor['TeamBlue'] = '#00b2e1';
            EntityColor['TeamRed'] = '#f14e54';
            EntityColor['TeamPurple'] = '#bf7ff5';
            EntityColor['TeamGreen'] = '#00e16e';
            EntityColor['Square'] = '#ffe869';
            EntityColor['Triangle'] = '#fc7677';
            EntityColor['Pentagon'] = '#768dfc';
            EntityColor['AlphaPentagon'] = '#768dfc';
            EntityColor['Crasher'] = '#f177dd';
            EntityColor['NecromancerDrone'] = '#fcc376';
        })(EntityColor || (EntityColor = {}));
        const TeamColors = [EntityColor.TeamBlue, EntityColor.TeamRed, EntityColor.TeamPurple, EntityColor.TeamGreen];
        /**
         * Represents an ingame Entity.
         *
         * Holds minimal information currently.
         */
        class Entity extends Movement {
            type;
            extras;
            constructor(type, extras = {}) {
                super();
                this.type = type;
                this.extras = extras;
            }
            updatePos(newPos) {
                super.updatePos(newPos);
            }
        } // CONCATENATED MODULE: ./src/entity_manager.ts

        /**
         * Entity Manager is used to access the information about the entities, that are currently drawn on the screen.
         * To access the entities the EntityManager exposes the EntityManager.entities field.
         */
        class EntityManager {
            #entities = [];
            #entitiesUpdated = [];
            constructor() {
                this.#triangleHook();
                this.#squareHook();
                this.#pentagonHook();
                //when is a bullet being drawn?
                //when is a player being drawn?
                this.#playerHook();
                game.on('frame', () => {
                    this.#entities = this.#entitiesUpdated;
                    this.#entitiesUpdated = [];
                });
            }
            get entities() {
                return this.#entities;
            }
            /**
             * Adds the entity to `#entitiesUpdated`.
             *
             * Will either find the entity in `#entities` or create a new `Entity`.
             */
            #add(type, position, extras = {}) {
                const entityIndex = this.#findEntity(type, position);
                let entity;
                if (entityIndex === -1) {
                    entity = new Entity(type, {
                        id: Math.random().toString(36).slice(2, 5),
                        timestamp: performance.now(),
                        ...extras,
                    });
                } else {
                    entity = this.#entities[entityIndex];
                }
                entity.updatePos(position);
                this.#entitiesUpdated.push(entity);
            }
            /**
             * Searches `#entities` for the entity that is closest to `position` and
             * returns the __index__ of that entity or __-1__ if there is no match.
             */
            #findEntity(type, position) {
                let result = -1;
                let shortestDistance = Number.MAX_SAFE_INTEGER;
                this.#entities.forEach((x, i) => {
                    const distance = Vector.distance(x.predictPos(0), position);
                    if (distance < shortestDistance) {
                        shortestDistance = distance;
                        result = i;
                    }
                });
                //if distance is too high
                if (shortestDistance > 28 /* accuracy */) {
                    return -1;
                }
                //sanity check
                if (EntityType.UNKNOWN !== type && this.#entities[result].type !== type) {
                    return -1;
                }
                return result;
            }
            /**
             * Will call the cb method, when a polygon with `numVertices` vertices is drawn.
             */
            #createPolygonHook(numVertices, cb) {
                let index = 0;
                let vertices = [];
                const onFillPolygon = (ctx) => {
                    cb(vertices, ctx);
                };
                CanvasKit.hook('beginPath', (target, thisArg, args) => {
                    index = 1;
                    vertices = [];
                });
                CanvasKit.hook('moveTo', (target, thisArg, args) => {
                    if (index === 1) {
                        index++;
                        vertices.push(new Vector(args[0], args[1]));
                        return;
                    }
                    index = 0;
                });
                CanvasKit.hook('lineTo', (target, thisArg, args) => {
                    if (index >= 2 && index <= numVertices) {
                        index++;
                        vertices.push(new Vector(args[0], args[1]));
                        return;
                    }
                    index = 0;
                });
                CanvasKit.hook('fill', (target, thisArg, args) => {
                    if (index === numVertices + 1) {
                        index++;
                        onFillPolygon(thisArg);
                        return;
                    }
                    index = 0;
                });
            }
            #triangleHook() {
                this.#createPolygonHook(3, (vertices, ctx) => {
                    const side1 = Math.round(Vector.distance(vertices[0], vertices[1]));
                    const side2 = Math.round(Vector.distance(vertices[0], vertices[2]));
                    const side3 = Math.round(Vector.distance(vertices[1], vertices[2]));
                    //ignore Minimap Arrow
                    if (side1 !== side2 || side2 !== side3) return;
                    //ignore Leader Arrow
                    if ('#000000' === ctx.fillStyle) return;
                    vertices = vertices.map((x) => arenaScaling.toArenaPos(x));
                    const position = Vector.centroid(...vertices);
                    const radius = Math.round(Vector.radius(...vertices));
                    const color = ctx.fillStyle;
                    let type;
                    switch (radius) {
                        case 23:
                            //battleship drone
                            if (TeamColors.includes(color)) type = EntityType.Drone;
                            break;
                        case 30:
                            //base drone
                            if (TeamColors.includes(color)) type = EntityType.Drone;
                            break;
                        case 35:
                            //small crasher
                            if (EntityColor.Crasher === color) type = EntityType.Crasher;
                            break;
                        case 40:
                        case 41:
                        case 42:
                        case 43:
                        case 44:
                        case 45:
                        case 46:
                            //overseer/overlord drone
                            if (TeamColors.includes(color)) type = EntityType.Drone;
                            break;
                        case 55:
                            //big crasher
                            if (EntityColor.Crasher === color) type = EntityType.Crasher;
                            //triangle
                            if (EntityColor.Triangle === color) type = EntityType.Triangle;
                            break;
                    }
                    if (type === undefined) type = EntityType.UNKNOWN;
                    this.#add(type, position, { color, radius });
                });
            }
            #squareHook() {
                this.#createPolygonHook(4, (vertices, ctx) => {
                    vertices = vertices.map((x) => arenaScaling.toArenaPos(x));
                    const position = Vector.centroid(...vertices);
                    const radius = Math.round(Vector.radius(...vertices));
                    const color = ctx.fillStyle;
                    let type;
                    switch (radius) {
                        case 55:
                            //square
                            if (EntityColor.Square === color) type = EntityType.Square;
                            //necromancer drone
                            if (TeamColors.includes(color) || EntityColor.NecromancerDrone === color)
                                type = EntityType.Drone;
                            break;
                    }
                    if (type === undefined) type = EntityType.UNKNOWN;
                    this.#add(type, position, { color, radius });
                });
            }
            #pentagonHook() {
                this.#createPolygonHook(5, (vertices, ctx) => {
                    vertices = vertices.map((x) => arenaScaling.toArenaPos(x));
                    const position = Vector.centroid(...vertices);
                    const radius = Math.round(Vector.radius(...vertices));
                    const color = ctx.fillStyle;
                    let type;
                    switch (radius) {
                        case 75:
                            if (EntityColor.Pentagon === color) type = EntityType.Pentagon;
                            break;
                        case 200:
                            if (EntityColor.AlphaPentagon === color) type = EntityType.AlphaPentagon;
                            break;
                    }
                    if (type === undefined) type = EntityType.UNKNOWN;
                    this.#add(type, position, { color, radius });
                });
            }
            #playerHook() {
                let index = 0;
                let position;
                let color;
                let radius;
                const onCircle = () => {
                    position = arenaScaling.toArenaPos(position);
                    radius = arenaScaling.toArenaUnits(new Vector(radius, radius)).x;
                    let type = EntityType.UNKNOWN;
                    if (radius > 53) {
                        type = EntityType.Player;
                    } else {
                        type = EntityType.Bullet;
                    }
                    this.#add(type, position, {
                        color,
                        radius,
                    });
                };
                //Sequence: beginPath -> arc -> fill -> beginPath -> arc -> fill -> arc
                CanvasKit.hook('beginPath', (target, thisArg, args) => {
                    //start
                    if (index !== 3) {
                        index = 1;
                        return;
                    }
                    if (index === 3) {
                        index++;
                        return;
                    }
                    index = 0;
                });
                //check when a circle is drawn.
                CanvasKit.hook('arc', (target, thisArg, args) => {
                    //outline
                    if (index === 1) {
                        index++;
                        const transform = thisArg.getTransform();
                        position = new Vector(transform.e, transform.f);
                        radius = transform.a;
                        return;
                    }
                    if (index === 4) {
                        index++;
                        color = thisArg.fillStyle;
                        return;
                    }
                    //last arc call
                    if (index === 6) {
                        index++;
                        onCircle();
                        return;
                    }
                    index = 0;
                });
                CanvasKit.hook('fill', (target, thisArg, args) => {
                    if (index === 2) {
                        index++;
                        return;
                    }
                    if (index === 5) {
                        index++;
                        return;
                    }
                    index = 0;
                });
            }
        }
        const entityManager = new EntityManager(); // CONCATENATED MODULE: ./src/index.ts

        diepAPI = __webpack_exports__;
        /******/
    })();

    window.diepAPI = diepAPI;
})();