您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
https://github.com/Cazka/diepAPI
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/433681/978437/diepAPI.js
// ==UserScript== // @name diepAPI // @description https://github.com/Cazka/diepAPI // @version 2.0.3 // @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() { super.emit('ready'); } } 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 screen 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 screen units */ toScreenUnits(v) { return Vector.scale(this.#scalingFactor, v); } /** * Will translate coordinates from canvas to arena * @param {Vector} screenPos The canvas coordinates * @returns {Vector} The `screenPos` translated to arena coordinates */ toArenaPos(screenPos) { const direction = Vector.subtract(screenPos, 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 */ toScreenPos(arenaPos) { const direction = Vector.subtract(arenaPos, camera.position); const scaled = this.toScreenUnits(direction); const screenPos = Vector.add(scaled, new Vector(window.innerWidth / 2, window.innerHeight / 2)); return screenPos; } } 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, 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 at given `time` * @param time performance.now() time */ predictPos(time) { const duration = (time - 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; #mouseScreenPos = new Vector(0, 0); #mousePos = new Vector(0, 0); #gamemode = window.localStorage.gamemode; 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.#mouseScreenPos); }); //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); }, }); }); } 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; } /** * Predict where this object will be at given `time` * @param time performance.now() time */ 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 = window.devicePixelRatio * arenaScaling.windowRatio * (x_index * 115 + 97.5); const y = window.devicePixelRatio * 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.toScreenPos(arenaPos); window.input.mouse(position.x, position.y); this.#onmousemove({ clientX: position.x, clientY: position.y }); } #onmousemove(e) { this.#mouseScreenPos = new Vector(e.clientX, e.clientY); if (gamepad.connected) { const arenaPos = arenaScaling.toArenaPos(this.#mouseScreenPos); 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(performance.now()), 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', (tarrget, 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; })();