diepAPI

https://github.com/Cazka/diepAPI

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         diepAPI
// @description  An API for https://diep.io - https://github.com/Cazka/diepAPI#readme
// @version      3.3.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==
(() => {
  const _window = 'undefined' == typeof unsafeWindow ? window : unsafeWindow;
  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__, {
  apis: () => (/* reexport */ apis_namespaceObject),
  core: () => (/* reexport */ core_namespaceObject),
  extensions: () => (/* reexport */ extensions_namespaceObject),
  tools: () => (/* reexport */ tools_namespaceObject),
  types: () => (/* reexport */ types_namespaceObject)
});

// NAMESPACE OBJECT: ./src/apis/index.ts
var apis_namespaceObject = {};
__webpack_require__.r(apis_namespaceObject);
__webpack_require__.d(apis_namespaceObject, {
  arena: () => (arena),
  camera: () => (camera),
  game: () => (game),
  input: () => (input),
  minimap: () => (minimap),
  player: () => (player),
  playerMovement: () => (playerMovement),
  scaling: () => (scaling)
});

// NAMESPACE OBJECT: ./src/core/index.ts
var core_namespaceObject = {};
__webpack_require__.r(core_namespaceObject);
__webpack_require__.d(core_namespaceObject, {
  CanvasKit: () => (CanvasKit),
  EventEmitter: () => (EventEmitter),
  Movement: () => (Movement),
  Vector: () => (Vector)
});

// NAMESPACE OBJECT: ./src/extensions/index.ts
var extensions_namespaceObject = {};
__webpack_require__.r(extensions_namespaceObject);
__webpack_require__.d(extensions_namespaceObject, {
  debugTool: () => (debugTool),
  entityManager: () => (entityManager)
});

// NAMESPACE OBJECT: ./src/tools/index.ts
var tools_namespaceObject = {};
__webpack_require__.r(tools_namespaceObject);
__webpack_require__.d(tools_namespaceObject, {
  backgroundOverlay: () => (backgroundOverlay),
  overlay: () => (overlay)
});

// NAMESPACE OBJECT: ./src/types/index.ts
var types_namespaceObject = {};
__webpack_require__.r(types_namespaceObject);
__webpack_require__.d(types_namespaceObject, {
  Entity: () => (Entity),
  EntityColor: () => (EntityColor),
  EntityType: () => (EntityType)
});

;// CONCATENATED MODULE: ./src/core/vector.ts
class Vector {
    x;
    y;
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    static len(v) {
        return Math.sqrt(v.x ** 2 + v.y ** 2);
    }
    static round(v) {
        return new Vector(Math.round(v.x), Math.round(v.y));
    }
    static scale(r, v) {
        return new Vector(r * v.x, r * v.y);
    }
    static unscale(r, v) {
        return new Vector(v.x / r, v.y / r);
    }
    static add(u, v) {
        return new Vector(u.x + v.x, u.y + v.y);
    }
    static subtract(u, v) {
        return new Vector(u.x - v.x, u.y - v.y);
    }
    static multiply(u, v) {
        return new Vector(u.x * v.x, u.y * v.y);
    }
    static divide(u, v) {
        return new Vector(u.x / v.x, u.y / v.y);
    }
    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/core/canvas_kit.ts

const CANVAS_KIT_BYPASS = Symbol('CanvasKit-bypass');
class CanvasKit {
    /**
     * If you need a canvas then create it with this method.
     */
    static createCanvas() {
        const canvas = document.createElement('canvas');
        canvas[CANVAS_KIT_BYPASS] = true;
        canvas.style.pointerEvents = 'none';
        canvas.style.position = 'fixed';
        canvas.style.zIndex = '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;
    }
    /**
     * 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);
            },
        });
    }
    /**
     * The consumer will be called before
     */
    static hookCtx(method, consumer, useOffscreenCtx = false) {
        const target = useOffscreenCtx
            ? _window.OffscreenCanvasRenderingContext2D.prototype
            : _window.CanvasRenderingContext2D.prototype;
        target[method] = new Proxy(target[method], {
            apply(target, thisArg, args) {
                if (!thisArg.canvas[CANVAS_KIT_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 overrideCtx(method, func, useOffscreenCtx = false) {
        const target = useOffscreenCtx
            ? _window.OffscreenCanvasRenderingContext2D.prototype
            : _window.CanvasRenderingContext2D.prototype;
        target[method] = new Proxy(target[method], {
            apply(target, thisArg, args) {
                if (!thisArg.canvas[CANVAS_KIT_BYPASS])
                    return func(target, thisArg, args);
                return Reflect.apply(target, thisArg, args);
            },
        });
    }
    /**
     *
     * Calls the callback method when a polygon with `numVertices` is being drawn.
     */
    static hookPolygon(numVertices, cb, useOffscreenCtx = false) {
        let index = 0;
        let vertices = [];
        const onFillPolygon = (ctx) => cb(vertices, ctx);
        CanvasKit.hookCtx('beginPath', (target, thisArg, args) => {
            index = 1;
            vertices = [];
        }, useOffscreenCtx);
        CanvasKit.hookCtx('moveTo', (target, thisArg, args) => {
            if (index === 1) {
                index++;
                vertices.push(new Vector(args[0], args[1]));
                return;
            }
            index = 0;
        }, useOffscreenCtx);
        CanvasKit.hookCtx('lineTo', (target, thisArg, args) => {
            if (index >= 2 && index <= numVertices) {
                index++;
                vertices.push(new Vector(args[0], args[1]));
                return;
            }
            index = 0;
        }, useOffscreenCtx);
        CanvasKit.hookCtx('fill', (target, thisArg, args) => {
            if (index === numVertices + 1) {
                index++;
                onFillPolygon(thisArg);
                return;
            }
            index = 0;
        }, useOffscreenCtx);
    }
}

;// CONCATENATED MODULE: ./src/core/event_emitter.ts
class EventEmitter extends EventTarget {
    emit(eventName, ...args) {
        super.dispatchEvent(new CustomEvent(eventName, { detail: args }));
    }
    on(eventName, listener) {
        super.addEventListener(eventName, ((e) => {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            Reflect.apply(listener, this, e.detail);
        }));
    }
    once(eventName, listener) {
        super.addEventListener(eventName, ((e) => {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            Reflect.apply(listener, this, e.detail);
        }), {
            once: true,
        });
    }
    off(eventName, listener) {
        super.removeEventListener(eventName, listener);
    }
}

;// CONCATENATED MODULE: ./src/apis/game.ts


/**
 * Events:
 * - ready: Emitted when the game is ready
 * - frame_start: Emitted before `frame`.
 * - frame: Emitted every frame. Can be used for things that should be executed on every frame
 * - frame_end: Emitted after `frame` and is mainly used internally to update position variables
 * - state => (state): Emitted whenever the game changes its state: 'home', 'game', 'stats', 'loading', 'captcha
 * - s_home: Emitted when the game changes its state to home
 * - s_game: Emitted when the game changes its state to game
 * - s_stats: Emitted when the game changes its state to stats
 * - s_loading: Emitted when the game changes its state to loading
 * - s_captcha: Emitted when the game changes its state to captcha
 */
class Game extends EventEmitter {
    #ready = false;
    #shadowRoot;
    constructor() {
        super();
        CanvasKit.hookRAF(() => {
            this.#onframe();
        });
    }
    #onframe() {
        if (!this.#ready && _window.input !== undefined) {
            this.#ready = true;
            this.#onready();
        }
        super.emit('frame_start');
        super.emit('frame');
        super.emit('frame_end');
    }
    #onready() {
        setTimeout(() => {
            super.emit('ready');
        }, 100);
        // TODO: Causes the game not to load. Find a fix.
        // this.#shadowRoot = document.querySelector('d-base')?.shadowRoot;
        // if (this.#shadowRoot == null) {
        //   throw new Error('diepAPI: Shadow root does not exist.');
        // }
        // new MutationObserver((mutationList, observer) => {
        //   mutationList.forEach((mutation) => {
        //     if (mutation.addedNodes.length === 0) {
        //       return;
        //     }
        //     super.emit('state', this.state);
        //     super.emit(`s_${this.state}`);
        //   });
        // }).observe(this.#shadowRoot, { childList: true });
    }
    get state() {
        return this.#shadowRoot?.querySelector('.screen')?.tagName.slice(2).toLowerCase();
    }
    get inHome() {
        return this.state === 'home';
    }
    get inGame() {
        return this.state === 'game';
    }
    get inStats() {
        return this.state === 'stats';
    }
    get inLoading() {
        return this.state === 'loading';
    }
    get inCaptcha() {
        return this.state === 'captcha';
    }
}
const game = new Game();

;// CONCATENATED MODULE: ./src/apis/minimap.ts



/**
 * The Minimap API
 */
class Minimap {
    #minimapDim = new Vector(1, 1);
    #minimapPos = new Vector(0, 0);
    #viewportDim = new Vector(1, 1);
    #viewportPos = new Vector(1, 1);
    #arrowPos = new Vector(0.5, 0.5);
    #drawViewport = false;
    constructor() {
        game.once('ready', () => {
            if (_window.input == null) {
                throw new Error('diepAPI: window.input does not exist.');
            }
            _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];
                        return;
                    }
                    return 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;
    }
    #minimapHook() {
        CanvasKit.hookCtx('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.overrideCtx('fillRect', (target, thisArg, args) => {
            const transform = thisArg.getTransform();
            if (Math.round(thisArg.globalAlpha * 10) / 10 !== 0.1) {
                Reflect.apply(target, thisArg, args);
                return;
            }
            if (Math.abs(transform.a / transform.d - _window.innerWidth / _window.innerHeight) >
                (_window.innerWidth / _window.innerHeight) * 0.00005) {
                Reflect.apply(target, thisArg, args);
                return;
            }
            this.#viewportDim = new Vector(transform.a, transform.d);
            this.#viewportPos = new Vector(transform.e, transform.f);
            if (this.#drawViewport) {
                Reflect.apply(target, thisArg, args);
                return;
            }
        });
    }
    #arrowHook() {
        CanvasKit.hookPolygon(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]));
            if (side1 === side2 && side2 === side3)
                return;
            const centroid = Vector.centroid(...vertices);
            const arrowPos = Vector.subtract(centroid, this.#minimapPos);
            const position = Vector.divide(arrowPos, this.#minimapDim);
            this.#arrowPos = position;
        });
    }
}
const minimap = new Minimap();

;// CONCATENATED MODULE: ./src/apis/camera.ts




class Camera {
    #position = new Vector(0, 0);
    constructor() {
        game.on('frame_end', () => {
            const center = Vector.add(minimap.viewportPos, Vector.unscale(2, minimap.viewportDim));
            const cameraPos = Vector.subtract(center, minimap.minimapPos);
            const normalized = Vector.divide(cameraPos, minimap.minimapDim);
            this.#position = arena.scale(normalized);
        });
    }
    get position() {
        return this.#position;
    }
}
const camera = new Camera();

;// CONCATENATED MODULE: ./src/apis/scaling.ts



class Scaling {
    #scalingFactor = 1;
    #drawSolidBackground = false;
    constructor() {
        // TODO: game.on('ready')
        setTimeout(() => {
            if (_window.input == null) {
                throw new Error('diepAPI: window.input does not exist.');
            }
            _window.input.set_convar = new Proxy(_window.input.set_convar, {
                apply: (target, thisArg, args) => {
                    if (args[0] === 'ren_solid_background')
                        this.#drawSolidBackground = args[1];
                    else
                        Reflect.apply(target, thisArg, args);
                },
            });
        }, 1000);
        CanvasKit.overrideCtx('stroke', (target, thisArg, args) => {
            if (thisArg.fillStyle !== '#cccccc') {
                Reflect.apply(target, thisArg, args);
                return;
            }
            if (thisArg.globalAlpha === 0) {
                Reflect.apply(target, thisArg, args);
                return;
            }
            this.#scalingFactor = thisArg.globalAlpha * 10;
            if (!this.#drawSolidBackground) {
                Reflect.apply(target, thisArg, args);
                return;
            }
        });
    }
    get windowRatio() {
        return Math.max(_window.innerWidth / 1920, _window.innerHeight / 1080);
    }
    get scalingFactor() {
        return this.#scalingFactor;
    }
    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.round(Vector.unscale(this.#scalingFactor, v));
    }
    /**
     *
     * @param {Vector} v The vector in arena units
     * @returns {Vector} The vector in canvas units
     */
    toCanvasUnits(v) {
        return Vector.round(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 scaling = new Scaling();

;// CONCATENATED MODULE: ./src/apis/arena.ts



class Arena {
    #size = 1;
    constructor() {
        setInterval(() => {
            const ratio = Vector.divide(minimap.minimapDim, minimap.viewportDim);
            const arenaDim = Vector.multiply(ratio, scaling.screenToCanvas(new Vector(_window.innerWidth, _window.innerHeight)));
            const arenaSize = scaling.toArenaUnits(arenaDim);
            this.#size = arenaSize.x;
        }, 16);
    }
    /**
     * @returns {number} The Arena size in arena units
     */
    get size() {
        return this.#size;
    }
    /**
     *
     * @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/apis/input.ts

const sleep = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
class Input {
    #gameCanvas;
    constructor() {
        game.once('ready', () => {
            this.#gameCanvas = document.getElementById('canvas');
            if (this.#gameCanvas == null) {
                throw new Error('diepAPI: Game canvas does not exist.');
            }
        });
    }
    keyDown(key) {
        if (typeof key == 'string') {
            key = this.#toKeyCode(key);
        }
        const keydown = new KeyboardEvent('keydown', {
            key: '',
            code: '',
            keyCode: key,
            which: key,
            cancelable: true,
            composed: true,
            bubbles: true,
        });
        _window.dispatchEvent(keydown);
    }
    keyUp(key) {
        if (typeof key == 'string') {
            key = this.#toKeyCode(key);
        }
        const keyup = new KeyboardEvent('keyup', {
            key: '',
            code: '',
            keyCode: key,
            which: key,
            cancelable: true,
            composed: true,
            bubbles: true,
        });
        _window.dispatchEvent(keyup);
    }
    async keyPress(key) {
        this.keyDown(key);
        await sleep(200);
        this.keyUp(key);
        await sleep(10);
    }
    mouse(x, y) {
        const mousemove = new MouseEvent('mousemove', {
            clientX: x,
            clientY: y,
            cancelable: true,
            composed: true,
            bubbles: true,
        });
        this.#gameCanvas?.dispatchEvent(mousemove);
    }
    /**
     * button: 0 = left, 1 = middle, 2 = right
     */
    mouseDown(button) {
        const mouseDown = new MouseEvent('mousedown', {
            button: button,
            cancelable: true,
            composed: true,
            bubbles: true,
        });
        _window.dispatchEvent(mouseDown);
    }
    /**
     * button: 0 = left, 1 = middle, 2 = right
     */
    mouseUp(button) {
        const mouseUp = new MouseEvent('mouseup', {
            button: button,
            cancelable: true,
            composed: true,
            bubbles: true,
        });
        _window.dispatchEvent(mouseUp);
    }
    /**
     * button: 0 = left, 1 = middle, 2 = right
     */
    async mousePress(button) {
        this.mouseDown(button);
        await sleep(200);
        this.mouseUp(button);
        await sleep(10);
    }
    #toKeyCode(key) {
        if (key.length != 1) {
            throw new Error(`diepAPI: Unsupported key: ${key}`);
        }
        return key.toUpperCase().charCodeAt(0);
    }
}
const input = new Input();

;// CONCATENATED MODULE: ./src/apis/gamepad.ts
class Gamepad {
    #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.from({ length: 17 }, () => ({ pressed: false }));
        this.connected = false;
        // eslint-disable-next-line @typescript-eslint/unbound-method
        _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;
    }
    get x() {
        return this.#axes[0];
    }
    set y(value) {
        this.#axes[1] = value;
    }
    get y() {
        return this.#axes[1];
    }
    set mx(value) {
        this.#axes[2] = value;
    }
    get mx() {
        return this.#axes[2];
    }
    set my(value) {
        this.#axes[3] = value;
    }
    get my() {
        return this.#axes[3];
    }
    set leftMouse(value) {
        this.#buttons[7].pressed = value;
    }
    get leftMouse() {
        return this.#buttons[7].pressed;
    }
    set rightMouse(value) {
        this.#buttons[6].pressed = value;
    }
    get rightMouse() {
        return this.#buttons[6].pressed;
    }
    #toGamepad() {
        return {
            axes: this.#axes,
            buttons: this.#buttons,
            mapping: 'standard',
        };
    }
}
const gamepad = new Gamepad();

;// CONCATENATED MODULE: ./src/core/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.unscale(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.unscale(this.#velocitySamples.length, this.#velocitySamples.reduce((acc, x) => Vector.add(acc, x)));
    }
}

;// CONCATENATED MODULE: ./src/apis/player_movement.ts




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

;// CONCATENATED MODULE: ./src/apis/player.ts








const player_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);
    #username = _window.localStorage.name ?? '';
    #gamemode = _window.localStorage.selected_gamemode ?? '';
    #level = 1;
    #tank = 'Tank';
    constructor() {
        super();
        game.once('ready', () => {
            //Check dead or alive
            game.on('frame', () => {
                const isDead = document.getElementById('dimmer')?.dataset.isActive === 'true';
                if (this.#isDead == isDead)
                    return;
                this.#isDead = isDead;
                if (this.#isDead)
                    void this.#ondead();
                else
                    void this.#onspawn();
            });
            //update mouse position
            game.on('frame', () => {
                this.#mousePos = scaling.toArenaPos(this.#mouseCanvasPos);
            });
            //Mouse events
            const canvas = document.getElementById('canvas');
            if (canvas == null) {
                throw new Error('diepAPI: Game canvas does not exist.');
            }
            canvas.onmousemove = new Proxy(canvas.onmousemove ??
                (() => {
                    /* empty */
                }), {
                apply: (target, thisArg, args) => {
                    if (this.#mouseLock)
                        return;
                    this.#onmousemove(args[0]);
                    return Reflect.apply(target, thisArg, args);
                },
            });
            canvas.onmousedown = new Proxy(canvas.onmousedown ??
                (() => {
                    /* empty */
                }), {
                apply: (target, thisArg, args) => {
                    if (this.#mouseLock)
                        return;
                    this.#onmousedown(args[0]);
                    return Reflect.apply(target, thisArg, args);
                },
            });
            canvas.onmouseup = new Proxy(canvas.onmouseup ??
                (() => {
                    /* empty */
                }), {
                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 ??
                (() => {
                    /* empty */
                }), {
                apply: (target, thisArg, args) => {
                    this.#onkeydown(args[0]);
                    return Reflect.apply(target, thisArg, args);
                },
            });
            _window.onkeyup = new Proxy(_window.onkeyup ??
                (() => {
                    /* empty */
                }), {
                apply: (target, thisArg, args) => {
                    this.#onkeyup(args[0]);
                    return Reflect.apply(target, thisArg, args);
                },
            });
            // tank and level event listener
            CanvasKit.hookCtx('fillText', (target, thisArg, args) => {
                const text = args[0];
                const match = /^Lvl (\d+) (.+)$/.exec(text);
                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);
    }
    async #ondead() {
        await player_sleep(50);
        super.emit('dead');
    }
    async #onspawn() {
        this.#gamemode = _window.localStorage.selected_gamemode ?? '';
        this.#username = _window.localStorage.player_name ?? '';
        await player_sleep(50);
        super.emit('spawn');
    }
    useGamepad(value) {
        gamepad.connected = value;
    }
    async spawn(name = this.#username) {
        await Promise.resolve();
        if (!this.#isDead) {
            return;
        }
        const spawnNameInput = document.getElementById('spawn-nickname');
        spawnNameInput.select();
        document.execCommand('insertText', false, name);
        document.getElementById('spawn-button')?.click();
    }
    async upgrade_stat(id, level) {
        if (id < 1 || id > 8)
            throw new Error(`diepAPI: ${id} is not a supported stat`);
        input.keyDown(85);
        for (let i = 0; i < level; i++) {
            await input.keyPress(48 + id);
        }
        input.keyUp(85);
        await player_sleep(250);
    }
    async upgrade_tank(index) {
        if (index < 1)
            throw new Error(`diepAPI: ${index} is not a supported tank index`);
        index -= 1;
        const x_index = index % 2;
        const y_index = Math.floor(index / 2);
        const x = scaling.screenToCanvasUnits(scaling.windowRatio * (x_index * 115 + 97.5));
        const y = scaling.screenToCanvasUnits(scaling.windowRatio * (y_index * 110 + 120));
        this.#mouseLock = true;
        input.mouse(x, y);
        await input.mousePress(0);
        // wait 200 ms before disabling mouselock
        await player_sleep(200);
        this.#mouseLock = false;
        // wait 1500 ms for the animation to finish
        await player_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) {
                input.keyUp('a');
                input.keyDown('d');
            }
            else if (direction.x < 0) {
                input.keyUp('d');
                input.keyDown('a');
            }
            else {
                input.keyUp('a');
                input.keyUp('d');
            }
            if (direction.y > 0) {
                input.keyUp('w');
                input.keyDown('s');
            }
            else if (direction.y < 0) {
                input.keyUp('s');
                input.keyDown('w');
            }
            else {
                input.keyUp('w');
                input.keyUp('s');
            }
        }
    }
    lookAt(arenaPos) {
        const position = scaling.toCanvasPos(arenaPos);
        input.mouse(position.x, position.y);
        this.#onmousemove({ clientX: position.x, clientY: position.y });
    }
    #onmousemove(e) {
        this.#mouseCanvasPos = scaling.screenToCanvas(new Vector(e.clientX, e.clientY));
        if (gamepad.connected) {
            const arenaPos = scaling.toArenaPos(this.#mouseCanvasPos);
            const direction = Vector.subtract(arenaPos, this.position);
            let axes = Vector.scale(scaling.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/apis/index.ts









;// CONCATENATED MODULE: ./src/core/index.ts





;// CONCATENATED MODULE: ./src/tools/overlay.ts


class Overlay {
    canvas;
    ctx;
    constructor() {
        this.canvas = CanvasKit.createCanvas();
        const ctx = this.canvas.getContext('2d');
        if (ctx == null) {
            throw new Error('diepAPI: Your browser does not support canvas.');
        }
        this.ctx = ctx;
        game.once('ready', () => {
            document.body.appendChild(this.canvas);
            _window.addEventListener('resize', () => {
                this.#onResize();
            });
            game.on('frame_start', () => {
                this.#onFrameStart();
            });
            this.#onResize();
        });
    }
    #onResize() {
        this.canvas.width = _window.innerWidth * _window.devicePixelRatio;
        this.canvas.height = _window.innerHeight * _window.devicePixelRatio;
    }
    #onFrameStart() {
        this.canvas.width = _window.innerWidth * _window.devicePixelRatio;
        this.canvas.height = _window.innerHeight * _window.devicePixelRatio;
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);
    }
}
const overlay = new Overlay();

;// CONCATENATED MODULE: ./src/types/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";
    // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
    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;
    parent;
    extras;
    constructor(type, parent, extras) {
        super();
        this.type = type;
        this.parent = parent;
        this.extras = extras;
    }
    updatePos(newPos) {
        super.updatePos(newPos);
    }
}

;// CONCATENATED MODULE: ./src/extensions/extension.ts
class Extension {
    onload;
    #loaded = false;
    constructor(onload) {
        this.onload = onload;
    }
    load() {
        if (this.#loaded) {
            return;
        }
        this.#loaded = true;
        this.onload();
    }
}

;// CONCATENATED MODULE: ./src/extensions/entity_manager.ts




const random_id = () => Math.random().toString(36).slice(2, 5);
/**
 * 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 extends Extension {
    #entities = [];
    #entitiesLastFrame = this.#entities;
    constructor() {
        super(() => {
            game.on('frame_end', () => {
                this.#entitiesLastFrame = this.#entities;
                this.#entities = [];
            });
            this.#triangleHook();
            this.#squareHook();
            this.#pentagonHook();
            //when is a bullet being drawn?
            //when is a player being drawn?
            this.#playerHook();
        });
    }
    get entities() {
        return this.#entities;
    }
    /**
     *
     * @returns The own player entity
     */
    getPlayer() {
        const player = this.#entities.filter((entity) => entity.type == EntityType.Player &&
            Vector.distance(entity.position, playerMovement.position) < 28);
        return player[0];
    }
    /**
     * Adds the entity to `#entities`.
     *
     * Will either find the entity in `#entitiesLastFrame` or create a new `Entity`.
     */
    #add(type, position, extras) {
        let entity = this.#findEntity(type, position);
        if (!entity) {
            const parent = this.#findParent(type, position);
            entity = new Entity(type, parent, {
                id: random_id(),
                timestamp: performance.now(),
                ...extras,
            });
        }
        //TODO: remove radius from extras
        entity.extras.radius = extras.radius;
        entity.updatePos(position);
        this.#entities.push(entity);
    }
    /**
     * If an entity is newly created, try to find it's parent entity.
     */
    #findParent(type, position) {
        if (type == EntityType.Bullet) {
            // TODO: do we want to change the parent entity to EntityType.Barrel in the future?
            return this.#findEntity(EntityType.Player, position, 300);
        }
    }
    /**
     * Searches `#entitiesLastFrame` for the entity that is closest to `position`
     * @returns the entity or null if there is no match.
     */
    #findEntity(type, position, tolerance = 42) {
        let result = undefined;
        let shortestDistance = Infinity;
        this.#entitiesLastFrame.forEach((entity) => {
            if (entity.type !== type)
                return;
            const distance = Vector.distance(entity.position, position);
            if (distance < shortestDistance) {
                shortestDistance = distance;
                result = entity;
            }
        });
        if (shortestDistance > tolerance) {
            return undefined;
        }
        return result;
    }
    #triangleHook() {
        CanvasKit.hookPolygon(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) => scaling.toArenaPos(x));
            const position = Vector.centroid(...vertices);
            const radius = Math.round(Vector.radius(...vertices));
            const color = ctx.fillStyle;
            let type = EntityType.UNKNOWN;
            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;
            }
            this.#add(type, position, { color, radius });
        });
    }
    #squareHook() {
        CanvasKit.hookPolygon(4, (vertices, ctx) => {
            vertices = vertices.map((x) => scaling.toArenaPos(x));
            const position = Vector.centroid(...vertices);
            const radius = Math.round(Vector.radius(...vertices));
            const color = ctx.fillStyle;
            let type = EntityType.UNKNOWN;
            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;
            }
            this.#add(type, position, { color, radius });
        });
    }
    #pentagonHook() {
        CanvasKit.hookPolygon(5, (vertices, ctx) => {
            vertices = vertices.map((x) => scaling.toArenaPos(x));
            const position = Vector.centroid(...vertices);
            const radius = Math.round(Vector.radius(...vertices));
            const color = ctx.fillStyle;
            let type = EntityType.UNKNOWN;
            switch (radius) {
                case 75:
                    if (EntityColor.Pentagon === color)
                        type = EntityType.Pentagon;
                    break;
                case 200:
                    if (EntityColor.AlphaPentagon === color)
                        type = EntityType.AlphaPentagon;
                    break;
            }
            this.#add(type, position, { color, radius });
        });
    }
    #playerHook() {
        let index = 0;
        let position;
        let color;
        let radius;
        const onCircle = () => {
            position = scaling.toArenaPos(position);
            radius = scaling.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.hookCtx('beginPath', (target, thisArg, args) => {
            //start
            if (index !== 3) {
                index = 1;
                return;
            }
            // TODO: check if this is a bug.
            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
            if (index === 3) {
                index++;
                return;
            }
            index = 0;
        });
        //check when a circle is drawn.
        CanvasKit.hookCtx('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.hookCtx('fill', (target, thisArg, args) => {
            if (index === 2) {
                index++;
                return;
            }
            if (index === 5) {
                index++;
                return;
            }
            index = 0;
        });
    }
}
const entityManager = new EntityManager();

;// CONCATENATED MODULE: ./src/extensions/debug_tool.ts






class DebugTool extends Extension {
    #drawBoundingBox = false;
    #drawVelocity = false;
    #drawParent = false;
    #drawInfo = false;
    #drawStats = false;
    constructor() {
        super(() => {
            entityManager.load();
            game.on('frame', () => {
                entityManager.entities.forEach((entity) => {
                    const position = scaling.toCanvasPos(entity.position);
                    const futurePos = scaling.toCanvasPos(entity.predictPos(1000));
                    const dimensions = scaling.toCanvasUnits(new Vector(2 * (entity.extras.radius ?? 0), 2 * (entity.extras.radius ?? 0)));
                    if (this.#drawBoundingBox) {
                        this.#_drawboundingBox(entity, position, dimensions);
                    }
                    if (this.#drawVelocity) {
                        this.#_drawVelocity(position, futurePos);
                    }
                    if (this.#drawParent) {
                        this.#_drawParent(entity, position);
                    }
                    if (this.#drawInfo) {
                        this.#_drawInfo(entity, position, dimensions);
                    }
                });
                if (this.#drawStats) {
                    this.#_drawStats();
                }
            });
        });
    }
    drawAll(v) {
        this.#drawBoundingBox = v;
        this.#drawVelocity = v;
        this.#drawParent = v;
        this.#drawInfo = v;
        this.#drawStats = v;
    }
    drawBoundingBox(v) {
        this.#drawBoundingBox = v;
    }
    drawVelocity(v) {
        this.#drawVelocity = v;
    }
    drawParent(v) {
        this.#drawParent = v;
    }
    drawInfo(v) {
        this.#drawInfo = v;
    }
    drawStats(v) {
        this.#drawStats = v;
    }
    #_drawboundingBox(entity, position, dimensions) {
        overlay.ctx.save();
        overlay.ctx.strokeStyle =
            entity.type === EntityType.UNKNOWN ? '#ffffff' : (entity.extras.color ?? '#ffffff');
        overlay.ctx.lineWidth = scaling.toCanvasUnits(new Vector(5, 5)).x;
        overlay.ctx.strokeRect(position.x - dimensions.x / 2, position.y - dimensions.y / 2, dimensions.x, dimensions.y);
        overlay.ctx.restore();
    }
    #_drawVelocity(position, futurePos) {
        overlay.ctx.save();
        overlay.ctx.strokeStyle = '#000000';
        overlay.ctx.lineWidth = scaling.toCanvasUnits(new Vector(5, 5)).x;
        overlay.ctx.beginPath();
        overlay.ctx.moveTo(position.x, position.y);
        overlay.ctx.lineTo(futurePos.x, futurePos.y);
        overlay.ctx.stroke();
        overlay.ctx.restore();
    }
    #_drawParent(entity, position) {
        if (entity.parent == null) {
            return;
        }
        const parentPos = scaling.toCanvasPos(entity.parent.position);
        overlay.ctx.save();
        overlay.ctx.strokeStyle = '#8aff69';
        overlay.ctx.lineWidth = scaling.toCanvasUnits(new Vector(5, 5)).x;
        overlay.ctx.beginPath();
        overlay.ctx.moveTo(position.x, position.y);
        overlay.ctx.lineTo(parentPos.x, parentPos.y);
        overlay.ctx.stroke();
        overlay.ctx.restore();
    }
    #_drawInfo(entity, position, dimensions) {
        overlay.ctx.save();
        const fontSize = scaling.toCanvasUnits(new Vector(30, 30)).x;
        overlay.ctx.font = `${fontSize}px Ubuntu`;
        overlay.ctx.fillStyle = `#ffffff`;
        overlay.ctx.strokeStyle = '#000000';
        overlay.ctx.lineWidth = fontSize / 5;
        const text = `${entity.extras.id} ${Math.floor((performance.now() - entity.extras.timestamp) / 1000)}`;
        const textMetrics = overlay.ctx.measureText(text);
        const textWidth = textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft;
        const textHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
        overlay.ctx.strokeText(text, position.x - textWidth / 2, position.y - dimensions.y / 2 - textHeight / 2);
        overlay.ctx.fillText(text, position.x - textWidth / 2, position.y - dimensions.y / 2 - textHeight / 2);
        const positionText = `${entity.position.x.toFixed()},${entity.position.y.toFixed()}`;
        const positionTextMetrics = overlay.ctx.measureText(positionText);
        const positionTextWidth = positionTextMetrics.actualBoundingBoxRight + positionTextMetrics.actualBoundingBoxLeft;
        const positionTextHeight = positionTextMetrics.actualBoundingBoxAscent + positionTextMetrics.actualBoundingBoxDescent;
        overlay.ctx.strokeText(positionText, position.x - positionTextWidth / 2, position.y + dimensions.y / 2 + positionTextHeight);
        overlay.ctx.fillText(positionText, position.x - positionTextWidth / 2, position.y + dimensions.y / 2 + positionTextHeight);
        overlay.ctx.restore();
    }
    #_drawStats() {
        const text = `Debug Tool:
        Game Info:
        gamemode: ${player.gamemode}
        entities: ${entityManager.entities.length}

        Player Info:
        Is dead: ${player.isDead}
        level: ${player.level}
        tank: ${player.tank}
        position: ${Math.round(player.position.x)},${Math.round(player.position.y)}
        mouse: ${Math.round(player.mouse.x)},${Math.round(player.mouse.y)}
        velocity [units/seconds]: ${Math.round(Math.hypot(player.velocity.x, player.velocity.y))}

        Arena Info:
        size: ${Math.round(arena.size)}

        Camera Info:
        position: ${camera.position.x},${camera.position.y}
        scaling factor: ${scaling.scalingFactor}
        fov: ${scaling.fov}

        Minimap Info:
        minimapDim: ${minimap.minimapDim.x},${minimap.minimapDim.y}
        minimapPos: ${minimap.minimapPos.x},${minimap.minimapPos.y}
        viewportDim: ${minimap.viewportDim.x},${minimap.viewportDim.y}
        viewportPos: ${minimap.viewportPos.x},${minimap.viewportPos.y}
        arrowPos: ${minimap.arrowPos.x},${minimap.arrowPos.y}
        `;
        overlay.ctx.save();
        const fontSize = 20 * _window.devicePixelRatio;
        overlay.ctx.font = `${fontSize}px Ubuntu`;
        overlay.ctx.fillStyle = `#ffffff`;
        overlay.ctx.strokeStyle = '#000000';
        overlay.ctx.lineWidth = fontSize / 5;
        text.split('\n').forEach((x, i) => {
            overlay.ctx.strokeText(x, 0, _window.innerHeight * 0.25 + i * fontSize * 1.05);
            overlay.ctx.fillText(x, 0, _window.innerHeight * 0.25 + i * fontSize * 1.05);
        });
        overlay.ctx.restore();
    }
}
const debugTool = new DebugTool();

;// CONCATENATED MODULE: ./src/extensions/index.ts



;// CONCATENATED MODULE: ./src/tools/background_overlay.ts


class BackgroundOverlay {
    canvas;
    ctx;
    #gameCanvas;
    #gameContext;
    constructor() {
        this.canvas = CanvasKit.createCanvas();
        const ctx = this.canvas.getContext('2d');
        if (ctx == null) {
            throw new Error('diepAPI: Your browser does not support canvas.');
        }
        this.ctx = ctx;
        _window.addEventListener('resize', () => {
            this.#onResize();
        });
        game.on('frame_start', () => {
            this.#onFrameStart();
        });
        this.#onResize();
        game.once('ready', () => {
            this.#gameCanvas = document.getElementById('canvas');
            if (this.#gameCanvas == null) {
                throw new Error('diepAPI: Game canvas does not exist.');
            }
            this.#gameContext = this.#gameCanvas.getContext('2d');
            if (this.#gameContext == null) {
                throw new Error('diepAPI: Game canvas context does not exist.');
            }
            this.#hookBackground();
        });
    }
    #onResize() {
        this.canvas.width = _window.innerWidth * _window.devicePixelRatio;
        this.canvas.height = _window.innerHeight * _window.devicePixelRatio;
    }
    #onFrameStart() {
        this.canvas.width = _window.innerWidth * _window.devicePixelRatio;
        this.canvas.height = _window.innerHeight * _window.devicePixelRatio;
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.setTransform(1, 0, 0, 1, 0, 0);
    }
    #hookBackground() {
        CanvasKit.overrideCtx('fillRect', (target, thisArg, args) => {
            if (typeof thisArg.fillStyle !== 'object' || this.#gameContext == null) {
                Reflect.apply(target, thisArg, args);
                return;
            }
            Reflect.apply(target, thisArg, args);
            this.#gameContext.save();
            this.#gameContext.setTransform(1, 0, 0, 1, 0, 0);
            this.#gameContext.globalAlpha = 1;
            this.#gameContext.drawImage(this.canvas, 0, 0);
            this.#gameContext.restore();
        });
    }
}
const backgroundOverlay = new BackgroundOverlay();

;// CONCATENATED MODULE: ./src/tools/index.ts



;// CONCATENATED MODULE: ./src/types/index.ts


;// CONCATENATED MODULE: ./src/index.ts






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

  _window.diepAPI = diepAPI;
})();