// ==UserScript==
// @name ! Glotus Client [Moomoo.io]
// @author Murka
// @description An excellent Moomoo.io hack for a comfortable gaming experience
// @icon https://imagizer.imageshack.com/img924/3497/SedB2D.png
// @version 2.0.5
// @match *://moomoo.io/
// @match *://moomoo.io/?server*
// @match *://*.moomoo.io/
// @match *://*.moomoo.io/?server*
// @run-at document-start
// @grant GM_webRequest
// @license MIT
// @namespace https://greasyfork.org/users/919633
// @downloadURL https://update.greasyfork.icu/scripts/540039/%21%20Glotus%20Client%20%5BMoomooio%5D.user.js
// @updateURL https://update.greasyfork.icu/scripts/540039/%21%20Glotus%20Client%20%5BMoomooio%5D.meta.js
// ==/UserScript==
/* jshint esversion:6 */
/*
Author: Murka
Github: https://github.com/Murka007/Glotus-client
Greasyfork: https://greasyfork.org/users/919633
Discord: https://discord.gg/cPRFdcZkeD
Feel free to use and distribute it, but don't forget about special credits.
*/
GM_webRequest([
{ selector: { include: ["*cookie*", "*cloudflare*", "*ads*", "*jquery*", "*howler*", "*frvr-channel-web*", "*securepubads*"] }, action: "cancel" },
]);
Function("(" + (() => {
"use strict";
var __webpack_exports__, code, Navbar_code, Keybinds_code, Combat_code, Visuals_code, Misc_code, Devtool_code, Bots_code, Credits_code, __webpack_require__ = {};
(() => {
__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_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
})();
__webpack_exports__ = {};
__webpack_require__.d(__webpack_exports__, {
ZI: () => connection,
mq: () => myClient
});
const Config_Config = {
maxScreenWidth: 1920,
maxScreenHeight: 1080,
serverUpdateRate: 9,
collisionDepth: 6,
minimapRate: 3e3,
colGrid: 10,
clientSendRate: 5,
barWidth: 50,
barHeight: 17,
barPad: 4.5,
iconPadding: 15,
iconPad: .9,
deathFadeout: 3e3,
crownIconScale: 60,
crownPad: 35,
chatCountdown: 3e3,
chatCooldown: 500,
maxAge: 100,
gatherAngle: Math.PI / 2.6,
gatherWiggle: 10,
hitReturnRatio: .25,
hitAngle: Math.PI / 2,
playerScale: 35,
playerSpeed: .0016,
playerDecel: .993,
nameY: 34,
animalCount: 7,
aiTurnRandom: .06,
shieldAngle: Math.PI / 3,
resourceTypes: [ "wood", "food", "stone", "points" ],
areaCount: 7,
treesPerArea: 9,
bushesPerArea: 3,
totalRocks: 32,
goldOres: 7,
riverWidth: 724,
riverPadding: 114,
waterCurrent: .0011,
waveSpeed: 1e-4,
waveMax: 1.3,
treeScales: [ 150, 160, 165, 175 ],
bushScales: [ 80, 85, 95 ],
rockScales: [ 80, 85, 90 ],
snowBiomeTop: 2400,
snowSpeed: .75,
maxNameLength: 15,
mapScale: 14400,
mapPingScale: 40,
mapPingTime: 2200,
skinColors: [ "#bf8f54", "#cbb091", "#896c4b", "#fadadc", "#ececec", "#c37373", "#4c4c4c", "#ecaff7", "#738cc3", "#8bc373", "#91B2DB" ]
};
const constants_Config = Config_Config;
const Weapons = [ {
id: 0,
itemType: 0,
upgradeType: 0,
type: 0,
age: 0,
name: "tool hammer",
description: "tool for gathering all resources",
src: "hammer_1",
length: 140,
width: 140,
xOffset: -3,
yOffset: 18,
damage: 25,
range: 65,
gather: 1,
speed: 300
}, {
id: 1,
itemType: 0,
upgradeType: 1,
type: 0,
age: 2,
name: "hand axe",
description: "gathers resources at a higher rate",
src: "axe_1",
length: 140,
width: 140,
xOffset: 3,
yOffset: 24,
damage: 30,
spdMult: 1,
range: 70,
gather: 2,
speed: 400
}, {
id: 2,
itemType: 0,
upgradeOf: 1,
upgradeType: 1,
type: 0,
age: 8,
pre: 1,
name: "great axe",
description: "deal more damage and gather more resources",
src: "great_axe_1",
length: 140,
width: 140,
xOffset: -8,
yOffset: 25,
damage: 35,
spdMult: 1,
range: 75,
gather: 4,
speed: 400
}, {
id: 3,
itemType: 0,
upgradeType: 2,
type: 0,
age: 2,
name: "short sword",
description: "increased attack power but slower move speed",
src: "sword_1",
iPad: 1.3,
length: 130,
width: 210,
xOffset: -8,
yOffset: 46,
damage: 35,
spdMult: .85,
range: 110,
gather: 1,
speed: 300
}, {
id: 4,
itemType: 0,
upgradeOf: 3,
upgradeType: 2,
type: 0,
age: 8,
pre: 3,
name: "katana",
description: "greater range and damage",
src: "samurai_1",
iPad: 1.3,
length: 130,
width: 210,
xOffset: -8,
yOffset: 59,
damage: 40,
spdMult: .8,
range: 118,
gather: 1,
speed: 300
}, {
id: 5,
itemType: 0,
upgradeType: 3,
isUpgrade: false,
type: 0,
age: 2,
name: "polearm",
description: "long range melee weapon",
src: "spear_1",
iPad: 1.3,
length: 130,
width: 210,
xOffset: -8,
yOffset: 53,
damage: 45,
knock: .2,
spdMult: .82,
range: 142,
gather: 1,
speed: 700
}, {
id: 6,
itemType: 0,
upgradeType: 4,
isUpgrade: false,
type: 0,
age: 2,
name: "bat",
description: "fast long range melee weapon",
src: "bat_1",
iPad: 1.3,
length: 110,
width: 180,
xOffset: -8,
yOffset: 53,
damage: 20,
knock: .7,
range: 110,
gather: 1,
speed: 300
}, {
id: 7,
itemType: 0,
upgradeType: 5,
isUpgrade: false,
type: 0,
age: 2,
name: "daggers",
description: "really fast short range weapon",
src: "dagger_1",
iPad: .8,
length: 110,
width: 110,
xOffset: 18,
yOffset: 0,
damage: 20,
knock: .1,
range: 65,
gather: 1,
hitSlow: .1,
spdMult: 1.13,
speed: 100
}, {
id: 8,
itemType: 0,
upgradeType: 6,
isUpgrade: false,
type: 0,
age: 2,
name: "stick",
description: "great for gathering but very weak",
src: "stick_1",
length: 140,
width: 140,
xOffset: 3,
yOffset: 24,
damage: 1,
spdMult: 1,
range: 70,
gather: 7,
speed: 400
}, {
id: 9,
itemType: 1,
upgradeType: 7,
projectile: 0,
type: 1,
age: 6,
name: "hunting bow",
description: "bow used for ranged combat and hunting",
src: "bow_1",
cost: {
food: 0,
wood: 4,
stone: 0,
gold: 0
},
length: 120,
width: 120,
xOffset: -6,
yOffset: 0,
spdMult: .75,
speed: 600,
range: 2200
}, {
id: 10,
itemType: 1,
upgradeType: 8,
isUpgrade: false,
type: 1,
age: 6,
name: "great hammer",
description: "hammer used for destroying structures",
src: "great_hammer_1",
length: 140,
width: 140,
xOffset: -9,
yOffset: 25,
damage: 10,
spdMult: .88,
range: 75,
sDmg: 7.5,
gather: 1,
speed: 400
}, {
id: 11,
itemType: 1,
upgradeType: 9,
isUpgrade: false,
type: 1,
age: 6,
name: "wooden shield",
description: "blocks projectiles and reduces melee damage",
src: "shield_1",
length: 120,
width: 120,
shield: .2,
xOffset: 6,
yOffset: 0,
spdMult: .7,
speed: 1
}, {
id: 12,
itemType: 1,
upgradeType: 7,
projectile: 2,
upgradeOf: 9,
type: 1,
age: 8,
pre: 9,
name: "crossbow",
description: "deals more damage and has greater range",
src: "crossbow_1",
cost: {
food: 0,
wood: 5,
stone: 0,
gold: 0
},
aboveHand: true,
armS: .75,
length: 120,
width: 120,
xOffset: -4,
yOffset: 0,
spdMult: .7,
speed: 700,
range: 2200
}, {
id: 13,
itemType: 1,
upgradeType: 7,
projectile: 3,
upgradeOf: 12,
type: 1,
age: 9,
pre: 12,
name: "repeater crossbow",
description: "high firerate crossbow with reduced damage",
src: "crossbow_2",
cost: {
food: 0,
wood: 10,
stone: 0,
gold: 0
},
aboveHand: true,
armS: .75,
length: 120,
width: 120,
xOffset: -4,
yOffset: 0,
spdMult: .7,
speed: 230,
range: 2200
}, {
id: 14,
itemType: 1,
upgradeType: 10,
isUpgrade: false,
type: 1,
age: 6,
name: "mc grabby",
description: "steals resources from enemies",
src: "grab_1",
length: 130,
width: 210,
xOffset: -8,
yOffset: 53,
damage: 0,
steal: 250,
knock: .2,
spdMult: 1.05,
range: 125,
gather: 0,
speed: 700
}, {
id: 15,
itemType: 1,
upgradeType: 7,
projectile: 5,
upgradeOf: 12,
type: 1,
age: 9,
pre: 12,
name: "musket",
description: "slow firerate but high damage and range",
src: "musket_1",
cost: {
food: 0,
wood: 0,
stone: 10,
gold: 0
},
aboveHand: true,
rec: .35,
armS: .6,
hndS: .3,
hndD: 1.6,
length: 205,
width: 205,
xOffset: 25,
yOffset: 0,
hideProjectile: true,
spdMult: .6,
speed: 1500,
range: 2200
} ];
const ItemGroups = {
[1]: {
name: "Wall",
limit: 30,
layer: 0
},
[2]: {
name: "Spike",
limit: 15,
layer: 0
},
[3]: {
name: "Windmill",
limit: 7,
sandboxLimit: 299,
layer: 1
},
[4]: {
name: "Mine",
limit: 1,
layer: 0
},
[5]: {
name: "Trap",
limit: 6,
layer: -1
},
[6]: {
name: "Boost",
limit: 12,
sandboxLimit: 299,
layer: -1
},
[7]: {
name: "Turret",
limit: 2,
layer: 1
},
[8]: {
name: "Plaftorm",
limit: 12,
layer: -1
},
[9]: {
name: "Healing pad",
limit: 4,
layer: -1
},
[10]: {
name: "Spawn",
limit: 1,
layer: -1
},
[11]: {
name: "Sapling",
limit: 2,
layer: 0
},
[12]: {
name: "Blocker",
limit: 3,
layer: -1
},
[13]: {
name: "Teleporter",
limit: 2,
sandboxLimit: 299,
layer: -1
}
};
const Items = [ {
id: 0,
itemType: 2,
name: "apple",
description: "restores 20 health when consumed",
age: 0,
cost: {
food: 10,
wood: 0,
stone: 0,
gold: 0
},
restore: 20,
scale: 22,
holdOffset: 15
}, {
id: 1,
itemType: 2,
upgradeOf: 0,
name: "cookie",
description: "restores 40 health when consumed",
age: 3,
cost: {
food: 15,
wood: 0,
stone: 0,
gold: 0
},
restore: 40,
scale: 27,
holdOffset: 15
}, {
id: 2,
itemType: 2,
upgradeOf: 1,
name: "cheese",
description: "restores 30 health and another 50 over 5 seconds",
age: 7,
cost: {
food: 25,
wood: 0,
stone: 0,
gold: 0
},
restore: 30,
scale: 27,
holdOffset: 15
}, {
id: 3,
itemType: 3,
itemGroup: 1,
name: "wood wall",
description: "provides protection for your village",
age: 0,
cost: {
food: 0,
wood: 10,
stone: 0,
gold: 0
},
projDmg: true,
health: 380,
scale: 50,
holdOffset: 20,
placeOffset: -5
}, {
id: 4,
itemType: 3,
itemGroup: 1,
upgradeOf: 3,
name: "stone wall",
description: "provides improved protection for your village",
age: 3,
cost: {
food: 0,
wood: 0,
stone: 25,
gold: 0
},
health: 900,
scale: 50,
holdOffset: 20,
placeOffset: -5
}, {
pre: 1,
id: 5,
itemType: 3,
itemGroup: 1,
upgradeOf: 4,
name: "castle wall",
description: "provides powerful protection for your village",
age: 7,
cost: {
food: 0,
wood: 0,
stone: 35,
gold: 0
},
health: 1500,
scale: 52,
holdOffset: 20,
placeOffset: -5
}, {
id: 6,
itemType: 4,
itemGroup: 2,
name: "spikes",
description: "damages enemies when they touch them",
age: 0,
cost: {
food: 0,
wood: 20,
stone: 5,
gold: 0
},
health: 400,
damage: 20,
scale: 49,
spritePadding: -23,
holdOffset: 8,
placeOffset: -5
}, {
id: 7,
itemType: 4,
itemGroup: 2,
upgradeOf: 6,
name: "greater spikes",
description: "damages enemies when they touch them",
age: 5,
cost: {
food: 0,
wood: 30,
stone: 10,
gold: 0
},
health: 500,
damage: 35,
scale: 52,
spritePadding: -23,
holdOffset: 8,
placeOffset: -5
}, {
id: 8,
itemType: 4,
itemGroup: 2,
upgradeOf: 7,
name: "poison spikes",
description: "poisons enemies when they touch them",
age: 9,
pre: 1,
cost: {
food: 0,
wood: 35,
stone: 15,
gold: 0
},
health: 600,
damage: 30,
poisonDamage: 5,
scale: 52,
spritePadding: -23,
holdOffset: 8,
placeOffset: -5
}, {
id: 9,
itemType: 4,
itemGroup: 2,
upgradeOf: 7,
name: "spinning spikes",
description: "damages enemies when they touch them",
age: 9,
pre: 2,
cost: {
food: 0,
wood: 30,
stone: 20,
gold: 0
},
health: 500,
damage: 45,
turnSpeed: .003,
scale: 52,
spritePadding: -23,
holdOffset: 8,
placeOffset: -5
}, {
id: 10,
itemType: 5,
itemGroup: 3,
name: "windmill",
description: "generates gold over time",
age: 0,
cost: {
food: 0,
wood: 50,
stone: 10,
gold: 0
},
health: 400,
pps: 1,
turnSpeed: .0016,
spritePadding: 25,
iconLineMult: 12,
scale: 45,
holdOffset: 20,
placeOffset: 5
}, {
id: 11,
itemType: 5,
itemGroup: 3,
upgradeOf: 10,
name: "faster windmill",
description: "generates more gold over time",
age: 5,
pre: 1,
cost: {
food: 0,
wood: 60,
stone: 20,
gold: 0
},
health: 500,
pps: 1.5,
turnSpeed: .0025,
spritePadding: 25,
iconLineMult: 12,
scale: 47,
holdOffset: 20,
placeOffset: 5
}, {
id: 12,
itemType: 5,
itemGroup: 3,
upgradeOf: 11,
name: "power mill",
description: "generates more gold over time",
age: 8,
pre: 1,
cost: {
food: 0,
wood: 100,
stone: 50,
gold: 0
},
health: 800,
pps: 2,
turnSpeed: .005,
spritePadding: 25,
iconLineMult: 12,
scale: 47,
holdOffset: 20,
placeOffset: 5
}, {
id: 13,
itemType: 6,
itemGroup: 4,
name: "mine",
description: "allows you to mine stone",
age: 5,
type: 2,
cost: {
food: 0,
wood: 20,
stone: 100,
gold: 0
},
iconLineMult: 12,
scale: 65,
holdOffset: 20,
placeOffset: 0
}, {
id: 14,
itemType: 6,
itemGroup: 11,
name: "sapling",
description: "allows you to farm wood",
age: 5,
type: 0,
cost: {
food: 0,
wood: 150,
stone: 0,
gold: 0
},
iconLineMult: 12,
colDiv: .5,
scale: 110,
holdOffset: 50,
placeOffset: -15
}, {
id: 15,
itemType: 7,
itemGroup: 5,
name: "pit trap",
description: "pit that traps enemies if they walk over it",
age: 4,
cost: {
food: 0,
wood: 30,
stone: 30,
gold: 0
},
trap: true,
ignoreCollision: true,
hideFromEnemy: true,
health: 500,
colDiv: .2,
scale: 50,
holdOffset: 20,
placeOffset: -5
}, {
id: 16,
itemType: 7,
itemGroup: 6,
name: "boost pad",
description: "provides boost when stepped on",
age: 4,
cost: {
food: 0,
wood: 5,
stone: 20,
gold: 0
},
ignoreCollision: true,
boostSpeed: 1.5,
health: 150,
colDiv: .7,
scale: 45,
holdOffset: 20,
placeOffset: -5
}, {
id: 17,
itemType: 8,
itemGroup: 7,
name: "turret",
description: "defensive structure that shoots at enemies",
age: 7,
doUpdate: true,
cost: {
food: 0,
wood: 200,
stone: 150,
gold: 0
},
health: 800,
projectile: 1,
shootRange: 700,
shootRate: 2200,
scale: 43,
holdOffset: 20,
placeOffset: -5
}, {
id: 18,
itemType: 8,
itemGroup: 8,
name: "platform",
description: "platform to shoot over walls and cross over water",
age: 7,
cost: {
food: 0,
wood: 20,
stone: 0,
gold: 0
},
ignoreCollision: true,
zIndex: 1,
health: 300,
scale: 43,
holdOffset: 20,
placeOffset: -5
}, {
id: 19,
itemType: 8,
itemGroup: 9,
name: "healing pad",
description: "standing on it will slowly heal you",
age: 7,
cost: {
food: 10,
wood: 30,
stone: 0,
gold: 0
},
ignoreCollision: true,
healCol: 15,
health: 400,
colDiv: .7,
scale: 45,
holdOffset: 20,
placeOffset: -5
}, {
id: 20,
itemType: 9,
itemGroup: 10,
name: "spawn pad",
description: "you will spawn here when you die but it will dissapear",
age: 9,
cost: {
food: 0,
wood: 100,
stone: 100,
gold: 0
},
health: 400,
ignoreCollision: true,
spawnPoint: true,
scale: 45,
holdOffset: 20,
placeOffset: -5
}, {
id: 21,
itemType: 8,
itemGroup: 12,
name: "blocker",
description: "blocks building in radius",
age: 7,
cost: {
food: 0,
wood: 30,
stone: 25,
gold: 0
},
ignoreCollision: true,
blocker: 300,
health: 400,
colDiv: .7,
scale: 45,
holdOffset: 20,
placeOffset: -5
}, {
id: 22,
itemType: 8,
itemGroup: 13,
name: "teleporter",
description: "teleports you to a random point on the map",
age: 7,
cost: {
food: 0,
wood: 60,
stone: 60,
gold: 0
},
ignoreCollision: true,
teleport: true,
health: 200,
colDiv: .7,
scale: 45,
holdOffset: 20,
placeOffset: -5
} ];
const WeaponVariants = [ {
id: 0,
src: "",
xp: 1,
needXP: 0,
val: 1,
color: "#7e7e90"
}, {
id: 1,
src: "_g",
xp: 3e3,
needXP: 3e3,
val: 1.1,
color: "#f7cf45"
}, {
id: 2,
src: "_d",
xp: 7e3,
needXP: 4e3,
val: 1.18,
color: "#6d91cb"
}, {
id: 3,
src: "_r",
poison: true,
xp: 12e3,
needXP: 5e3,
val: 1.18,
color: "#be5454"
} ];
const Projectiles = [ {
id: 0,
name: "Hunting bow",
index: 0,
layer: 0,
src: "arrow_1",
damage: 25,
scale: 103,
range: 1e3,
speed: 1.6
}, {
id: 1,
name: "Turret",
index: 1,
layer: 1,
damage: 25,
scale: 20,
speed: 1.5,
range: 700
}, {
id: 2,
name: "Crossbow",
index: 0,
layer: 0,
src: "arrow_1",
damage: 35,
scale: 103,
range: 1200,
speed: 2.5
}, {
id: 3,
name: "Repeater crossbow",
index: 0,
layer: 0,
src: "arrow_1",
damage: 30,
scale: 103,
range: 1200,
speed: 2
}, {
id: 4,
index: 1,
layer: 1,
damage: 16,
scale: 20,
range: 0,
speed: 0
}, {
id: 5,
name: "Musket",
index: 0,
layer: 0,
src: "bullet_1",
damage: 50,
scale: 160,
range: 1400,
speed: 3.6
} ];
class Vector_Vector {
x;
y;
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
static fromAngle(angle, length = 1) {
return new Vector_Vector(Math.cos(angle) * length, Math.sin(angle) * length);
}
add(vec) {
if (vec instanceof Vector_Vector) {
this.x += vec.x;
this.y += vec.y;
} else {
this.x += vec;
this.y += vec;
}
return this;
}
sub(vec) {
if (vec instanceof Vector_Vector) {
this.x -= vec.x;
this.y -= vec.y;
} else {
this.x -= vec;
this.y -= vec;
}
return this;
}
mult(scalar) {
this.x *= scalar;
this.y *= scalar;
return this;
}
div(scalar) {
this.x /= scalar;
this.y /= scalar;
return this;
}
get length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
normalize() {
return this.length > 0 ? this.div(this.length) : this;
}
dot(vec) {
return this.x * vec.x + this.y * vec.y;
}
proj(vec) {
const k = this.dot(vec) / vec.dot(vec);
return vec.copy().mult(k);
}
setXY(x, y) {
this.x = x;
this.y = y;
return this;
}
setVec(vec) {
return this.setXY(vec.x, vec.y);
}
setLength(value) {
return this.normalize().mult(value);
}
copy() {
return new Vector_Vector(this.x, this.y);
}
distance(vec) {
return this.copy().sub(vec).length;
}
angle(vec) {
const copy = vec.copy().sub(this);
return Math.atan2(copy.y, copy.x);
}
direction(angle, length) {
return this.copy().add(Vector_Vector.fromAngle(angle, length));
}
isEqual(vec) {
return this.x === vec.x && this.y === vec.y;
}
stringify() {
return this.x + ":" + this.y;
}
}
const modules_Vector = Vector_Vector;
const getAngle = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1);
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const getAngleDist = (a, b) => {
const p = Math.abs(b - a) % (2 * Math.PI);
return p > Math.PI ? 2 * Math.PI - p : p;
};
const removeFast = (array, index) => {
if (index < 0 || index >= array.length) {
throw new RangeError("removeFast: Index out of range");
}
if (index === array.length - 1) {
array.pop();
} else {
array[index] = array.pop();
}
};
let uniqueID = 0;
const getUniqueID = () => uniqueID++;
const isActiveInput = () => {
const active = document.activeElement || document.body;
return active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement;
};
const getAngleFromBitmask = (bitmask, rotate) => {
const vec = {
x: 0,
y: 0
};
if (1 & bitmask) {
vec.y--;
}
if (2 & bitmask) {
vec.y++;
}
if (4 & bitmask) {
vec.x--;
}
if (8 & bitmask) {
vec.x++;
}
if (rotate) {
vec.x *= -1;
vec.y *= -1;
}
return 0 === vec.x && 0 === vec.y ? null : Math.atan2(vec.y, vec.x);
};
const formatCode = code => {
code += "";
if ("Backspace" === code) {
return code;
}
if ("Escape" === code) {
return "ESC";
}
if ("Delete" === code) {
return "DEL";
}
if ("Minus" === code) {
return "-";
}
if ("Equal" === code) {
return "=";
}
if ("BracketLeft" === code) {
return "[";
}
if ("BracketRight" === code) {
return "]";
}
if ("Slash" === code) {
return "/";
}
if ("Backslash" === code) {
return "\\";
}
if ("Quote" === code) {
return "'";
}
if ("Backquote" === code) {
return "`";
}
if ("Semicolon" === code) {
return ";";
}
if ("Comma" === code) {
return ",";
}
if ("Period" === code) {
return ".";
}
if ("CapsLock" === code) {
return "CAPS";
}
if ("ContextMenu" === code) {
return "CTXMENU";
}
if ("NumLock" === code) {
return "LOCK";
}
return code.replace(/^Page/, "PG").replace(/^Digit/, "").replace(/^Key/, "").replace(/^(Shift|Control|Alt)(L|R).*$/, "$2$1").replace(/Control/, "CTRL").replace(/^Arrow/, "").replace(/^Numpad/, "NUM").replace(/Decimal/, "DEC").replace(/Subtract/, "SUB").replace(/Divide/, "DIV").replace(/Multiply/, "MULT").toUpperCase();
};
const formatButton = button => {
if (0 === button) {
return "LBTN";
}
if (1 === button) {
return "MBTN";
}
if (2 === button) {
return "RBTN";
}
if (3 === button) {
return "BBTN";
}
if (4 === button) {
return "FBTN";
}
throw new Error(`formatButton Error: "${button}" is not valid button`);
};
const removeClass = (target, name) => {
if (target instanceof HTMLElement) {
target.classList.remove(name);
return;
}
for (const element of target) {
element.classList.remove(name);
}
};
const pointInRiver = position => {
const y = position.y;
const below = y >= constants_Config.mapScale / 2 - constants_Config.riverWidth / 2;
const above = y <= constants_Config.mapScale / 2 + constants_Config.riverWidth / 2;
return below && above;
};
const pointInDesert = position => position.y >= constants_Config.mapScale - constants_Config.snowBiomeTop;
const inView = (x, y, radius) => {
const maxScreenWidth = Math.min(1920, modules_ZoomHandler.scale.current.w);
const maxScreenHeight = Math.min(1080, modules_ZoomHandler.scale.current.h);
const visibleHorizontally = x + radius > 0 && x - radius < maxScreenWidth;
const visibleVertically = y + radius > 0 && y - radius < maxScreenHeight;
return visibleHorizontally && visibleVertically;
};
const findPlacementAngles = angles => {
const output = new Set;
for (let i = 0; i < angles.length; i++) {
const {angle, offset} = angles[i];
const start = angle - offset;
const end = angle + offset;
let startIntersects = false;
let endIntersects = false;
for (let j = 0; j < angles.length; j++) {
if (startIntersects && endIntersects) {
break;
}
if (i !== j) {
const {angle, offset} = angles[j];
if (getAngleDist(start, angle) <= offset) {
startIntersects = true;
}
if (getAngleDist(end, angle) <= offset) {
endIntersects = true;
}
}
}
if (!startIntersects) {
output.add(start);
}
if (!endIntersects) {
output.add(end);
}
}
return output;
};
const cursorPosition = () => {
const {ModuleHandler, myPlayer} = myClient;
const {w, h} = modules_ZoomHandler.scale.current;
const scale = Math.max(innerWidth / w, innerHeight / h);
const cursorX = (ModuleHandler.mouse.lockX - innerWidth / 2) / scale;
const cursorY = (ModuleHandler.mouse.lockY - innerHeight / 2) / scale;
const pos = myPlayer.position.current;
return new modules_Vector(pos.x + cursorX, pos.y + cursorY);
};
const Hooker = new class Hooker {
createRecursiveHook(target, prop, callback) {
(function recursiveHook() {
Object.defineProperty(target, prop, {
set(value) {
delete target[prop];
this[prop] = value;
if (callback(this, value)) {
return;
}
recursiveHook();
},
configurable: true
});
})();
}
createHook(target, prop, callback) {
const symbol = Symbol(prop);
Object.defineProperty(target, prop, {
get() {
return this[symbol];
},
set(value) {
callback(this, value, symbol);
},
configurable: true
});
}
linker(value) {
const hook = [ value ];
hook.valueOf = () => hook[0];
return hook;
}
};
const utility_Hooker = Hooker;
const resizeEvent = new Event("resize");
const ZoomHandler = new class ZoomHandler {
scale={
Default: {
w: 1920,
h: 1080
},
current: {
w: 1920,
h: 1080
},
smooth: {
w: utility_Hooker.linker(1920),
h: utility_Hooker.linker(1080)
}
};
wheels=3;
scaleFactor=250;
getScale() {
const dpr = 1;
return Math.max(innerWidth / this.scale.Default.w, innerHeight / this.scale.Default.h) * dpr;
}
getMinScale(scale) {
const {w, h} = this.scale.Default;
const min = Math.min(w, h);
const count = Math.floor(min / scale);
return {
w: w - scale * count,
h: h - scale * count
};
}
handler(event) {
if (!(event.target instanceof HTMLCanvasElement) || event.ctrlKey || event.shiftKey || event.altKey || isActiveInput()) {
return;
}
const {Default, current, smooth} = this.scale;
if (Default.w === current.w && Default.h === current.h && 0 !== (this.wheels = (this.wheels + 1) % 4)) {
return;
}
const {w, h} = this.getMinScale(this.scaleFactor);
const zoom = Math.sign(event.deltaY) * -this.scaleFactor;
current.w = Math.max(w, current.w + zoom);
current.h = Math.max(h, current.h + zoom);
smooth.w[0] = current.w;
smooth.h[0] = current.h;
window.dispatchEvent(resizeEvent);
}
};
const modules_ZoomHandler = ZoomHandler;
class Storage {
static get(key) {
const value = localStorage.getItem(key);
return null === value ? null : JSON.parse(value);
}
static set(key, value, stringify = true) {
const data = stringify ? JSON.stringify(value) : value;
localStorage.setItem(key, data);
}
static delete(key) {
const has = localStorage.hasOwnProperty(key) && key in localStorage;
localStorage.removeItem(key);
return has;
}
}
class Cookie {
static get(key) {
const cookies = document.cookie.split(";");
for (const cookie of cookies) {
const match = cookie.trim().match(/^(.+?)=(.+?)$/);
if (null !== match && match[1] === key) {
try {
return JSON.parse(decodeURIComponent(match[2]));
} catch (err) {}
}
}
return null;
}
static set(name, value, days) {
const date = new Date;
date.setTime(date.getTime() + 24 * days * 60 * 60 * 1e3);
const expires = "; expires=" + date.toUTCString();
const domain = "; domain=.moomoo.io";
const path = "; path=/";
const cookieString = `${name}=${encodeURIComponent(value)}${expires}${domain}${path}`;
document.cookie = cookieString;
}
}
const defaultSettings = {
primary: "Digit1",
secondary: "Digit2",
food: "KeyQ",
wall: "Digit4",
spike: "KeyC",
windmill: "KeyR",
farm: "KeyT",
trap: "Space",
turret: "KeyF",
spawn: "KeyG",
up: "KeyW",
left: "KeyA",
down: "KeyS",
right: "KeyD",
autoattack: "KeyE",
lockrotation: "KeyX",
lockBotPosition: "KeyZ",
toggleChat: "Enter",
toggleShop: "ShiftLeft",
toggleClan: "ControlLeft",
toggleMenu: "Escape",
biomehats: true,
autoemp: true,
antienemy: true,
antianimal: true,
antispike: true,
autoheal: true,
healingSpeed: 25,
automill: true,
autoplacer: true,
autobreak: true,
enemyTracers: false,
enemyTracersColor: "#cc5151",
teammateTracers: false,
teammateTracersColor: "#8ecc51",
animalTracers: false,
animalTracersColor: "#518ccc",
notificationTracers: true,
notificationTracersColor: "#f5d951",
arrows: true,
itemMarkers: true,
itemMarkersColor: "#84bd4b",
teammateMarkers: true,
teammateMarkersColor: "#bdb14b",
enemyMarkers: true,
enemyMarkersColor: "#ba4949",
weaponXPBar: true,
playerTurretReloadBar: true,
playerTurretReloadBarColor: "#cf7148",
weaponReloadBar: true,
weaponReloadBarColor: "#5155cc",
renderHP: true,
objectTurretReloadBar: false,
objectTurretReloadBarColor: "#66d9af",
itemHealthBar: false,
itemHealthBarColor: "#6b449e",
itemCounter: true,
renderGrid: false,
windmillRotation: false,
entityDanger: true,
displayPlayerAngle: false,
projectileHitbox: false,
possibleShootTarget: false,
weaponHitbox: false,
collisionHitbox: false,
placementHitbox: false,
turretHitbox: false,
possiblePlacement: true,
autospawn: false,
autoaccept: false,
menuTransparency: false,
storeItems: [ [ 15, 31, 6, 7, 22, 12, 26, 11, 53, 20, 40, 56 ], [ 11, 17, 16, 13, 19, 18, 21 ] ]
};
defaultSettings.storeItems;
const settings = {
...defaultSettings,
...Cookie.get("Glotus")
};
for (const iterator in settings) {
const key = iterator;
if (!defaultSettings.hasOwnProperty(key)) {
delete settings[key];
}
}
const SaveSettings = () => {
Cookie.set("Glotus", JSON.stringify(settings), 365);
};
SaveSettings();
const Settings = settings;
const Hats = {
[0]: {
index: 0,
id: 0,
name: "Unequip",
dontSell: true,
price: 0,
scale: 0,
description: "None"
},
[45]: {
index: 1,
id: 45,
name: "Shame!",
dontSell: true,
price: 0,
scale: 120,
description: "hacks are for losers"
},
[51]: {
index: 2,
id: 51,
name: "Moo Cap",
price: 0,
scale: 120,
description: "coolest mooer around"
},
[50]: {
index: 3,
id: 50,
name: "Apple Cap",
price: 0,
scale: 120,
description: "apple farms remembers"
},
[28]: {
index: 4,
id: 28,
name: "Moo Head",
price: 0,
scale: 120,
description: "no effect"
},
[29]: {
index: 5,
id: 29,
name: "Pig Head",
price: 0,
scale: 120,
description: "no effect"
},
[30]: {
index: 6,
id: 30,
name: "Fluff Head",
price: 0,
scale: 120,
description: "no effect"
},
[36]: {
index: 7,
id: 36,
name: "Pandou Head",
price: 0,
scale: 120,
description: "no effect"
},
[37]: {
index: 8,
id: 37,
name: "Bear Head",
price: 0,
scale: 120,
description: "no effect"
},
[38]: {
index: 9,
id: 38,
name: "Monkey Head",
price: 0,
scale: 120,
description: "no effect"
},
[44]: {
index: 10,
id: 44,
name: "Polar Head",
price: 0,
scale: 120,
description: "no effect"
},
[35]: {
index: 11,
id: 35,
name: "Fez Hat",
price: 0,
scale: 120,
description: "no effect"
},
[42]: {
index: 12,
id: 42,
name: "Enigma Hat",
price: 0,
scale: 120,
description: "join the enigma army"
},
[43]: {
index: 13,
id: 43,
name: "Blitz Hat",
price: 0,
scale: 120,
description: "hey everybody i'm blitz"
},
[49]: {
index: 14,
id: 49,
name: "Bob XIII Hat",
price: 0,
scale: 120,
description: "like and subscribe"
},
[57]: {
index: 15,
id: 57,
name: "Pumpkin",
price: 50,
scale: 120,
description: "Spooooky"
},
[8]: {
index: 16,
id: 8,
name: "Bummle Hat",
price: 100,
scale: 120,
description: "no effect"
},
[2]: {
index: 17,
id: 2,
name: "Straw Hat",
price: 500,
scale: 120,
description: "no effect"
},
[15]: {
index: 18,
id: 15,
name: "Winter Cap",
price: 600,
scale: 120,
description: "allows you to move at normal speed in snow",
coldM: 1
},
[5]: {
index: 19,
id: 5,
name: "Cowboy Hat",
price: 1e3,
scale: 120,
description: "no effect"
},
[4]: {
index: 20,
id: 4,
name: "Ranger Hat",
price: 2e3,
scale: 120,
description: "no effect"
},
[18]: {
index: 21,
id: 18,
name: "Explorer Hat",
price: 2e3,
scale: 120,
description: "no effect"
},
[31]: {
index: 22,
id: 31,
name: "Flipper Hat",
price: 2500,
scale: 120,
description: "have more control while in water",
watrImm: true
},
[1]: {
index: 23,
id: 1,
name: "Marksman Cap",
price: 3e3,
scale: 120,
description: "increases arrow speed and range",
aMlt: 1.3
},
[10]: {
index: 24,
id: 10,
name: "Bush Gear",
price: 3e3,
scale: 160,
description: "allows you to disguise yourself as a bush"
},
[48]: {
index: 25,
id: 48,
name: "Halo",
price: 3e3,
scale: 120,
description: "no effect"
},
[6]: {
index: 26,
id: 6,
name: "Soldier Helmet",
price: 4e3,
scale: 120,
description: "reduces damage taken but slows movement",
spdMult: .94,
dmgMult: .75
},
[23]: {
index: 27,
id: 23,
name: "Anti Venom Gear",
price: 4e3,
scale: 120,
description: "makes you immune to poison",
poisonRes: 1
},
[13]: {
index: 28,
id: 13,
name: "Medic Gear",
price: 5e3,
scale: 110,
description: "slowly regenerates health over time",
healthRegen: 3
},
[9]: {
index: 29,
id: 9,
name: "Miners Helmet",
price: 5e3,
scale: 120,
description: "earn 1 extra gold per resource",
extraGold: 1
},
[32]: {
index: 30,
id: 32,
name: "Musketeer Hat",
price: 5e3,
scale: 120,
description: "reduces cost of projectiles",
projCost: .5
},
[7]: {
index: 31,
id: 7,
name: "Bull Helmet",
price: 6e3,
scale: 120,
description: "increases damage done but drains health",
healthRegen: -5,
dmgMultO: 1.5,
spdMult: .96
},
[22]: {
index: 32,
id: 22,
name: "Emp Helmet",
price: 6e3,
scale: 120,
description: "turrets won't attack but you move slower",
antiTurret: 1,
spdMult: .7
},
[12]: {
index: 33,
id: 12,
name: "Booster Hat",
price: 6e3,
scale: 120,
description: "increases your movement speed",
spdMult: 1.16
},
[26]: {
index: 34,
id: 26,
name: "Barbarian Armor",
price: 8e3,
scale: 120,
description: "knocks back enemies that attack you",
dmgK: .6
},
[21]: {
index: 35,
id: 21,
name: "Plague Mask",
price: 1e4,
scale: 120,
description: "melee attacks deal poison damage",
poisonDmg: 5,
poisonTime: 6
},
[46]: {
index: 36,
id: 46,
name: "Bull Mask",
price: 1e4,
scale: 120,
description: "bulls won't target you unless you attack them",
bullRepel: 1
},
[14]: {
index: 37,
id: 14,
name: "Windmill Hat",
topSprite: true,
price: 1e4,
scale: 120,
description: "generates points while worn",
pps: 1.5
},
[11]: {
index: 38,
id: 11,
name: "Spike Gear",
topSprite: true,
price: 1e4,
scale: 120,
description: "deal damage to players that damage you",
dmg: .45
},
[53]: {
index: 39,
id: 53,
name: "Turret Gear",
topSprite: true,
price: 1e4,
scale: 120,
description: "you become a walking turret",
turret: {
projectile: 1,
range: 700,
rate: 2500
},
spdMult: .7
},
[20]: {
index: 40,
id: 20,
name: "Samurai Armor",
price: 12e3,
scale: 120,
description: "increased attack speed and fire rate",
atkSpd: .78
},
[58]: {
index: 41,
id: 58,
name: "Dark Knight",
price: 12e3,
scale: 120,
description: "restores health when you deal damage",
healD: .4
},
[27]: {
index: 42,
id: 27,
name: "Scavenger Gear",
price: 15e3,
scale: 120,
description: "earn double points for each kill",
kScrM: 2
},
[40]: {
index: 43,
id: 40,
name: "Tank Gear",
price: 15e3,
scale: 120,
description: "increased damage to buildings but slower movement",
spdMult: .3,
bDmg: 3.3
},
[52]: {
index: 44,
id: 52,
name: "Thief Gear",
price: 15e3,
scale: 120,
description: "steal half of a players gold when you kill them",
goldSteal: .5
},
[55]: {
index: 45,
id: 55,
name: "Bloodthirster",
price: 2e4,
scale: 120,
description: "Restore Health when dealing damage. And increased damage",
healD: .25,
dmgMultO: 1.2
},
[56]: {
index: 46,
id: 56,
name: "Assassin Gear",
price: 2e4,
scale: 120,
description: "Go invisible when not moving. Can't eat. Increased speed",
noEat: true,
spdMult: 1.1,
invisTimer: 1e3
}
};
const Accessories = {
[0]: {
index: 0,
id: 0,
name: "Unequip",
dontSell: true,
price: 0,
scale: 0,
xOffset: 0,
description: "None"
},
[12]: {
index: 1,
id: 12,
name: "Snowball",
price: 1e3,
scale: 105,
xOffset: 18,
description: "no effect"
},
[9]: {
index: 2,
id: 9,
name: "Tree Cape",
price: 1e3,
scale: 90,
description: "no effect"
},
[10]: {
index: 3,
id: 10,
name: "Stone Cape",
price: 1e3,
scale: 90,
description: "no effect"
},
[3]: {
index: 4,
id: 3,
name: "Cookie Cape",
price: 1500,
scale: 90,
description: "no effect"
},
[8]: {
index: 5,
id: 8,
name: "Cow Cape",
price: 2e3,
scale: 90,
description: "no effect"
},
[11]: {
index: 6,
id: 11,
name: "Monkey Tail",
price: 2e3,
scale: 97,
xOffset: 25,
description: "Super speed but reduced damage",
spdMult: 1.35,
dmgMultO: .2
},
[17]: {
index: 7,
id: 17,
name: "Apple Basket",
price: 3e3,
scale: 80,
xOffset: 12,
description: "slowly regenerates health over time",
healthRegen: 1
},
[6]: {
index: 8,
id: 6,
name: "Winter Cape",
price: 3e3,
scale: 90,
description: "no effect"
},
[4]: {
index: 9,
id: 4,
name: "Skull Cape",
price: 4e3,
scale: 90,
description: "no effect"
},
[5]: {
index: 10,
id: 5,
name: "Dash Cape",
price: 5e3,
scale: 90,
description: "no effect"
},
[2]: {
index: 11,
id: 2,
name: "Dragon Cape",
price: 6e3,
scale: 90,
description: "no effect"
},
[1]: {
index: 12,
id: 1,
name: "Super Cape",
price: 8e3,
scale: 90,
description: "no effect"
},
[7]: {
index: 13,
id: 7,
name: "Troll Cape",
price: 8e3,
scale: 90,
description: "no effect"
},
[14]: {
index: 14,
id: 14,
name: "Thorns",
price: 1e4,
scale: 115,
xOffset: 20,
description: "no effect"
},
[15]: {
index: 15,
id: 15,
name: "Blockades",
price: 1e4,
scale: 95,
xOffset: 15,
description: "no effect"
},
[20]: {
index: 16,
id: 20,
name: "Devils Tail",
price: 1e4,
scale: 95,
xOffset: 20,
description: "no effect"
},
[16]: {
index: 17,
id: 16,
name: "Sawblade",
price: 12e3,
scale: 90,
spin: true,
xOffset: 0,
description: "deal damage to players that damage you",
dmg: .15
},
[13]: {
index: 18,
id: 13,
name: "Angel Wings",
price: 15e3,
scale: 138,
xOffset: 22,
description: "slowly regenerates health over time",
healthRegen: 3
},
[19]: {
index: 19,
id: 19,
name: "Shadow Wings",
price: 15e3,
scale: 138,
xOffset: 22,
description: "increased movement speed",
spdMult: 1.1
},
[18]: {
index: 20,
id: 18,
name: "Blood Wings",
price: 2e4,
scale: 178,
xOffset: 26,
description: "restores health when you deal damage",
healD: .2
},
[21]: {
index: 21,
id: 21,
name: "Corrupt X Wings",
price: 2e4,
scale: 178,
xOffset: 26,
description: "deal damage to players that damage you",
dmg: .25
}
};
const store = [ Hats, Accessories ];
class DataHandler {
static isWeaponType(type) {
return type <= 1;
}
static isItemType(type) {
return type >= 2;
}
static getStore(type) {
return store[type];
}
static getStoreItem(type, id) {
switch (type) {
case 0:
return Hats[id];
case 1:
return Accessories[id];
default:
throw new Error(`getStoreItem Error: type "${type}" is not defined`);
}
}
static getProjectile(id) {
return Projectiles[Weapons[id].projectile];
}
static isWeapon(id) {
return void 0 !== Weapons[id];
}
static isItem(id) {
return void 0 !== Items[id];
}
static isPrimary(id) {
return null !== id && 0 === Weapons[id].itemType;
}
static isSecondary(id) {
return null !== id && 1 === Weapons[id].itemType;
}
static isMelee(id) {
return null !== id && "damage" in Weapons[id];
}
static isAttackable(id) {
return null !== id && "range" in Weapons[id];
}
static isShootable(id) {
return null !== id && "projectile" in Weapons[id];
}
static isPlaceable(id) {
return -1 !== id && "itemGroup" in Items[id];
}
static isHealable(id) {
return "restore" in Items[id];
}
static isDestroyable(id) {
return "health" in Items[id];
}
}
const utility_DataHandler = DataHandler;
const StoreHandler = new class StoreHandler {
isOpened=false;
store=[ {
previous: -1,
current: -1,
list: new Map
}, {
previous: -1,
current: -1,
list: new Map
} ];
currentType=0;
isRightStore(type) {
return this.isOpened && this.currentType === type;
}
createStore(type) {
const storeContainer = document.createElement("div");
storeContainer.id = "storeContainer";
storeContainer.style.display = "none";
const button = document.createElement("div");
button.id = "toggleStoreType";
button.textContent = 0 === type ? "Hats" : "Accessories";
button.onmousedown = () => {
this.currentType = 0 === this.currentType ? 1 : 0;
button.textContent = 0 === this.currentType ? "Hats" : "Accessories";
if (this.isOpened) {
this.fillStore(this.currentType);
}
};
storeContainer.appendChild(button);
const itemHolder = document.createElement("div");
itemHolder.id = "itemHolder";
storeContainer.appendChild(itemHolder);
itemHolder.addEventListener("wheel", (event => {
event.preventDefault();
const scale = 50 * Math.sign(event.deltaY);
itemHolder.scroll(0, itemHolder.scrollTop + scale);
}));
const {gameUI} = UI_GameUI.getElements();
gameUI.appendChild(storeContainer);
}
getTextEquip(type, id, price) {
const {list, current} = this.store[type];
if (current === id) {
return "Unequip";
}
if (list.has(id) || 0 === price) {
return "Equip";
}
return "Buy";
}
generateStoreElement(type, id, name, price, isTop) {
const srcType = [ "hats/hat", "accessories/access" ];
const src = [ srcType[type], id ];
if (isTop) {
src.push("p");
}
const html = `\n
\n

\n
${name}\n
${this.getTextEquip(type, id, price)}
\n
\n `;
const div = document.createElement("div");
div.innerHTML = html;
const equipButton = div.querySelector(".equipButton");
equipButton.onmousedown = () => {
myClient.ModuleHandler.equip(type, id, true, true);
};
return div.firstElementChild;
}
fillStore(type) {
const {itemHolder} = UI_GameUI.getElements();
itemHolder.innerHTML = "";
const items = Settings.storeItems[type];
for (const id of items) {
const item = utility_DataHandler.getStoreItem(type, id);
const element = this.generateStoreElement(type, id, item.name, item.price, "topSprite" in item);
itemHolder.appendChild(element);
}
}
handleEquipUpdate(type, prev, curr, isBuy) {
if (!this.isRightStore(type)) {
return;
}
const current = document.querySelector(`.equipButton[data-id="${curr}"]`);
if (null !== current) {
current.textContent = isBuy ? "Equip" : "Unequip";
}
if (!isBuy && -1 !== prev) {
const previous = document.querySelector(`.equipButton[data-id="${prev}"]`);
if (null !== previous) {
previous.textContent = "Equip";
}
}
}
updateStoreState(type, action, id) {
const store = this.store[type];
if (0 === action) {
store.previous = store.current;
store.current = id;
const {previous, current, list} = store;
list.set(previous, 0);
list.set(current, 1);
this.handleEquipUpdate(type, store.previous, id, false);
} else {
store.list.set(id, 0);
this.handleEquipUpdate(type, store.previous, id, true);
}
}
closeStore() {
const {storeContainer, itemHolder} = UI_GameUI.getElements();
itemHolder.innerHTML = "";
storeContainer.style.display = "none";
this.isOpened = false;
}
openStore() {
UI_GameUI.closePopups();
const {storeContainer} = UI_GameUI.getElements();
this.fillStore(this.currentType);
storeContainer.style.display = "";
storeContainer.classList.remove("closedItem");
this.isOpened = true;
}
toggleStore() {
const {storeContainer, itemHolder} = UI_GameUI.getElements();
if (this.isOpened) {
itemHolder.innerHTML = "";
} else {
UI_GameUI.closePopups();
this.fillStore(this.currentType);
}
storeContainer.style.display = "none" === storeContainer.style.display ? "" : "none";
this.isOpened = !this.isOpened;
}
init() {
this.createStore(0);
}
};
const UI_StoreHandler = StoreHandler;
const styles = '@import"https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;600;800&display=swap";header{display:flex;justify-content:space-between;align-items:center;height:45px;background:#121212;padding:10px;border-radius:6px}header h1{font-size:2.3em}header #credits{display:flex;justify-content:space-between;gap:10px;height:45px}header #credits p{margin-top:auto}header #logo{display:block;width:auto;height:100%;scale:1.2}header #close-button{display:block;fill:#adadad;cursor:pointer;width:auto;height:100%}header #close-button:hover{fill:#ebebeb}@keyframes ripple{from{opacity:1;transform:scale(0)}to{opacity:0;transform:scale(2)}}#navbar-container{display:flex;flex-direction:column;background:#121212;padding:10px;border-radius:6px}#navbar-container .open-menu{position:relative;width:8.5em;height:3.5em;background:#0d0d0d;font-weight:800;font-size:1.3em;overflow:hidden;transition:background 100ms}#navbar-container .open-menu:hover{background:#3d3d3d}#navbar-container .open-menu.active{background:#3d3d3d;pointer-events:none}#navbar-container .open-menu.bottom-align{margin-top:auto}#navbar-container .open-menu .ripple{position:absolute;z-index:5;background:rgba(255,255,255,.3);top:0;left:0;border-radius:50%;opacity:0;animation:ripple 1100ms;pointer-events:none}@keyframes toclose{from{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(0)}}@keyframes toopen{from{opacity:0;transform:scale(0)}to{opacity:1;transform:scale(1)}}@keyframes appear{from{opacity:0}to{opacity:1}}#page-container{width:100%;height:100%;overflow-y:scroll}.menu-page{background:#121212;padding:10px;border-radius:6px;display:none}.menu-page.opened{display:block}.menu-page h1{font-size:2.8em}.menu-page>.section{margin-top:20px;background:#0d0d0d;padding:15px;border-radius:6px}.menu-page>.section .section-title{font-weight:800;font-size:1.8em;color:#999;margin-bottom:10px}.menu-page>.section .section-content.split{display:flex;column-gap:30px}.menu-page>.section .section-content .content-split{width:100%;display:flex;flex-direction:column;row-gap:10px}.menu-page>.section .section-content .content-option{display:flex;justify-content:space-between;align-items:center}.menu-page>.section .section-content .content-option.centered{justify-content:center}.menu-page>.section .section-content .content-option .option-title{font-weight:800;font-size:1.4em;color:#585858;transition:color 100ms}.menu-page>.section .section-content .content-option .option-content{display:flex;justify-content:center;align-items:center;column-gap:10px}.menu-page>.section .section-content .content-option .disconnect-button{width:30px;height:30px;cursor:pointer;fill:rgba(122,49,49,.4784313725);transition:fill 100ms}.menu-page>.section .section-content .content-option:hover .option-title{color:#7e7d7d}.menu-page>.section .section-content .content-option:hover .disconnect-button{fill:#7a3131}.menu-page>.section .section-content .content-option:hover .disconnect-button:hover{fill:#893333}.menu-page>.section .section-content .text{display:flex;justify-content:left;gap:10px}.menu-page>.section .section-content .text .text-value{color:#857f7f;font-weight:800;font-size:1.5em}.menu-page>.section .section-content .option-button{width:117px;height:52px;background:#303030;border:5px solid #262626;border-radius:6px;font-weight:800;font-size:1.1em;color:#7e7d7d;transition:background 100ms,border-color 100ms}.menu-page>.section .section-content .option-button:hover{background:#383838;border-color:#2c2c2c}.menu-page>.section .section-content .hotkeyInput{width:90px;height:40px;background:#303030;border:5px solid #262626;border-radius:6px;font-weight:800;font-size:1.1em;color:#7e7d7d;display:flex;justify-content:center;align-items:center;transition:background 100ms,border-color 100ms,color 100ms}.menu-page>.section .section-content .hotkeyInput:hover{background:#383838;border-color:#2c2c2c}.menu-page>.section .section-content .hotkeyInput.active{background:#404040;border-color:#303030}.menu-page>.section .section-content .hotkeyInput.red{background:#7a3131;border-color:#672929;color:#b57272}.menu-page>.section .section-content .hotkeyInput.red:hover{background:#893333;border-color:#712d2d}.menu-page>.section .section-content .hotkeyInput.red.active{background:#923939;border-color:#712d2d}.menu-page>.section .section-content .switch-checkbox{position:relative;width:90px;height:40px}.menu-page>.section .section-content .switch-checkbox input{width:0;height:0;opacity:0}.menu-page>.section .section-content .switch-checkbox input:checked+span{background:#404040;box-shadow:0px -20px 0px 0px #353535 inset}.menu-page>.section .section-content .switch-checkbox input:checked+span:before{transform:translateX(50px) scale(0.7);background:#7e7d7d}.menu-page>.section .section-content .switch-checkbox span{position:absolute;cursor:pointer;top:0;left:0;bottom:0;right:0;width:100%;height:100%;display:flex;align-items:center;background:#303030;border-radius:6px;box-shadow:0px -20px 0px 0px #2a2a2a inset}.menu-page>.section .section-content .switch-checkbox span:before{position:absolute;content:"";transform:scale(0.7);transition:transform 300ms;width:40px;height:40px;border-radius:6px;background:#585858}.menu-page>.section .section-content input[id][type=color]{width:60px;height:33.3333333333px;outline:none;border:none;padding:3px;margin:0;background:#303030;border-radius:6px;cursor:pointer}.menu-page>.section .section-content .reset-color{background:var(--data-color);width:10px;height:10px;border-radius:50%}.menu-page>.section .section-content .slider{position:relative;display:flex;align-items:center;justify-content:space-between;gap:10px}.menu-page>.section .section-content .slider input{appearance:none;outline:none;cursor:pointer;padding:0;margin:0;border:none;width:144px;height:30px;background:#404040;box-shadow:0px -15px 0px 0px #353535 inset;border-radius:6px}.menu-page>.section .section-content .slider input::-webkit-slider-thumb{-webkit-appearance:none;transform:scale(0.7);width:30px;height:30px;background:#7e7d7d;border-radius:6px}.menu-page>.section .section-content .slider .slider-value{color:#585858;font-weight:800;font-size:1.4em;opacity:.4}@keyframes toclose{from{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(0)}}@keyframes toopen{from{opacity:0;transform:scale(0)}to{opacity:1;transform:scale(1)}}@keyframes appear{from{opacity:0}to{opacity:1}}html,body{margin:0;padding:0;scrollbar-width:thin;scrollbar-track-color:#303030;scrollbar-face-color:#262626}*{font-family:"Noto Sans",sans-serif;color:#f1f1f1}h1{font-weight:800;margin:0}h2{margin:0}p{font-weight:800;font-size:1.1rem;margin:0;color:#b8b8b8}button{border:none;outline:none;cursor:pointer}#menu-container{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:1280px;height:720px;display:flex;justify-content:center;align-items:center}#menu-container.transparent #menu-wrapper{background:rgba(8,8,8,.5882352941)}#menu-container.transparent header,#menu-container.transparent .menu-page,#menu-container.transparent #navbar-container{background:rgba(18,18,18,.5882352941)}#menu-container.transparent .section{background:rgba(13,13,13,.462745098)}#menu-container.transparent .open-menu{background:rgba(13,13,13,.462745098)}#menu-container.transparent .open-menu:hover,#menu-container.transparent .open-menu.active{background:rgba(61,61,61,.6039215686)}#menu-wrapper{position:relative;display:flex;flex-direction:column;row-gap:10px;width:85%;height:85%;padding:10px;border-radius:6px;background:#080808}#menu-wrapper.toclose{animation:150ms ease-in toclose forwards}#menu-wrapper.toopen{animation:150ms ease-in toopen forwards}main{display:flex;column-gap:10px;width:100%;height:calc(100% - 75px)}::-webkit-scrollbar{width:12px}::-webkit-scrollbar-track{background:#303030;border-radius:6px}::-webkit-scrollbar-thumb{background:#262626;border-radius:6px}.icon{width:50px;height:50px}';
const Game = "#iframe-page-container{position:absolute;z-index:10;top:0;left:0;bottom:0;right:0;width:100%;height:100%;border:none}#promoImgHolder,#gameName,#rightCardHolder,#mobileDownloadButtonContainer,#touch-controls-left,#touch-controls-right,#touch-controls-fullscreen,#partyButton,#adCard,#joinPartyButton,#guideCard>*:not(#serverBrowser,#altServer,#skinColorHolder,.settingRadio){display:none !important}#setupCard{display:flex;flex-direction:column;row-gap:10px;background:rgba(189,189,189,.2666666667);box-shadow:none}#linksContainer2{background:#505050}#setupCard>*{margin:0 !important}.actionBarItem{position:relative}.itemCounter{position:absolute;top:3px;right:3px;font-size:.95em;color:#fff;text-shadow:#3d3f42 2px 0px 0px,#3d3f42 1.75517px .958851px 0px,#3d3f42 1.0806px 1.68294px 0px,#3d3f42 .141474px 1.99499px 0px,#3d3f42 -0.832294px 1.81859px 0px,#3d3f42 -1.60229px 1.19694px 0px,#3d3f42 -1.97998px .28224px 0px,#3d3f42 -1.87291px -0.701566px 0px,#3d3f42 -1.30729px -1.5136px 0px,#3d3f42 -0.421592px -1.95506px 0px,#3d3f42 .567324px -1.91785px 0px,#3d3f42 1.41734px -1.41108px 0px,#3d3f42 1.92034px -0.558831px 0px}.itemCounter.hidden{display:none}#onetrust-consent-sdk{display:none !important}#topInfoHolder{display:flex;flex-direction:column;justify-content:right;align-items:flex-end;gap:10px}#topInfoHolder>div:not([class]):not([id]){display:none}#killCounter,#totalKillCounter{position:static;margin:0;background-image:url(../img/icons/skull.png)}.closedItem{display:none !important}#storeContainer{display:flex;flex-direction:column;gap:10px;max-width:400px;width:100%;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}#toggleStoreType{display:flex;justify-content:center;align-items:center;padding:10px;background-color:rgba(0,0,0,.15);color:#fff;border-radius:4px;cursor:pointer;font-size:20px;pointer-events:all}#itemHolder{background-color:rgba(0,0,0,.15);max-height:200px;height:100%;padding:10px;overflow-y:scroll;border-radius:4px;pointer-events:all}#itemHolder::-webkit-scrollbar{display:none;width:0;height:0;background:rgba(0,0,0,0)}.storeItemContainer{display:flex;align-items:center;gap:10px;padding:5px;height:50px;box-sizing:border-box;overflow:hidden}.storeHat{display:flex;justify-content:center;align-items:center;width:45px;height:45px;margin-top:-5px;pointer-events:none}.storeItemName{color:#fff;font-size:20px}.equipButton{margin-left:auto;color:#80eefc;cursor:pointer;font-size:35px}#bottomContainer{bottom:10px}#scriptName{position:absolute;top:0;left:0}";
code = '';
const Header = code;
Navbar_code = '';
const Navbar = Navbar_code;
Keybinds_code = '';
const Keybinds = Keybinds_code;
Combat_code = '';
const Combat = Combat_code;
Visuals_code = '';
const Visuals = Visuals_code;
Misc_code = '';
const Misc = Misc_code;
Devtool_code = '';
const Devtool = Devtool_code;
Bots_code = '';
const Bots = Bots_code;
Credits_code = '';
const Credits = Credits_code;
class Logger {
static log=console.log;
static error=console.error;
static timers=new Map;
static start(label) {
this.timers.set(label, performance.now());
}
static end(label, ...args) {
if (this.timers.has(label)) {
this.log(`${label}: ${performance.now() - this.timers.get(label)}`, ...args);
}
this.timers.delete(label);
}
}
const toBytes = b64 => Uint8Array.from(atob(b64), (c => c.charCodeAt(0)));
class Altcha {
code=null;
coreCount=Math.min(16, navigator.hardwareConcurrency || 4);
tokenEncode="IWZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2xldCBlPW5ldyBUZXh0RW5jb2Rlcjthc3luYyBmdW5jdGlvbiB0KHQsbixyKXt2YXIgbDtyZXR1cm4gbD1hd2FpdCBjcnlwdG8uc3VidGxlLmRpZ2VzdChyLnRvVXBwZXJDYXNlKCksZS5lbmNvZGUodCtuKSksWy4uLm5ldyBVaW50OEFycmF5KGwpXS5tYXAoZT0+ZS50b1N0cmluZygxNikucGFkU3RhcnQoMiwiMCIpKS5qb2luKCIiKX1mdW5jdGlvbiBuKGUsdD0xMil7bGV0IG49bmV3IFVpbnQ4QXJyYXkodCk7Zm9yKGxldCByPTA7cjx0O3IrKyluW3JdPWUlMjU2LGU9TWF0aC5mbG9vcihlLzI1Nik7cmV0dXJuIG59YXN5bmMgZnVuY3Rpb24gcih0LHI9IiIsbD0xZTYsbz0wKXtsZXQgYT0iQUVTLUdDTSIsYz1uZXcgQWJvcnRDb250cm9sbGVyLGk9RGF0ZS5ub3coKSx1PShhc3luYygpPT57Zm9yKGxldCBlPW87ZTw9bCYmIWMuc2lnbmFsLmFib3J0ZWQmJnMmJnc7ZSsrKXRyeXtsZXQgdD1hd2FpdCBjcnlwdG8uc3VidGxlLmRlY3J5cHQoe25hbWU6YSxpdjpuKGUpfSxzLHcpO2lmKHQpcmV0dXJue2NsZWFyVGV4dDpuZXcgVGV4dERlY29kZXIoKS5kZWNvZGUodCksdG9vazpEYXRlLm5vdygpLWl9fWNhdGNoe31yZXR1cm4gbnVsbH0pKCkscz1udWxsLHc9bnVsbDt0cnl7dz1mdW5jdGlvbiBlKHQpe2xldCBuPWF0b2IodCkscj1uZXcgVWludDhBcnJheShuLmxlbmd0aCk7Zm9yKGxldCBsPTA7bDxuLmxlbmd0aDtsKyspcltsXT1uLmNoYXJDb2RlQXQobCk7cmV0dXJuIHJ9KHQpO2xldCBmPWF3YWl0IGNyeXB0by5zdWJ0bGUuZGlnZXN0KCJTSEEtMjU2IixlLmVuY29kZShyKSk7cz1hd2FpdCBjcnlwdG8uc3VidGxlLmltcG9ydEtleSgicmF3IixmLGEsITEsWyJkZWNyeXB0Il0pfWNhdGNoe3JldHVybntwcm9taXNlOlByb21pc2UucmVqZWN0KCksY29udHJvbGxlcjpjfX1yZXR1cm57cHJvbWlzZTp1LGNvbnRyb2xsZXI6Y319bGV0IGw7b25tZXNzYWdlPWFzeW5jIGU9PntsZXR7dHlwZTpuLHBheWxvYWQ6byxzdGFydDphLG1heDpjfT1lLmRhdGEsaT1udWxsO2lmKCJhYm9ydCI9PT1uKWwmJmwuYWJvcnQoKSxsPXZvaWQgMDtlbHNlIGlmKCJ3b3JrIj09PW4pe2lmKCJvYmZ1c2NhdGVkImluIG8pe2xldHtrZXk6dSxvYmZ1c2NhdGVkOnN9PW98fHt9O2k9YXdhaXQgcihzLHUsYyxhKX1lbHNle2xldHthbGdvcml0aG06dyxjaGFsbGVuZ2U6ZixzYWx0OmR9PW98fHt9O2k9ZnVuY3Rpb24gZShuLHIsbD0iU0hBLTI1NiIsbz0xZTYsYT0wKXtsZXQgYz1uZXcgQWJvcnRDb250cm9sbGVyLGk9RGF0ZS5ub3coKSx1PShhc3luYygpPT57Zm9yKGxldCBlPWE7ZTw9byYmIWMuc2lnbmFsLmFib3J0ZWQ7ZSsrKXtsZXQgdT1hd2FpdCB0KHIsZSxsKTtpZih1PT09bilyZXR1cm57bnVtYmVyOmUsdG9vazpEYXRlLm5vdygpLWl9fXJldHVybiBudWxsfSkoKTtyZXR1cm57cHJvbWlzZTp1LGNvbnRyb2xsZXI6Y319KGYsZCx3LGMsYSl9bD1pLmNvbnRyb2xsZXIsaS5wcm9taXNlLnRoZW4oZT0+e3NlbGYucG9zdE1lc3NhZ2UoZSYmey4uLmUsd29ya2VyOiEwfSl9KX19fSgpOw==";
workerBlob=new Blob([ toBytes(this.tokenEncode) ], {
type: "text/javascript;charset=utf-8"
});
static createPayload(data, result) {
return btoa(JSON.stringify({
algorithm: data.algorithm,
challenge: data.challenge,
number: result.number,
salt: data.salt,
signature: data.signature,
test: !!data || void 0,
took: result.took
}));
}
createWorker(name = "alt_worker") {
try {
const url = URL.createObjectURL(this.workerBlob);
const worker = new Worker(url, {
name
});
worker.addEventListener("error", (() => URL.revokeObjectURL(url)));
return worker;
} catch (e) {
return new Worker(`data:text/javascript;base64,${this.tokenEncode}`, {
name
});
}
}
async fetchChallenge() {
const res = await fetch("https://api.moomoo.io/verify");
if (!res.ok) {
throw new Error("Failed to fetch challenge.");
}
return res.json();
}
async getWorkerSolution(task, total, count = this.coreCount) {
const workerCount = Math.min(16, Math.max(1, count));
const workers = Array.from({
length: workerCount
}, (() => this.createWorker()));
const chunkSize = Math.ceil(total / workerCount);
const results = await Promise.all(workers.map(((worker, index) => {
const start = index * chunkSize;
return new Promise((resolve => {
worker.onmessage = msg => {
if (msg.data) {
workers.forEach((w => {
if (w !== worker) {
w.postMessage({
type: "abort"
});
}
}));
}
resolve(msg.data ?? null);
};
worker.onerror = () => resolve(null);
const message = {
type: "work",
payload: task,
start,
max: start + chunkSize
};
worker.postMessage(message);
}));
})));
workers.forEach((worker => worker.terminate()));
return results.find((r => !!r)) ?? null;
}
async validateChallenge(data) {
const solution = await this.getWorkerSolution(data, data.maxnumber);
if (!solution) {
throw new Error("Failed to solve challenge.");
}
return {
challengeData: data,
solution
};
}
async generate() {
try {
const challengeData = await this.fetchChallenge();
const {solution} = await this.validateChallenge(challengeData);
const encoded = Altcha.createPayload(challengeData, solution);
this.code = `alt:${encoded}`;
return encodeURIComponent(this.code);
} catch (error) {
console.error("Token generation failed:", error);
throw error;
}
}
}
const altcha = new Altcha;
const createSocket = async () => {
const token = await altcha.generate();
const socket = myClient.connection.socket;
const origin = new URL(socket.url).origin;
const url = origin + "/?token=" + token;
const ws = new WebSocket(url);
ws.binaryType = "arraybuffer";
return ws;
};
const modules_createSocket = createSocket;
class ObjectItem {
id;
position;
angle;
scale;
constructor(id, x, y, angle, scale) {
this.id = id;
this.position = {
current: new modules_Vector(x, y)
};
this.angle = angle;
this.scale = scale;
}
get hitScale() {
return this.scale;
}
}
class Resource extends ObjectItem {
type;
layer;
constructor(id, x, y, angle, scale, type) {
super(id, x, y, angle, scale);
this.type = type;
this.layer = 0 === type ? 3 : 2 === type ? 0 : 2;
}
formatScale(scaleMult = 1) {
const reduceScale = 0 === this.type || 1 === this.type ? .6 * scaleMult : 1;
return this.scale * reduceScale;
}
get collisionScale() {
return this.formatScale();
}
get placementScale() {
return this.formatScale(.6);
}
get isCactus() {
return 1 === this.type && pointInDesert(this.position.current);
}
}
class PlayerObject extends ObjectItem {
type;
ownerID;
collisionDivider;
health;
maxHealth;
reload=-1;
maxReload=-1;
isDestroyable;
seenPlacement=false;
layer;
itemGroup;
constructor(id, x, y, angle, scale, type, ownerID) {
super(id, x, y, angle, scale);
this.type = type;
this.ownerID = ownerID;
const item = Items[type];
this.collisionDivider = "colDiv" in item ? item.colDiv : 1;
this.health = "health" in item ? item.health : Infinity;
this.maxHealth = this.health;
this.isDestroyable = Infinity !== this.maxHealth;
if (17 === item.id) {
this.reload = item.shootRate;
this.maxReload = this.reload;
}
this.layer = ItemGroups[item.itemGroup].layer;
this.itemGroup = item.itemGroup;
}
formatScale(placeCollision = false) {
return this.scale * (placeCollision ? 1 : this.collisionDivider);
}
get collisionScale() {
return this.formatScale();
}
get placementScale() {
const item = Items[this.type];
if (21 === item.id) {
return item.blocker;
}
return this.scale;
}
}
class SpatialHashGrid {
cellSize;
cells;
constructor(cellSize) {
this.cellSize = cellSize;
this.cells = [];
}
hashPosition(x, y) {
const cellX = Math.floor(x / this.cellSize);
const cellY = Math.floor(y / this.cellSize);
return [ cellX, cellY ];
}
clear() {
this.cells.length = 0;
}
insert(object) {
const {x, y} = object.position.current;
const [cellX, cellY] = this.hashPosition(x, y);
if (!this.cells[cellX]) {
this.cells[cellX] = [];
}
if (!this.cells[cellX][cellY]) {
this.cells[cellX][cellY] = [];
}
this.cells[cellX][cellY].push(object);
}
retrieve(position, radius) {
const {x, y} = position;
const [startX, startY] = this.hashPosition(x - radius, y - radius);
const [endX, endY] = this.hashPosition(x + radius, y + radius);
const results = [];
for (let cellX = startX - 1; cellX <= endX + 1; cellX++) {
for (let cellY = startY - 1; cellY <= endY + 1; cellY++) {
if (this.cells[cellX] && this.cells[cellX][cellY]) {
const objects = this.cells[cellX][cellY];
for (const object of objects) {
results.push(object);
}
}
}
}
return results;
}
remove(object) {
const {x, y} = object.position.current;
const [cellX, cellY] = this.hashPosition(x, y);
if (this.cells[cellX] && this.cells[cellX][cellY]) {
const objects = this.cells[cellX][cellY];
const index = objects.indexOf(object);
if (-1 !== index) {
const lastIndex = objects.length - 1;
if (index === lastIndex) {
objects.pop();
} else {
objects[index] = objects.pop();
}
return true;
}
}
return false;
}
}
const modules_SpatialHashGrid = SpatialHashGrid;
class EnemyManager {
client;
enemiesGrid=new modules_SpatialHashGrid(100);
enemies=[];
trappedEnemies=new Set;
dangerousEnemies=[];
_nearestEnemy=[ null, null ];
nearestMeleeReloaded=null;
nearestDangerAnimal=null;
nearestTrap=null;
nearestCollideSpike=null;
nearestTurretEntity=null;
detectedEnemy=false;
constructor(client) {
this.client = client;
}
reset() {
this.enemiesGrid.clear();
this.enemies.length = 0;
this.trappedEnemies.clear();
this.dangerousEnemies.length = 0;
this._nearestEnemy[0] = null;
this._nearestEnemy[1] = null;
this.nearestMeleeReloaded = null;
this.nearestDangerAnimal = null;
this.nearestTrap = null;
this.nearestCollideSpike = null;
this.nearestTurretEntity = null;
this.detectedEnemy = false;
}
get nearestEnemy() {
return this._nearestEnemy[0];
}
get nearestAnimal() {
return this._nearestEnemy[1];
}
isNear(enemy, nearest) {
if (null === nearest) {
return true;
}
const {myPlayer} = this.client;
const a0 = myPlayer.position.current;
const distance1 = a0.distance(enemy.position.current);
const distance2 = a0.distance(nearest.position.current);
return distance1 < distance2;
}
get nearestEntity() {
const target1 = this.nearestEnemy;
const target2 = this.nearestAnimal;
if (null === target1) {
return target2;
}
return this.isNear(target1, target2) ? target1 : target2;
}
nearestEnemyInRangeOf(range, target) {
const enemy = target || this.nearestEnemy;
return null !== enemy && this.client.myPlayer.collidingEntity(enemy, range);
}
handleNearestDanger(enemy) {
const {myPlayer, ModuleHandler} = this.client;
const extraRange = enemy.usingBoost && !enemy.isTrapped ? 400 : 100;
const range = enemy.getMaxWeaponRange() + myPlayer.hitScale + extraRange;
if (myPlayer.collidingEntity(enemy, range)) {
if (enemy.danger >= 3) {
ModuleHandler.needToHeal = true;
}
this.detectedEnemy = true;
}
}
handleDanger(enemy) {
if (enemy.dangerList.length >= 2) {
enemy.dangerList.shift();
}
const danger = enemy.canPossiblyInstakill();
enemy.dangerList.push(danger);
enemy.danger = Math.max(...enemy.dangerList);
if (0 !== enemy.danger) {
this.dangerousEnemies.push(enemy);
this.handleNearestDanger(enemy);
}
}
checkCollision(target, isOwner = false) {
target.isTrapped = false;
target.onPlatform = false;
const {ObjectManager, PlayerManager} = this.client;
const objects = ObjectManager.retrieveObjects(target.position.current, target.collisionScale);
for (const object of objects) {
if (object instanceof Resource) {
continue;
}
if (!target.collidingObject(object, 5)) {
continue;
}
const isEnemyObject = PlayerManager.isEnemyByID(object.ownerID, target);
if (15 === object.type && isEnemyObject) {
this.trappedEnemies.add(target);
target.isTrapped = true;
if (isOwner && this.isNear(target, this.nearestTrap)) {
this.nearestTrap = object;
}
} else if (18 === object.type) {
target.onPlatform = true;
} else if (2 === object.itemGroup && isEnemyObject) {
if (!isOwner && this.isNear(target, this.nearestCollideSpike)) {
const pos1 = target.position.future;
const pos2 = object.position.current;
const distance = pos1.distance(pos2);
const range = object.collisionScale + target.collisionScale;
const willCollide = distance <= range;
if (willCollide) {
this.nearestCollideSpike = target;
}
}
}
}
}
handleNearest(type, enemy) {
if (this.isNear(enemy, this._nearestEnemy[type])) {
this._nearestEnemy[type] = enemy;
if (enemy.canUseTurret && this.client.myPlayer.collidingEntity(enemy, 700)) {
this.nearestTurretEntity = enemy;
}
}
}
handleNearestMelee(enemy) {
const {myPlayer, ModuleHandler} = this.client;
const range = enemy.getMaxWeaponRange() + myPlayer.hitScale + 60;
const angle = ModuleHandler.getMoveAngle();
if (!enemy.meleeReloaded()) {
return;
}
if (!myPlayer.collidingEntity(enemy, range)) {
return;
}
if (!myPlayer.runningAwayFrom(enemy, angle)) {
return;
}
if (!this.isNear(enemy, this.nearestMeleeReloaded)) {
return;
}
this.nearestMeleeReloaded = enemy;
}
handleNearestDangerAnimal(animal) {
const {myPlayer} = this.client;
if (!animal.isDanger) {
return;
}
if (!myPlayer.collidingEntity(animal, animal.collisionRange)) {
return;
}
if (!this.isNear(animal, this.nearestDangerAnimal)) {
return;
}
this.nearestDangerAnimal = animal;
}
handleEnemies(players, animals) {
this.reset();
const {myPlayer} = this.client;
this.checkCollision(myPlayer, true);
for (let i = 0; i < players.length; i++) {
const player = players[i];
if (myPlayer.isEnemyByID(player.id)) {
this.enemiesGrid.insert(player);
this.enemies.push(player);
this.checkCollision(player);
this.handleDanger(player);
this.handleNearest(0, player);
this.handleNearestMelee(player);
}
}
for (let i = 0; i < animals.length; i++) {
const animal = animals[i];
this.handleNearest(1, animal);
this.handleNearestDangerAnimal(animal);
}
}
}
const Managers_EnemyManager = EnemyManager;
class LeaderboardManager {
client;
list=new Set;
constructor(client) {
this.client = client;
}
updatePlayer(id, nickname, gold) {
const owner = this.client.PlayerManager.playerData.get(id) || this.client.PlayerManager.createPlayer({
id,
nickname
});
this.list.add(owner);
owner.totalGold = gold;
owner.inLeaderboard = true;
}
update(data) {
for (const player of this.list) {
player.inLeaderboard = false;
}
this.list.clear();
for (let i = 0; i < data.length; i += 3) {
const id = data[i + 0];
const nickname = data[i + 1];
const gold = data[i + 2];
this.updatePlayer(id, nickname, gold);
}
}
}
const Managers_LeaderboardManager = LeaderboardManager;
const WeaponTypeString = [ "primary", "secondary" ];
class Entity {
id=-1;
position={
previous: new modules_Vector,
current: new modules_Vector,
future: new modules_Vector
};
angle=0;
scale=0;
setFuturePosition() {
const {previous, current, future} = this.position;
const distance = previous.distance(current);
const angle = previous.angle(current);
future.setVec(current.direction(angle, distance));
}
get collisionScale() {
return this.scale;
}
get hitScale() {
return 1.8 * this.scale;
}
client;
constructor(client) {
this.client = client;
}
colliding(object, radius) {
const {previous: a0, current: a1, future: a2} = this.position;
const b0 = object.position.current;
return a0.distance(b0) <= radius || a1.distance(b0) <= radius || a2.distance(b0) <= radius;
}
collidingObject(object, addRadius = 0, checkPrevious = true) {
const {previous: a0, current: a1, future: a2} = this.position;
const b0 = object.position.current;
const radius = this.collisionScale + object.collisionScale + addRadius;
return checkPrevious && a0.distance(b0) <= radius || a1.distance(b0) <= radius || a2.distance(b0) <= radius;
}
collidingEntity(entity, range, checkBased = false, prev = true) {
const {previous: a0, current: a1, future: a2} = this.position;
const {previous: b0, current: b1, future: b2} = entity.position;
if (checkBased) {
return prev && a0.distance(b0) <= range || a1.distance(b1) <= range || a2.distance(b2) <= range;
}
return a0.distance(b0) <= range || a0.distance(b1) <= range || a0.distance(b2) <= range || a1.distance(b0) <= range || a1.distance(b1) <= range || a1.distance(b2) <= range || a2.distance(b0) <= range || a2.distance(b1) <= range || a2.distance(b2) <= range;
}
checkCollision(itemGroup, addRadius = 0, checkEnemy = false, checkPrevious = true) {
const {ObjectManager} = this.client;
const objects = ObjectManager.retrieveObjects(this.position.current, this.collisionScale);
for (const object of objects) {
const matchItem = object instanceof PlayerObject && object.itemGroup === itemGroup;
const isCactus = object instanceof Resource && 2 === itemGroup && object.isCactus;
if (matchItem || isCactus) {
if (checkEnemy && !ObjectManager.isEnemyObject(object)) {
continue;
}
if (this.collidingObject(object, addRadius, checkPrevious)) {
return true;
}
}
}
return false;
}
runningAwayFrom(entity, angle) {
if (null === angle) {
return false;
}
const pos1 = this.position.current;
const pos2 = entity.position.current;
const angleTo = pos1.angle(pos2);
if (getAngleDist(angle, angleTo) <= Math.PI / 2) {
return false;
}
return true;
}
}
const data_Entity = Entity;
class Player extends data_Entity {
socketID="";
currentItem=-1;
clanName=null;
isLeader=false;
nickname="unknown";
skinID=0;
scale=35;
hatID=0;
accessoryID=0;
totalStorePrice=0;
storeList=[ new Set, new Set ];
previousHealth=100;
currentHealth=100;
tempHealth=100;
maxHealth=100;
globalInventory={};
weapon={};
variant={};
reload={
primary: {},
secondary: {},
turret: {}
};
objects=new Set;
totalGold=0;
inLeaderboard=false;
newlyCreated=true;
usingBoost=false;
isTrapped=false;
onPlatform=false;
isFullyUpgraded=false;
potentialDamage=0;
foundProjectiles=new Map;
dangerList=[];
danger=0;
constructor(client) {
super(client);
this.init();
}
hasFound(projectile) {
const key = projectile.type;
return this.foundProjectiles.has(key);
}
addFound(projectile) {
const key = projectile.type;
if (!this.foundProjectiles.has(key)) {
this.foundProjectiles.set(key, []);
}
const list = this.foundProjectiles.get(key);
list.push(projectile);
}
resetReload() {
const {primary, secondary} = this.weapon;
const primarySpeed = null !== primary ? this.getWeaponSpeed(primary) : -1;
const secondarySpeed = null !== secondary ? this.getWeaponSpeed(secondary) : -1;
const reload = this.reload;
reload.primary.current = primarySpeed;
reload.primary.max = primarySpeed;
reload.secondary.current = secondarySpeed;
reload.secondary.max = secondarySpeed;
reload.turret.current = 2500;
reload.turret.max = 2500;
}
resetGlobalInventory() {
this.globalInventory[0] = null;
this.globalInventory[1] = null;
this.globalInventory[2] = null;
this.globalInventory[3] = null;
this.globalInventory[4] = null;
this.globalInventory[5] = null;
this.globalInventory[6] = null;
this.globalInventory[7] = null;
this.globalInventory[8] = null;
this.globalInventory[9] = null;
}
init() {
this.weapon.current = 0;
this.weapon.oldCurrent = 0;
this.weapon.primary = null;
this.weapon.secondary = null;
this.variant.current = 0;
this.variant.primary = 0;
this.variant.secondary = 0;
this.resetReload();
this.resetGlobalInventory();
this.newlyCreated = true;
this.usingBoost = false;
this.isFullyUpgraded = false;
this.foundProjectiles.clear();
}
get canUseTurret() {
return 22 !== this.hatID;
}
update(id, x, y, angle, currentItem, currentWeapon, weaponVariant, clanName, isLeader, hatID, accessoryID, isSkull) {
this.id = id;
this.position.previous.setVec(this.position.current);
this.position.current.setXY(x, y);
this.setFuturePosition();
this.angle = angle;
this.currentItem = currentItem;
this.weapon.oldCurrent = this.weapon.current;
this.weapon.current = currentWeapon;
this.variant.current = weaponVariant;
this.clanName = clanName;
this.isLeader = Boolean(isLeader);
this.hatID = hatID;
this.accessoryID = accessoryID;
if (!this.storeList[0].has(hatID)) {
this.storeList[0].add(hatID);
this.totalStorePrice += Hats[hatID].price;
}
if (!this.storeList[1].has(accessoryID)) {
this.storeList[1].add(accessoryID);
this.totalStorePrice += Accessories[accessoryID].price;
}
this.newlyCreated = false;
this.potentialDamage = 0;
this.predictItems();
this.predictWeapons();
this.updateReloads();
}
updateHealth(health) {
this.previousHealth = this.currentHealth;
this.currentHealth = health;
this.tempHealth = health;
}
predictItems() {
if (-1 === this.currentItem) {
return;
}
const item = Items[this.currentItem];
this.globalInventory[item.itemType] = this.currentItem;
}
increaseReload(reload) {
reload.current = Math.min(reload.current + this.client.PlayerManager.step, reload.max);
}
updateTurretReload() {
const reload = this.reload.turret;
this.increaseReload(reload);
if (53 !== this.hatID) {
return;
}
const {ProjectileManager} = this.client;
const speed = Projectiles[1].speed;
const list = ProjectileManager.projectiles.get(speed);
if (void 0 === list) {
return;
}
const current = this.position.current;
for (let i = 0; i < list.length; i++) {
const projectile = list[i];
const distance = current.distance(projectile.position.current);
if (distance < 2) {
if (this.hasFound(projectile)) {
this.foundProjectiles.clear();
}
this.addFound(projectile);
projectile.owner = this;
reload.current = 0;
removeFast(list, i);
break;
}
}
}
updateReloads() {
this.updateTurretReload();
if (-1 !== this.currentItem) {
return;
}
const weapon = Weapons[this.weapon.current];
const type = WeaponTypeString[weapon.itemType];
const reload = this.reload[type];
this.increaseReload(reload);
if ("projectile" in weapon) {
const {ProjectileManager} = this.client;
const speedMult = this.getWeaponSpeedMult();
const type = weapon.projectile;
const speed = Projectiles[type].speed * speedMult;
const list = ProjectileManager.projectiles.get(speed);
if (void 0 === list) {
return;
}
const current = this.position.current;
for (let i = 0; i < list.length; i++) {
const projectile = list[i];
const distance = current.distance(projectile.position.current);
if (distance < 2 && this.angle === projectile.angle) {
if (this.hasFound(projectile)) {
this.foundProjectiles.clear();
}
this.addFound(projectile);
projectile.owner = this;
reload.current = 0;
reload.max = this.getWeaponSpeed(weapon.id);
removeFast(list, i);
break;
}
}
}
}
handleObjectPlacement(object) {
this.objects.add(object);
const {myPlayer, ObjectManager} = this.client;
const item = Items[object.type];
if (object.seenPlacement) {
if (17 === object.type) {
ObjectManager.resetTurret(object.id);
} else if (16 === object.type && !this.newlyCreated) {
this.usingBoost = true;
}
this.updateInventory(object.type);
}
if (myPlayer.isMyPlayerByID(this.id) && 5 === item.itemType) {
myPlayer.totalGoldAmount += item.pps;
}
}
handleObjectDeletion(object) {
this.objects.delete(object);
const {myPlayer} = this.client;
const item = Items[object.type];
if (myPlayer.isMyPlayerByID(this.id) && 5 === item.itemType) {
myPlayer.totalGoldAmount -= item.pps;
}
}
updateInventory(type) {
const item = Items[type];
const inventoryID = this.globalInventory[item.itemType];
const shouldUpdate = null === inventoryID || item.age > Items[inventoryID].age;
if (shouldUpdate) {
this.globalInventory[item.itemType] = item.id;
}
}
detectFullUpgrade() {
const inventory = this.globalInventory;
const primary = inventory[0];
const secondary = inventory[1];
const spike = inventory[4];
if (primary && secondary) {
if ("isUpgrade" in Weapons[primary] && "isUpgrade" in Weapons[secondary]) {
return true;
}
}
return primary && 8 === Weapons[primary].age || secondary && 9 === Weapons[secondary].age || spike && 9 === Items[spike].age || 12 === inventory[5] || 20 === inventory[9];
}
predictPrimary(id) {
if (11 === id) {
return 4;
}
return 5;
}
predictSecondary(id) {
if (0 === id) {
return null;
}
if (2 === id || 4 === id) {
return 10;
}
return 15;
}
predictWeapons() {
const {current, oldCurrent} = this.weapon;
const weapon = Weapons[current];
const type = WeaponTypeString[weapon.itemType];
const reload = this.reload[type];
const upgradedWeapon = current !== oldCurrent && weapon.itemType === Weapons[oldCurrent].itemType;
if (-1 === reload.max || upgradedWeapon) {
reload.current = weapon.speed;
reload.max = weapon.speed;
}
this.globalInventory[weapon.itemType] = current;
this.variant[type] = this.variant.current;
const currentType = this.weapon[type];
if (null === currentType || weapon.age > Weapons[currentType].age) {
this.weapon[type] = current;
}
const primary = this.globalInventory[0];
const secondary = this.globalInventory[1];
const notPrimaryUpgrade = null === primary || !("isUpgrade" in Weapons[primary]);
const notSecondaryUpgrade = null === secondary || !("isUpgrade" in Weapons[secondary]);
if (utility_DataHandler.isSecondary(current) && notPrimaryUpgrade) {
const predicted = this.predictPrimary(current);
if (null === primary || Weapons[predicted].upgradeType === Weapons[primary].upgradeType) {
this.weapon.primary = predicted;
}
} else if (utility_DataHandler.isPrimary(current) && notSecondaryUpgrade) {
const predicted = this.predictSecondary(current);
if (null === predicted || null === secondary || Weapons[predicted].upgradeType === Weapons[secondary].upgradeType) {
this.weapon.secondary = predicted;
}
}
this.isFullyUpgraded = this.detectFullUpgrade();
if (this.isFullyUpgraded) {
if (null !== primary) {
this.weapon.primary = primary;
}
if (null !== secondary) {
this.weapon.secondary = secondary;
}
}
}
getWeaponVariant(id) {
const type = Weapons[id].itemType;
const variant = this.variant[WeaponTypeString[type]];
return {
current: variant,
next: Math.min(variant + 1, 3)
};
}
getBuildingDamage(id) {
const weapon = Weapons[id];
const variant = WeaponVariants[this.getWeaponVariant(id).current];
let damage = weapon.damage * variant.val;
if ("sDmg" in weapon) {
damage *= weapon.sDmg;
}
const hat = Hats[this.hatID];
if ("bDmg" in hat) {
damage *= hat.bDmg;
}
return damage;
}
canDealPoison(weaponID) {
const variant = this.getWeaponVariant(weaponID).current;
const isRuby = 3 === variant;
const hasPlague = 21 === this.hatID;
return {
isAble: isRuby || hasPlague,
count: isRuby ? 5 : hasPlague ? 6 : 0
};
}
getWeaponSpeed(id, hat = this.hatID) {
const reloadSpeed = 20 === hat ? Hats[hat].atkSpd : 1;
return Weapons[id].speed * reloadSpeed;
}
getWeaponSpeedMult() {
if (1 === this.hatID) {
return Hats[this.hatID].aMlt;
}
return 1;
}
getMaxWeaponRange() {
const {primary, secondary} = this.weapon;
const primaryRange = Weapons[primary].range;
if (utility_DataHandler.isMelee(secondary)) {
const range = Weapons[secondary].range;
if (range > primaryRange) {
return range;
}
}
return primaryRange;
}
getMaxWeaponDamage(id, lookingShield) {
if (utility_DataHandler.isMelee(id)) {
const bull = Hats[7];
const variant = this.getWeaponVariant(id).current;
let damage = Weapons[id].damage;
damage *= bull.dmgMultO;
damage *= WeaponVariants[variant].val;
if (lookingShield) {
damage *= Weapons[11].shield;
}
return damage;
} else if (utility_DataHandler.isShootable(id) && !lookingShield) {
const projectile = utility_DataHandler.getProjectile(id);
return projectile.damage;
}
return 0;
}
getItemPlaceScale(itemID) {
const item = Items[itemID];
return this.scale + item.scale + item.placeOffset;
}
isReloaded(type, tick = 2 * this.client.SocketManager.TICK) {
const reload = this.reload[type].current;
const max = this.reload[type].max - tick;
return reload >= max;
}
meleeReloaded() {
const {TICK} = this.client.SocketManager;
return this.isReloaded("primary", TICK) || utility_DataHandler.isMelee(this.weapon.secondary) && this.isReloaded("secondary", TICK);
}
detectSpikeInsta() {
const {myPlayer, ObjectManager} = this.client;
const spikeID = this.globalInventory[4] || 9;
const placeLength = this.getItemPlaceScale(spikeID);
const pos1 = this.position.current;
const pos2 = myPlayer.position.current;
const angleTo = pos1.angle(pos2);
const angles = ObjectManager.getBestPlacementAngles(pos1, spikeID, angleTo);
const spike = Items[spikeID];
for (const angle of angles) {
const spikePos = pos1.direction(angle, placeLength);
const distance = pos2.distance(spikePos);
const range = this.collisionScale + spike.scale;
if (distance <= range) {
this.potentialDamage += spike.damage;
break;
}
}
}
canPossiblyInstakill() {
const {PlayerManager, myPlayer} = myClient;
const lookingShield = PlayerManager.lookingShield(myPlayer, this);
const {primary, secondary} = this.weapon;
const primaryDamage = this.getMaxWeaponDamage(primary, lookingShield);
const secondaryDamage = this.getMaxWeaponDamage(secondary, lookingShield);
if (this.isReloaded("primary")) {
this.potentialDamage += primaryDamage;
}
if (this.isReloaded("secondary")) {
const turrets = this.foundProjectiles.get(1);
this.foundProjectiles.clear();
if (void 0 !== turrets) {
this.foundProjectiles.set(1, turrets);
}
this.potentialDamage += secondaryDamage;
}
if (this.isReloaded("turret") && !lookingShield) {
this.potentialDamage += 25;
}
this.detectSpikeInsta();
if (this.potentialDamage * Hats[6].dmgMult >= 100) {
return 3;
}
if (this.potentialDamage >= 100) {
return 2;
}
return 0;
}
}
const data_Player = Player;
class Renderer {
static objects=[];
static rect(ctx, pos, scale, color, lineWidth = 4) {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.translate(-myClient.myPlayer.offset.x, -myClient.myPlayer.offset.y);
ctx.translate(pos.x, pos.y);
ctx.rect(-scale, -scale, 2 * scale, 2 * scale);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
static roundRect(ctx, x, y, w, h, r) {
if (w < 2 * r) {
r = w / 2;
}
if (h < 2 * r) {
r = h / 2;
}
if (r < 0) {
r = 0;
}
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
static circle(ctx, x, y, radius, color, opacity = 1, lineWidth = 4) {
ctx.save();
ctx.globalAlpha = opacity;
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.translate(-myClient.myPlayer.offset.x, -myClient.myPlayer.offset.y);
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
static fillCircle(ctx, x, y, radius, color, opacity = 1) {
ctx.save();
ctx.globalAlpha = opacity;
ctx.fillStyle = color;
ctx.beginPath();
ctx.translate(-myClient.myPlayer.offset.x, -myClient.myPlayer.offset.y);
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
ctx.restore();
}
static line(ctx, start, end, color, opacity = 1, lineWidth = 4) {
ctx.save();
ctx.translate(-myClient.myPlayer.offset.x, -myClient.myPlayer.offset.y);
ctx.globalAlpha = opacity;
ctx.strokeStyle = color;
ctx.lineCap = "round";
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
ctx.restore();
}
static arrow(ctx, length, x, y, angle, color) {
ctx.save();
ctx.translate(-myClient.myPlayer.offset.x, -myClient.myPlayer.offset.y);
ctx.translate(x, y);
ctx.rotate(Math.PI / 4);
ctx.rotate(angle);
ctx.globalAlpha = .75;
ctx.strokeStyle = color;
ctx.lineCap = "round";
ctx.lineWidth = 8;
ctx.beginPath();
ctx.moveTo(-length, -length);
ctx.lineTo(length, -length);
ctx.lineTo(length, length);
ctx.stroke();
ctx.restore();
}
static cross(ctx, x, y, size, lineWidth, color) {
ctx.save();
ctx.globalAlpha = 1;
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.translate(x - myClient.myPlayer.offset.x, y - myClient.myPlayer.offset.y);
const halfSize = size / 2;
ctx.beginPath();
ctx.moveTo(-halfSize, -halfSize);
ctx.lineTo(halfSize, halfSize);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(halfSize, -halfSize);
ctx.lineTo(-halfSize, halfSize);
ctx.stroke();
ctx.restore();
}
static getTracerColor(entity) {
if (entity instanceof Notification) {
return Settings.notificationTracersColor;
}
if (Settings.animalTracers && entity.isAI) {
return Settings.animalTracersColor;
}
if (Settings.teammateTracers && entity.isPlayer && myClient.myPlayer.isTeammateByID(entity.sid)) {
return Settings.teammateTracersColor;
}
if (Settings.enemyTracers && entity.isPlayer && myClient.myPlayer.isEnemyByID(entity.sid)) {
return Settings.enemyTracersColor;
}
return null;
}
static renderTracer(ctx, entity, player) {
const color = this.getTracerColor(entity);
if (null === color) {
return;
}
const pos1 = new modules_Vector(player.x, player.y);
const pos2 = new modules_Vector(entity.x, entity.y);
if (Settings.arrows) {
const w = 8;
const distance = Math.min(100 + 2 * w, pos1.distance(pos2) - 2 * w);
const angle = pos1.angle(pos2);
const pos = pos1.direction(angle, distance);
this.arrow(ctx, w, pos.x, pos.y, angle, color);
} else {
this.line(ctx, pos1, pos2, color, .75);
}
}
static getMarkerColor(object) {
const id = object.owner?.sid;
if (void 0 === id) {
return null;
}
if (Settings.itemMarkers && myClient.myPlayer.isMyPlayerByID(id)) {
return Settings.itemMarkersColor;
}
if (Settings.teammateMarkers && myClient.myPlayer.isTeammateByID(id)) {
return Settings.teammateMarkersColor;
}
if (Settings.enemyMarkers && myClient.myPlayer.isEnemyByID(id)) {
return Settings.enemyMarkersColor;
}
return null;
}
static renderMarker(ctx, object) {
const color = this.getMarkerColor(object);
if (null === color) {
return;
}
const x = object.x + object.xWiggle - myClient.myPlayer.offset.x;
const y = object.y + object.yWiggle - myClient.myPlayer.offset.y;
ctx.save();
ctx.strokeStyle = "#3b3b3b";
ctx.lineWidth = 4;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
}
static barContainer(ctx, x, y, w, h, r = 8) {
ctx.fillStyle = "#3d3f42";
this.roundRect(ctx, x, y, w, h, r);
ctx.fill();
}
static barContent(ctx, x, y, w, h, fill, color) {
const barPad = constants_Config.barPad;
ctx.fillStyle = color;
this.roundRect(ctx, x + barPad, y + barPad, (w - 2 * barPad) * fill, h - 2 * barPad, 7);
ctx.fill();
}
static getNameY(target) {
let nameY = 34;
const height = 5;
if (target === myClient.myPlayer && Settings.weaponXPBar) {
nameY += height;
}
if (Settings.playerTurretReloadBar) {
nameY += height;
}
if (Settings.weaponReloadBar) {
nameY += height;
}
return nameY;
}
static getContainerHeight(entity) {
const {barHeight, barPad} = constants_Config;
let height = barHeight;
if (entity.isPlayer) {
const smallBarHeight = barHeight - 4;
const player = myClient.PlayerManager.playerData.get(entity.sid);
if (void 0 === player) {
return height;
}
if (player === myClient.myPlayer && Settings.weaponXPBar) {
height += smallBarHeight - barPad;
}
if (Settings.playerTurretReloadBar) {
height += smallBarHeight - barPad;
}
if (Settings.weaponReloadBar) {
height += barHeight - barPad;
}
}
return height;
}
static renderBar(ctx, entity) {
const {barWidth, barHeight, barPad} = constants_Config;
const smallBarHeight = barHeight - 4;
const totalWidth = barWidth + barPad;
const scale = entity.scale + 34;
const {myPlayer, PlayerManager} = myClient;
let x = entity.x - myPlayer.offset.x - totalWidth;
let y = entity.y - myPlayer.offset.y + scale;
ctx.save();
const player = entity.isPlayer && PlayerManager.playerData.get(entity.sid);
const animal = entity.isAI && PlayerManager.animalData.get(entity.sid);
let height = 0;
if (player instanceof data_Player) {
const {primary, secondary, turret} = player.reload;
if (player === myPlayer && Settings.weaponXPBar) {
const weapon = Weapons[myPlayer.weapon.current];
const current = WeaponVariants[myPlayer.getWeaponVariant(weapon.id).current].color;
const next = WeaponVariants[myPlayer.getWeaponVariant(weapon.id).next].color;
const XP = myPlayer.weaponXP[weapon.itemType];
this.barContainer(ctx, x, y, 2 * totalWidth, smallBarHeight);
this.barContent(ctx, x, y, 2 * totalWidth, smallBarHeight, 1, current);
this.barContent(ctx, x, y, 2 * totalWidth, smallBarHeight, clamp(XP.current / XP.max, 0, 1), next);
height += smallBarHeight - barPad;
}
if (Settings.playerTurretReloadBar) {
this.barContainer(ctx, x, y + height, 2 * totalWidth, smallBarHeight);
this.barContent(ctx, x, y + height, 2 * totalWidth, smallBarHeight, turret.current / turret.max, Settings.playerTurretReloadBarColor);
height += smallBarHeight - barPad;
}
if (Settings.weaponReloadBar) {
const extraPad = 2.25;
this.barContainer(ctx, x, y + height, 2 * totalWidth, barHeight);
this.barContent(ctx, x, y + height, totalWidth + extraPad, barHeight, primary.current / primary.max, Settings.weaponReloadBarColor);
this.barContent(ctx, x + totalWidth - extraPad, y + height, totalWidth + extraPad, barHeight, secondary.current / secondary.max, Settings.weaponReloadBarColor);
height += barHeight - barPad;
}
}
const target = player || animal;
if (target) {
window.config.nameY = this.getNameY(target);
const {currentHealth, maxHealth} = target;
const health = animal ? maxHealth : 100;
const color = PlayerManager.isEnemyTarget(myPlayer, target) ? "#cc5151" : "#8ecc51";
this.barContainer(ctx, x, y + height, 2 * totalWidth, barHeight);
this.barContent(ctx, x, y + height, 2 * totalWidth, barHeight, currentHealth / health, color);
height += barHeight;
}
ctx.restore();
}
static renderHP(ctx, entity) {
if (!Settings.renderHP) {
return;
}
const {barPad, nameY} = constants_Config;
const containerHeight = this.getContainerHeight(entity);
let text = `HP ${Math.floor(entity.health)}/${entity.maxHealth}`;
const offset = entity.scale + nameY + barPad + containerHeight;
const {myPlayer} = myClient;
const x = entity.x - myPlayer.offset.x;
const y = entity.y - myPlayer.offset.y + offset;
if (entity.isPlayer && myPlayer.isMyPlayerByID(entity.sid)) {
text += ` ${myPlayer.shameCount}/8`;
}
ctx.save();
ctx.fillStyle = "#fff";
ctx.strokeStyle = "#3d3f42";
ctx.lineWidth = 8;
ctx.lineJoin = "round";
ctx.textBaseline = "top";
ctx.font = `19px Hammersmith One`;
ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
ctx.restore();
}
static circularBar(ctx, object, perc, angle, color, offset = 0) {
const x = object.x + object.xWiggle - myClient.myPlayer.offset.x;
const y = object.y + object.yWiggle - myClient.myPlayer.offset.y;
const height = .7 * constants_Config.barHeight;
const defaultScale = 10 + height / 2;
const scale = defaultScale + 3 + offset;
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.lineCap = "round";
ctx.strokeStyle = "#3b3b3b";
ctx.lineWidth = height;
ctx.beginPath();
ctx.arc(0, 0, scale, 0, 2 * perc * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.strokeStyle = color;
ctx.lineWidth = height / 3;
ctx.beginPath();
ctx.arc(0, 0, scale, 0, 2 * perc * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.restore();
return defaultScale - 3;
}
}
const rendering_Renderer = Renderer;
const Animals = [ {
id: 0,
src: "cow_1",
hostile: false,
killScore: 150,
health: 500,
weightM: .8,
speed: 95e-5,
turnSpeed: .001,
scale: 72,
drop: [ "food", 50 ]
}, {
id: 1,
src: "pig_1",
hostile: false,
killScore: 200,
health: 800,
weightM: .6,
speed: 85e-5,
turnSpeed: .001,
scale: 72,
drop: [ "food", 80 ]
}, {
id: 2,
name: "Bull",
src: "bull_2",
hostile: true,
dmg: 20,
killScore: 1e3,
health: 1800,
weightM: .5,
speed: 94e-5,
turnSpeed: 74e-5,
scale: 78,
viewRange: 800,
chargePlayer: true,
drop: [ "food", 100 ]
}, {
id: 3,
name: "Bully",
src: "bull_1",
hostile: true,
dmg: 20,
killScore: 2e3,
health: 2800,
weightM: .45,
speed: .001,
turnSpeed: 8e-4,
scale: 90,
viewRange: 900,
chargePlayer: true,
drop: [ "food", 400 ]
}, {
id: 4,
name: "Wolf",
src: "wolf_1",
hostile: true,
dmg: 8,
killScore: 500,
health: 300,
weightM: .45,
speed: .001,
turnSpeed: .002,
scale: 84,
viewRange: 800,
chargePlayer: true,
drop: [ "food", 200 ]
}, {
id: 5,
name: "Quack",
src: "chicken_1",
hostile: false,
dmg: 8,
killScore: 2e3,
noTrap: true,
health: 300,
weightM: .2,
speed: .0018,
turnSpeed: .006,
scale: 70,
drop: [ "food", 100 ]
}, {
id: 6,
name: "MOOSTAFA",
nameScale: 50,
src: "enemy",
hostile: true,
dontRun: true,
fixedSpawn: true,
spawnDelay: 6e4,
noTrap: true,
colDmg: 100,
dmg: 40,
killScore: 8e3,
health: 18e3,
weightM: .4,
speed: 7e-4,
turnSpeed: .01,
scale: 80,
spriteMlt: 1.8,
leapForce: .9,
viewRange: 1e3,
hitRange: 210,
hitDelay: 1e3,
chargePlayer: true,
drop: [ "food", 100 ]
}, {
id: 7,
name: "Treasure",
hostile: true,
nameScale: 35,
src: "crate_1",
fixedSpawn: true,
spawnDelay: 12e4,
colDmg: 200,
killScore: 5e3,
health: 2e4,
weightM: .1,
speed: 0,
turnSpeed: 0,
scale: 70,
spriteMlt: 1
}, {
id: 8,
name: "MOOFIE",
src: "wolf_2",
hostile: true,
fixedSpawn: true,
dontRun: true,
hitScare: 4,
spawnDelay: 3e4,
noTrap: true,
nameScale: 35,
dmg: 10,
colDmg: 100,
killScore: 3e3,
health: 7e3,
weightM: .45,
speed: .0015,
turnSpeed: .002,
scale: 90,
viewRange: 800,
chargePlayer: true,
drop: [ "food", 1e3 ]
}, {
id: 9,
name: "💀MOOFIE",
src: "wolf_2",
hostile: !0,
fixedSpawn: !0,
dontRun: !0,
hitScare: 50,
spawnDelay: 6e4,
noTrap: !0,
nameScale: 35,
dmg: 12,
colDmg: 100,
killScore: 3e3,
health: 9e3,
weightM: .45,
speed: .0015,
turnSpeed: .0025,
scale: 94,
viewRange: 1440,
chargePlayer: !0,
drop: [ "food", 3e3 ],
minSpawnRange: .85,
maxSpawnRange: .9
}, {
id: 10,
name: "💀Wolf",
src: "wolf_1",
hostile: !0,
fixedSpawn: !0,
dontRun: !0,
hitScare: 50,
spawnDelay: 3e4,
dmg: 10,
killScore: 700,
health: 500,
weightM: .45,
speed: .00115,
turnSpeed: .0025,
scale: 88,
viewRange: 1440,
chargePlayer: !0,
drop: [ "food", 400 ],
minSpawnRange: .85,
maxSpawnRange: .9
}, {
id: 11,
name: "💀Bully",
src: "bull_1",
hostile: !0,
fixedSpawn: !0,
dontRun: !0,
hitScare: 50,
dmg: 20,
killScore: 5e3,
health: 5e3,
spawnDelay: 1e5,
weightM: .45,
speed: .00115,
turnSpeed: .0025,
scale: 94,
viewRange: 1440,
chargePlayer: !0,
drop: [ "food", 800 ],
minSpawnRange: .85,
maxSpawnRange: .9
} ];
const constants_Animals = Animals;
const colors = [ [ "orange", "red" ], [ "aqua", "blue" ] ];
const EntityRenderer = new class EntityRenderer {
start=Date.now();
step=0;
drawWeaponHitbox(ctx, player) {
if (!Settings.weaponHitbox) {
return;
}
const {myPlayer, ModuleHandler} = myClient;
const current = myPlayer.getItemByType(ModuleHandler.weapon);
if (utility_DataHandler.isMelee(current)) {
const weapon = Weapons[current];
rendering_Renderer.circle(ctx, player.x, player.y, weapon.range, "#f5cb42", 1, 1);
}
}
drawPlacement(ctx) {
if (!Settings.possiblePlacement) {
return;
}
const {myPlayer, ModuleHandler, ObjectManager} = myClient;
const id = myPlayer.getItemByType(7);
if (null === id) {
return;
}
const angles = ObjectManager.getBestPlacementAngles(myPlayer.position.current, id);
const dist = myPlayer.getItemPlaceScale(id);
const item = Items[id];
for (const angle of angles) {
const pos = myPlayer.position.current.direction(angle, dist);
rendering_Renderer.circle(ctx, pos.x, pos.y, item.scale, "purple", 1, 1);
}
}
drawEntityHP(ctx, entity) {
if (entity.isPlayer) {
if (Settings.turretHitbox && 53 === myClient.myPlayer.hatID) {
rendering_Renderer.circle(ctx, entity.x, entity.y, 700, "#3e2773", 1, 1);
}
}
rendering_Renderer.renderBar(ctx, entity);
rendering_Renderer.renderHP(ctx, entity);
}
drawHitScale(ctx, entity) {
if (!Settings.weaponHitbox) {
return;
}
const {PlayerManager} = myClient;
const type = entity.isPlayer ? PlayerManager.playerData : PlayerManager.animalData;
const target = type.get(entity.sid);
if (void 0 !== target) {
rendering_Renderer.circle(ctx, entity.x, entity.y, target.hitScale, "#3f4ec4", 1, 1);
}
if (entity.isAI && 6 === entity.index) {
const moostafa = constants_Animals[6];
rendering_Renderer.circle(ctx, entity.x, entity.y, moostafa.hitRange, "#f5cb42", 1, 1);
}
}
drawDanger(ctx, entity) {
if (!Settings.entityDanger) {
return;
}
const {PlayerManager} = myClient;
if (entity.isPlayer) {
const player = PlayerManager.playerData.get(entity.sid);
if (void 0 !== player && 0 !== player.danger) {
const isBoost = Number(player.usingBoost);
const isDanger = Number(player.danger >= 3);
rendering_Renderer.fillCircle(ctx, entity.x, entity.y, player.scale, colors[isBoost][isDanger], .35);
}
}
if (entity.isAI) {
const animal = PlayerManager.animalData.get(entity.sid);
const color = animal.isDanger ? "red" : "green";
rendering_Renderer.fillCircle(ctx, entity.x, entity.y, animal.attackRange, color, .3);
}
}
render(ctx, entity, player) {
const now = Date.now();
this.step = now - this.start;
this.start = now;
const {myPlayer, EnemyManager} = myClient;
const isMyPlayer = entity === player;
if (isMyPlayer) {
const pos = new modules_Vector(player.x, player.y);
if (Settings.displayPlayerAngle) {
rendering_Renderer.line(ctx, pos, pos.direction(myClient.myPlayer.angle, 70), "#e9adf0");
}
this.drawWeaponHitbox(ctx, player);
this.drawPlacement(ctx);
const secondary = myPlayer.weapon.current;
const enemy = EnemyManager.nearestEnemy;
if (Settings.projectileHitbox && utility_DataHandler.isShootable(secondary) && enemy) {
rendering_Renderer.circle(ctx, entity.x, entity.y, 700, "#3e2773", 1, 1);
}
if (myPlayer.isTrapped) {
rendering_Renderer.fillCircle(ctx, pos.x, pos.y, 35, "yellow", .5);
}
}
this.drawEntityHP(ctx, entity);
if (Settings.collisionHitbox) {
rendering_Renderer.circle(ctx, entity.x, entity.y, entity.scale, "#c7fff2", 1, 1);
}
if (!isMyPlayer) {
const willCollide = EnemyManager.nearestCollideSpike;
if (willCollide && !entity.isAI && myPlayer.isEnemyByID(entity.sid) && entity.sid === willCollide.id) {
rendering_Renderer.circle(ctx, entity.x, entity.y, entity.scale, "#691313", 1, 13);
}
this.drawHitScale(ctx, entity);
this.drawDanger(ctx, entity);
rendering_Renderer.renderTracer(ctx, entity, player);
}
if (isMyPlayer) {
rendering_NotificationRenderer.render(ctx, player);
}
}
};
const rendering_EntityRenderer = EntityRenderer;
class Notification {
x;
y;
timeout={
value: 0,
max: 1500
};
constructor(x, y) {
this.x = x;
this.y = y;
}
animate() {
const {value, max} = this.timeout;
if (value >= max) {
NotificationRenderer.remove(this);
return;
}
this.timeout.value += rendering_EntityRenderer.step;
}
render(ctx, player) {
this.animate();
rendering_Renderer.renderTracer(ctx, this, player);
}
}
const NotificationRenderer = new class NotificationRenderer {
notifications=new Set;
remove(notify) {
this.notifications.delete(notify);
}
add(object) {
const {x, y} = object.position.current;
const notify = new Notification(x, y);
this.notifications.add(notify);
}
render(ctx, player) {
for (const notification of this.notifications) {
notification.render(ctx, player);
}
}
};
const rendering_NotificationRenderer = NotificationRenderer;
class ObjectManager {
objects=new Map;
grid=new modules_SpatialHashGrid(100);
reloadingTurrets=new Map;
attackedObjects=new Map;
client;
constructor(client) {
this.client = client;
}
insertObject(object) {
this.grid.insert(object);
this.objects.set(object.id, object);
if (object instanceof PlayerObject) {
const {PlayerManager} = this.client;
const owner = PlayerManager.playerData.get(object.ownerID) || PlayerManager.createPlayer({
id: object.ownerID
});
object.seenPlacement = this.inPlacementRange(object);
owner.handleObjectPlacement(object);
}
}
createObjects(buffer) {
for (let i = 0; i < buffer.length; i += 8) {
const isResource = null === buffer[i + 6];
const data = [ buffer[i + 0], buffer[i + 1], buffer[i + 2], buffer[i + 3], buffer[i + 4] ];
this.insertObject(isResource ? new Resource(...data, buffer[i + 5]) : new PlayerObject(...data, buffer[i + 6], buffer[i + 7]));
}
}
removeObject(object) {
this.grid.remove(object);
this.objects.delete(object.id);
if (object instanceof PlayerObject) {
const player = this.client.PlayerManager.playerData.get(object.ownerID);
if (void 0 !== player) {
player.handleObjectDeletion(object);
}
}
}
removeObjectByID(id) {
const object = this.objects.get(id);
if (void 0 !== object) {
this.removeObject(object);
if (this.client.isOwner) {
const pos = object.position.current.copy().sub(this.client.myPlayer.offset);
if (Settings.notificationTracers && !inView(pos.x, pos.y, object.scale)) {
rendering_NotificationRenderer.add(object);
}
}
}
}
removePlayerObjects(player) {
for (const object of player.objects) {
this.removeObject(object);
}
}
resetTurret(id) {
const object = this.objects.get(id);
if (object instanceof PlayerObject) {
object.reload = 0;
this.reloadingTurrets.set(id, object);
}
}
isEnemyObject(object) {
if (object instanceof PlayerObject && !this.client.myPlayer.isEnemyByID(object.ownerID)) {
return false;
}
return true;
}
isTurretReloaded(object) {
const turret = this.reloadingTurrets.get(object.id);
if (void 0 === turret) {
return true;
}
const tick = this.client.SocketManager.TICK;
return turret.reload > turret.maxReload - tick;
}
postTick() {
for (const [id, turret] of this.reloadingTurrets) {
turret.reload += this.client.PlayerManager.step;
if (turret.reload >= turret.maxReload) {
turret.reload = turret.maxReload;
this.reloadingTurrets.delete(id);
}
}
}
retrieveObjects(pos, radius) {
return this.grid.retrieve(pos, radius);
}
canPlaceItem(id, position, addRadius = 0) {
if (18 !== id && pointInRiver(position)) {
return false;
}
const item = Items[id];
const objects = this.retrieveObjects(position, item.scale);
for (const object of objects) {
const scale = item.scale + object.placementScale + addRadius;
if (position.distance(object.position.current) < scale) {
return false;
}
}
return true;
}
inPlacementRange(object) {
const owner = this.client.PlayerManager.playerData.get(object.ownerID);
if (void 0 === owner || !this.client.PlayerManager.players.includes(owner)) {
return false;
}
const {previous: a0, current: a1, future: a2} = owner.position;
const b0 = object.position.current;
const item = Items[object.type];
const range = 2 * owner.scale + item.scale + item.placeOffset;
return a0.distance(b0) <= range || a1.distance(b0) <= range || a2.distance(b0) <= range;
}
getAngleOffset(angle, distance, scale) {}
getBestPlacementAngles(position, id, sortAngle = 0) {
const item = Items[id];
const length = this.client.myPlayer.getItemPlaceScale(id);
const objects = this.retrieveObjects(position, length + item.scale);
const angles = [];
for (const object of objects) {
const angle = position.angle(object.position.current);
const distance = position.distance(object.position.current);
const a = object.placementScale + item.scale;
const b = distance;
const c = length;
const offset = Math.acos((a ** 2 - b ** 2 - c ** 2) / (-2 * b * c));
if (!isNaN(offset)) {
angles.push({
angle,
offset
});
}
}
return findPlacementAngles(angles);
}
}
const Managers_ObjectManager = ObjectManager;
class Animal extends data_Entity {
type=-1;
currentHealth=0;
_maxHealth=0;
nameIndex=0;
isDanger=false;
isHostile=false;
constructor(client) {
super(client);
}
get maxHealth() {
return this._maxHealth;
}
canBeTrapped() {
return !("noTrap" in constants_Animals[this.type]);
}
update(id, type, x, y, angle, health, nameIndex) {
this.id = id;
this.type = type;
this.position.previous.setVec(this.position.current);
this.position.current.setXY(x, y);
this.setFuturePosition();
const animal = constants_Animals[type];
this.angle = angle;
this.currentHealth = health;
this._maxHealth = animal.health;
this.nameIndex = nameIndex;
this.scale = animal.scale;
const isHostile = animal.hostile && 7 !== type;
const isTrapped = this.canBeTrapped() && this.checkCollision(5);
this.isHostile = animal.hostile;
this.isDanger = isHostile && !isTrapped;
}
get attackRange() {
if (6 === this.type) {
return constants_Animals[this.type].hitRange + constants_Config.playerScale;
}
return this.scale;
}
get collisionRange() {
if (6 === this.type) {
return constants_Animals[this.type].hitRange + constants_Config.playerScale;
}
return this.scale + 60;
}
get canUseTurret() {
return this.isHostile;
}
}
const data_Animal = Animal;
class ClientPlayer extends data_Player {
inventory={};
weaponXP=[ {}, {} ];
itemCount=new Map;
resources={};
tempGold=0;
deathPosition=new modules_Vector;
offset=new modules_Vector;
inGame=false;
wasDead=true;
diedOnce=false;
platformActivated=false;
receivedDamage=null;
timerCount=1e3 / 9;
shameActive=false;
shameTimer=0;
shameCount=0;
teammates=new Set;
totalGoldAmount=0;
age=1;
upgradeAge=1;
poisonCount=0;
underTurretAttack=false;
upgradeOrder=[];
upgradeIndex=0;
joinRequests=[];
constructor(client) {
super(client);
this.reset(true);
}
isMyPlayerByID(id) {
return id === this.id;
}
isTeammateByID(id) {
return this.teammates.has(id);
}
isEnemyByID(id) {
return !this.isMyPlayerByID(id) && !this.isTeammateByID(id);
}
get isSandbox() {
return true;
}
getItemByType(type) {
return this.inventory[type];
}
hasResourcesForType(type) {
if (this.isSandbox) {
return true;
}
const res = this.resources;
const {food, wood, stone, gold} = Items[this.getItemByType(type)].cost;
return res.food >= food && res.wood >= wood && res.stone >= stone && res.gold >= gold;
}
getItemCount(group) {
const item = ItemGroups[group];
return {
count: this.itemCount.get(group) || 0,
limit: this.isSandbox ? "sandboxLimit" in item ? item.sandboxLimit : 99 : item.limit
};
}
hasItemCountForType(type) {
if (2 === type) {
return true;
}
const item = Items[this.getItemByType(type)];
const {count, limit} = this.getItemCount(item.itemGroup);
return count < limit;
}
canPlace(type) {
return null !== type && null !== this.getItemByType(type) && this.hasResourcesForType(type) && this.hasItemCountForType(type);
}
getBestDestroyingWeapon() {
const secondaryID = this.getItemByType(1);
if (10 === secondaryID) {
return 1;
}
const primary = Weapons[this.getItemByType(0)];
if (1 !== primary.damage) {
return 0;
}
return null;
}
getDmgOverTime() {
const hat = Hats[this.hatID];
const accessory = Accessories[this.accessoryID];
let damage = 0;
if ("healthRegen" in hat) {
damage += hat.healthRegen;
}
if ("healthRegen" in accessory) {
damage += accessory.healthRegen;
}
if (0 !== this.poisonCount) {
damage += -5;
}
return Math.abs(damage);
}
getBestCurrentHat() {
const {current, future} = this.position;
const {ModuleHandler, EnemyManager} = this.client;
const {actual} = ModuleHandler.getHatStore();
const useFlipper = ModuleHandler.canBuy(0, 31);
const useSoldier = ModuleHandler.canBuy(0, 6);
const useWinter = ModuleHandler.canBuy(0, 15);
const useActual = ModuleHandler.canBuy(0, actual);
if (Settings.biomehats && useFlipper) {
const inRiver = pointInRiver(current) || pointInRiver(future);
if (inRiver) {
const platformActivated = this.checkCollision(8, -30);
const stillStandingOnPlatform = this.checkCollision(8, 15);
if (!this.platformActivated && platformActivated) {
this.platformActivated = true;
}
if (this.platformActivated && !stillStandingOnPlatform) {
this.platformActivated = false;
}
if (!this.platformActivated) {
return 31;
}
}
}
if (useSoldier) {
if (Settings.antienemy && (EnemyManager.detectedEnemy || EnemyManager.nearestEnemyInRangeOf(275))) {
return 6;
}
if (Settings.antispike && this.checkCollision(2, 35, true)) {
return 6;
}
if (Settings.antianimal && null !== EnemyManager.nearestDangerAnimal) {
return 6;
}
}
if (Settings.biomehats && useWinter) {
const inWinter = current.y <= 2400 || future.y <= 2400;
if (inWinter) {
return 15;
}
}
if (useActual) {
return actual;
}
return 0;
}
getBestCurrentAcc() {
const {ModuleHandler, EnemyManager} = this.client;
const {actual} = ModuleHandler.getAccStore();
const useCorrupt = ModuleHandler.canBuy(1, 21);
const useShadow = ModuleHandler.canBuy(1, 19);
const useTail = ModuleHandler.canBuy(1, 11);
const useActual = ModuleHandler.canBuy(1, actual);
if (EnemyManager.detectedEnemy || EnemyManager.nearestEnemyInRangeOf(275, EnemyManager.nearestEntity)) {
const isEnemy = EnemyManager.nearestEnemyInRangeOf(275, EnemyManager.nearestEnemy);
if (isEnemy && useCorrupt) {
return 21;
}
if (useShadow) {
return 19;
}
if (useActual && 11 !== actual) {
return actual;
}
return 0;
}
if (useTail) {
return 11;
}
return 0;
}
getBestCurrentID(type) {
switch (type) {
case 0:
return this.getBestCurrentHat();
case 1:
return this.getBestCurrentAcc();
}
}
getBestUtilityHat() {
const {ModuleHandler, EnemyManager, ObjectManager, myPlayer} = this.client;
const {autoBreak, spikeTick} = ModuleHandler.staticModules;
const id = this.getItemByType(ModuleHandler.weapon);
if (11 === id) {
return null;
}
if (utility_DataHandler.isShootable(id)) {
return 20;
}
const weapon = Weapons[id];
const range = weapon.range + 60;
if (spikeTick.isActive && 1 === spikeTick.tickAction) {
return 53;
}
if (1 === ModuleHandler.attackingState || spikeTick.isActive) {
const nearest = EnemyManager.nearestEntity;
if (null !== nearest && this.collidingEntity(nearest, range + nearest.hitScale, true)) {
ModuleHandler.canHitEntity = true;
if (weapon.damage <= 1) {
return 20;
}
return 7;
}
}
if (0 !== ModuleHandler.attackingState || autoBreak.isActive) {
if (weapon.damage <= 1) {
return null;
}
const pos = myPlayer.position.current;
const objects = ObjectManager.retrieveObjects(pos, range);
for (const object of objects) {
if (object instanceof PlayerObject && object.isDestroyable && this.colliding(object, range + object.hitScale)) {
return 40;
}
}
}
return null;
}
getBestUtilityAcc() {
return null;
}
getBestUtilityID(type) {
switch (type) {
case 0:
return this.getBestUtilityHat();
case 1:
return this.getBestUtilityAcc();
}
}
getMaxWeaponRangeClient() {
const primary = this.inventory[0];
const secondary = this.inventory[1];
const primaryRange = Weapons[primary].range;
if (utility_DataHandler.isMelee(secondary)) {
const range = Weapons[secondary].range;
if (range > primaryRange) {
return range;
}
}
return primaryRange;
}
getPlacePosition(start, itemID, angle) {
return start.direction(angle, this.getItemPlaceScale(itemID));
}
tickUpdate() {
if (this.inGame && this.wasDead) {
this.wasDead = false;
this.onFirstTickAfterSpawn();
}
if (45 === this.hatID && !this.shameActive) {
this.shameActive = true;
this.shameTimer = 0;
this.shameCount = 8;
}
const {PlayerManager, ModuleHandler} = this.client;
this.shameTimer += PlayerManager.step;
if (this.shameTimer >= 3e4 && this.shameActive) {
this.shameActive = false;
this.shameTimer = 0;
this.shameCount = 0;
}
this.timerCount += PlayerManager.step;
if (this.timerCount >= 1e3) {
this.timerCount = 0;
this.poisonCount = Math.max(this.poisonCount - 1, 0);
}
ModuleHandler.postTick();
}
updateHealth(health) {
super.updateHealth(health);
if (this.shameActive) {
return;
}
if (this.currentHealth < this.previousHealth) {
this.receivedDamage = Date.now();
} else if (null !== this.receivedDamage) {
const step = Date.now() - this.receivedDamage;
this.receivedDamage = null;
if (step <= 120) {
this.shameCount += 1;
} else {
this.shameCount -= 2;
}
this.shameCount = clamp(this.shameCount, 0, 7);
}
if (health < 100) {
const {ModuleHandler} = this.client;
ModuleHandler.staticModules.shameReset.healthUpdate();
}
}
playerInit(id) {
this.id = id;
const {PlayerManager} = this.client;
if (!PlayerManager.playerData.has(id)) {
PlayerManager.playerData.set(id, this);
}
}
onFirstTickAfterSpawn() {
const {ModuleHandler, SocketManager, isOwner} = this.client;
const {mouse, staticModules} = ModuleHandler;
ModuleHandler.updateAngle(mouse.sentAngle, true);
if (myClient.ModuleHandler.autoattack) {
ModuleHandler.autoattack = true;
SocketManager.autoAttack();
}
if (!isOwner) {
UI_UI.updateBotOption(this.client, "title");
myClient.clientIDList.add(this.id);
const owner = myClient.ModuleHandler;
staticModules.tempData.setWeapon(owner.weapon);
staticModules.tempData.setAttacking(owner.attacking);
staticModules.tempData.setStore(0, owner.store[0].actual);
staticModules.tempData.setStore(1, owner.store[1].actual);
}
}
playerSpawn() {
this.inGame = true;
}
isUpgradeWeapon(id) {
const weapon = Weapons[id];
if ("upgradeOf" in weapon) {
return this.inventory[weapon.itemType] === weapon.upgradeOf;
}
return true;
}
newUpgrade(points, age) {
this.upgradeAge = age;
if (0 === points || 10 === age) {
return;
}
const ids = [];
for (const weapon of Weapons) {
if (weapon.age === age && this.isUpgradeWeapon(weapon.id)) {
ids.push(weapon.id);
}
}
for (const item of Items) {
if (item.age === age) {
ids.push(item.id + 16);
}
}
if (!this.client.isOwner) {
const id = myClient.myPlayer.upgradeOrder[this.upgradeIndex];
if (void 0 !== id && ids.includes(id)) {
this.upgradeIndex += 1;
this.client.ModuleHandler.upgradeItem(id);
}
}
}
updateAge(age) {
this.age = age;
}
upgradeItem(id) {
this.upgradeOrder.push(id);
const {isOwner, clients} = this.client;
if (isOwner) {
for (const client of clients) {
const {age, upgradeAge} = client.myPlayer;
if (age > this.upgradeAge) {
client.myPlayer.newUpgrade(1, upgradeAge);
}
}
}
if (id < 16) {
const weapon = Weapons[id];
this.inventory[weapon.itemType] = id;
const XP = this.weaponXP[weapon.itemType];
XP.current = 0;
XP.max = -1;
} else {
id -= 16;
const item = Items[id];
this.inventory[item.itemType] = id;
}
}
updateClanMembers(teammates) {
this.teammates.clear();
for (let i = 0; i < teammates.length; i += 2) {
const id = teammates[i + 0];
if (!this.isMyPlayerByID(id)) {
this.teammates.add(id);
}
}
}
updateItemCount(group, count) {
this.itemCount.set(group, count);
if (this.client.isOwner) {
UI_GameUI.updateItemCount(group);
}
}
updateResources(type, amount) {
const previousAmount = this.resources[type];
this.resources[type] = amount;
if ("gold" === type) {
this.tempGold = amount;
return;
}
if (amount < previousAmount) {
return;
}
const difference = amount - previousAmount;
if ("kills" === type) {
myClient.totalKills += difference;
UI_GameUI.updateTotalKill();
return;
}
this.updateWeaponXP(difference);
}
updateWeaponXP(amount) {
const {next} = this.getWeaponVariant(this.weapon.current);
const XP = this.weaponXP[Weapons[this.weapon.current].itemType];
const maxXP = WeaponVariants[next].needXP;
XP.current += amount;
if (-1 !== XP.max && XP.current >= XP.max) {
XP.current -= XP.max;
XP.max = maxXP;
return;
}
if (-1 === XP.max) {
XP.max = maxXP;
}
if (XP.current >= XP.max) {
XP.current -= XP.max;
XP.max = -1;
}
}
resetResources() {
this.resources.food = 100;
this.resources.wood = 100;
this.resources.stone = 100;
this.resources.gold = 100;
this.resources.kills = 0;
}
resetInventory() {
this.inventory[0] = 0;
this.inventory[1] = null;
this.inventory[2] = 0;
this.inventory[3] = 3;
this.inventory[4] = 6;
this.inventory[5] = 10;
this.inventory[6] = null;
this.inventory[7] = null;
this.inventory[8] = null;
this.inventory[9] = null;
}
resetWeaponXP() {
for (const XP of this.weaponXP) {
XP.current = 0;
XP.max = -1;
}
}
spawn() {
const name = localStorage.getItem("moo_name") || "";
const skin = Number(localStorage.getItem("skin_color")) || 0;
this.client.SocketManager.spawn(name, 1, 10 === skin ? "constructor" : skin);
}
handleDeath() {
if (Settings.autospawn) {
this.spawn();
return true;
}
return false;
}
handleJoinRequest(id, name) {
this.joinRequests.push([ id, name ]);
}
reset(first = false) {
this.resetResources();
this.resetInventory();
this.resetWeaponXP();
const {ModuleHandler, PlayerManager} = this.client;
ModuleHandler.reset();
this.inGame = false;
this.wasDead = true;
this.shameTimer = 0;
this.shameCount = 0;
this.upgradeOrder.length = 0;
this.upgradeIndex = 0;
if (first) {
return;
}
for (const player of PlayerManager.players) {
player.resetReload();
}
this.deathPosition.setVec(this.position.current);
this.diedOnce = true;
if (this.client.isOwner) {
UI_GameUI.reset();
} else {
this.spawn();
}
}
}
const data_ClientPlayer = ClientPlayer;
class PlayerManager {
playerData=new Map;
players=[];
animalData=new Map;
animals=[];
start=Date.now();
step=0;
client;
constructor(client) {
this.client = client;
}
get timeSinceTick() {
return Date.now() - this.start;
}
createPlayer({socketID, id, nickname, health, skinID}) {
const player = this.playerData.get(id) || new data_Player(this.client);
if (!this.playerData.has(id)) {
this.playerData.set(id, player);
}
player.socketID = socketID || "";
player.id = id;
player.nickname = nickname || "";
player.currentHealth = health || 100;
player.skinID = "undefined" === typeof skinID ? -1 : skinID;
player.init();
const {myPlayer} = this.client;
if (myPlayer.isMyPlayerByID(id)) {
myPlayer.playerSpawn();
}
return player;
}
canHitTarget(player, weaponID, target) {
const pos = target.position.current;
const distance = player.position.current.distance(pos);
const angle = player.position.current.angle(pos);
const range = Weapons[weaponID].range + target.hitScale;
return distance <= range && getAngleDist(angle, player.angle) <= constants_Config.gatherAngle;
}
attackPlayer(id, gathering, weaponID) {
const player = this.playerData.get(id);
if (void 0 === player) {
return;
}
const {hatID, reload} = player;
const {myPlayer, ObjectManager} = this.client;
if (myPlayer.isMyPlayerByID(id) && !myPlayer.inGame) {
return;
}
const weapon = Weapons[weaponID];
const type = WeaponTypeString[weapon.itemType];
reload[type].current = 0;
reload[type].max = player.getWeaponSpeed(weaponID);
if (myPlayer.isEnemyByID(id) && this.canHitTarget(player, weaponID, myPlayer)) {
const {isAble, count} = player.canDealPoison(weaponID);
if (isAble) {
myPlayer.poisonCount = count;
}
}
if (1 === gathering) {
const objects = ObjectManager.attackedObjects;
for (const [id, data] of objects) {
const [hitAngle, object] = data;
if (this.canHitTarget(player, weaponID, object) && getAngleDist(hitAngle, player.angle) <= 1.25) {
objects.delete(id);
if (object instanceof PlayerObject) {
const damage = player.getBuildingDamage(weaponID);
object.health = Math.max(0, object.health - damage);
} else if (player === myPlayer) {
let amount = 9 === hatID ? 1 : 0;
if (3 === object.type) {
amount += weapon.gather + 4;
}
myPlayer.updateWeaponXP(amount);
}
}
}
}
}
updatePlayer(buffer) {
this.players.length = 0;
const now = Date.now();
this.step = now - this.start;
this.start = now;
for (let i = 0; i < buffer.length; i += 13) {
const id = buffer[i];
const player = this.playerData.get(id);
if (!player) {
continue;
}
this.players.push(player);
player.update(id, buffer[i + 1], buffer[i + 2], buffer[i + 3], buffer[i + 4], buffer[i + 5], buffer[i + 6], buffer[i + 7], buffer[i + 8], buffer[i + 9], buffer[i + 10], buffer[i + 11]);
}
}
updateAnimal(buffer) {
this.animals.length = 0;
for (let i = 0; i < buffer.length; i += 7) {
const id = buffer[i];
if (!this.animalData.has(id)) {
this.animalData.set(id, new data_Animal(this.client));
}
const animal = this.animalData.get(id);
this.animals.push(animal);
animal.update(id, buffer[i + 1], buffer[i + 2], buffer[i + 3], buffer[i + 4], buffer[i + 5], buffer[i + 6]);
}
}
postTick() {
const {EnemyManager, ProjectileManager, ObjectManager, myPlayer} = this.client;
EnemyManager.handleEnemies(this.players, this.animals);
ProjectileManager.postTick();
ObjectManager.postTick();
if (myPlayer.inGame) {
myPlayer.tickUpdate();
}
}
isEnemy(target1, target2) {
return target1 !== target2 && (null === target1.clanName || null === target2.clanName || target1.clanName !== target2.clanName);
}
isEnemyByID(ownerID, target) {
const player = this.playerData.get(ownerID);
if (player instanceof data_ClientPlayer) {
return player.isEnemyByID(target.id);
}
if (target instanceof data_ClientPlayer) {
return target.isEnemyByID(player.id);
}
return this.isEnemy(player, target);
}
isEnemyTarget(owner, target) {
if (target instanceof data_Animal) {
return true;
}
return this.isEnemyByID(owner.id, target);
}
canShoot(ownerID, target) {
return target instanceof data_Animal || this.isEnemyByID(ownerID, target);
}
lookingShield(owner, target) {
const weapon = owner.weapon.current;
if (11 !== weapon) {
return false;
}
const {myPlayer, ModuleHandler} = this.client;
const pos1 = owner.position.current;
const pos2 = target.position.current;
const angle = pos1.angle(pos2);
const ownerAngle = myPlayer.isMyPlayerByID(owner.id) ? ModuleHandler.mouse.sentAngle : owner.angle;
return getAngleDist(angle, ownerAngle) <= constants_Config.shieldAngle;
}
getEntities() {
return [ ...this.players, ...this.animals ];
}
}
const Managers_PlayerManager = PlayerManager;
class Projectile {
position={};
angle;
range;
speed;
type;
onPlatform;
id;
isTurret;
scale;
maxRange;
owner=null;
constructor(angle, range, speed, type, onPlatform, id, maxRange) {
this.isTurret = 1 === type;
this.angle = angle;
this.range = range;
this.speed = speed;
this.type = type;
this.onPlatform = onPlatform;
this.id = id;
this.scale = Projectiles[type].scale;
this.maxRange = maxRange || 0;
}
formatFromCurrent(pos, increase) {
if (this.isTurret) {
return pos;
}
return pos.direction(this.angle, increase ? 70 : -70);
}
}
const data_Projectile = Projectile;
class ProjectileManager {
client;
projectiles=new Map;
ignoreCreation=new Set;
constructor(client) {
this.client = client;
}
createProjectile(projectile) {
const key = projectile.speed;
if (!this.projectiles.has(key)) {
this.projectiles.set(key, []);
}
const list = this.projectiles.get(key);
list.push(projectile);
}
shootingAt(owner, target) {}
postTick() {
this.projectiles.clear();
}
getProjectile(owner, projectile, onPlatform, angle, range) {
const bullet = Projectiles[projectile];
const isTurret = 1 === projectile;
const {previous: a0, current: a1, future: a2} = owner.position;
const arrow = new data_Projectile(angle, bullet.range, bullet.speed, projectile, onPlatform || isTurret ? 1 : 0, -1, range);
arrow.position.previous = arrow.formatFromCurrent(a0, true);
arrow.position.current = arrow.formatFromCurrent(a1, true);
arrow.position.future = arrow.formatFromCurrent(a2, true);
return arrow;
}
}
const Managers_ProjectileManager = ProjectileManager;
class SocketManager {
client;
PacketQueue=[];
startPing=Date.now();
ping=0;
pong=0;
TICK=1e3 / 9;
packetCount=0;
tickTimeout;
constructor(client) {
this.message = this.message.bind(this);
this.client = client;
const attachMessage = socket => {
socket.addEventListener("message", this.message);
socket.onclose = () => {
socket.removeEventListener("message", this.message);
};
};
const connection = client.connection;
if (void 0 === connection.socket) {
Object.defineProperty(connection, "socket", {
set(value) {
delete connection.socket;
connection.socket = value;
attachMessage(value);
},
configurable: true
});
return;
}
attachMessage(connection.socket);
}
handlePing() {
this.pong = Date.now() - this.startPing;
this.ping = this.pong / 2;
if (this.client.isOwner) {
UI_GameUI.updatePing(this.pong);
}
setTimeout((() => {
this.pingRequest();
}), 3e3);
}
message(event) {
const decoder = this.client.connection.Decoder;
if (null === decoder) {
return;
}
const data = event.data;
const decoded = decoder.decode(new Uint8Array(data));
const temp = [ decoded[0], ...decoded[1] ];
const {myPlayer, PlayerManager, ObjectManager, ProjectileManager, LeaderboardManager} = this.client;
switch (temp[0]) {
case "0":
this.handlePing();
break;
case "io-init":
this.pingRequest();
this.client.stableConnection = true;
if (this.client.isOwner) {
UI_GameUI.loadGame();
} else {
this.client.myPlayer.spawn();
this.client.connection.socket.dispatchEvent(new Event("connected"));
}
break;
case "C":
myPlayer.playerInit(temp[1]);
break;
case "P":
myPlayer.reset();
break;
case "N":
this.PacketQueue.push((() => {
const type = "points" === temp[1] ? "gold" : temp[1];
myPlayer.updateResources(type, temp[2]);
}));
break;
case "D":
{
const data = temp[1];
PlayerManager.createPlayer({
socketID: data[0],
id: data[1],
nickname: data[2],
health: data[6],
skinID: data[9]
});
break;
}
case "O":
{
const player = PlayerManager.playerData.get(temp[1]);
if (void 0 !== player) {
player.updateHealth(temp[2]);
}
break;
}
case "a":
PlayerManager.updatePlayer(temp[1]);
for (let i = 0; i < this.PacketQueue.length; i++) {
this.PacketQueue[i]();
}
this.PacketQueue.length = 0;
ObjectManager.attackedObjects.clear();
break;
case "I":
PlayerManager.updateAnimal(temp[1] || []);
clearTimeout(this.tickTimeout);
this.tickTimeout = setTimeout((() => {
PlayerManager.postTick();
}), 5);
break;
case "H":
ObjectManager.createObjects(temp[1]);
break;
case "Q":
ObjectManager.removeObjectByID(temp[1]);
break;
case "R":
{
const player = PlayerManager.playerData.get(temp[1]);
if (void 0 !== player) {
ObjectManager.removePlayerObjects(player);
}
break;
}
case "L":
{
const object = ObjectManager.objects.get(temp[2]);
if (object instanceof Resource || object && object.isDestroyable) {
ObjectManager.attackedObjects.set(getUniqueID(), [ temp[1], object ]);
}
break;
}
case "K":
this.PacketQueue.push((() => PlayerManager.attackPlayer(temp[1], temp[2], temp[3])));
break;
case "M":
{
const id = temp[1];
const angle = temp[2];
const turret = ObjectManager.objects.get(id);
if (void 0 !== turret) {
const creations = ProjectileManager.ignoreCreation;
const pos = turret.position.current.stringify();
creations.add(pos + ":" + angle);
}
this.PacketQueue.push((() => ObjectManager.resetTurret(id)));
break;
}
case "X":
{
const x = temp[1];
const y = temp[2];
const angle = temp[3];
const key = `${x}:${y}:${angle}`;
if (ProjectileManager.ignoreCreation.delete(key)) {
return;
}
const projectile = new data_Projectile(angle, temp[4], temp[5], temp[6], temp[7], temp[8]);
projectile.position.current = projectile.formatFromCurrent(new modules_Vector(x, y), false);
ProjectileManager.createProjectile(projectile);
break;
}
case "4":
myPlayer.updateClanMembers(temp[1]);
break;
case "3":
if ("string" !== typeof temp[1]) {
myPlayer.teammates.clear();
}
break;
case "2":
myPlayer.handleJoinRequest(temp[1], temp[2]);
break;
case "T":
if (4 === temp.length) {
myPlayer.updateAge(temp[3]);
}
break;
case "U":
myPlayer.newUpgrade(temp[1], temp[2]);
break;
case "S":
myPlayer.updateItemCount(temp[1], temp[2]);
break;
case "G":
LeaderboardManager.update(temp[1]);
break;
case "5":
{
const action = 0 === temp[1] ? 1 : 0;
UI_StoreHandler.updateStoreState(temp[3], action, temp[2]);
break;
}
}
}
send(data) {
const connection = this.client.connection;
if (void 0 === connection.socket || connection.socket.readyState !== connection.socket.OPEN || null === connection.Encoder) {
return;
}
const [type, ...args] = data;
const encoded = connection.Encoder.encode([ type, args ]);
connection.socket.send(encoded);
}
clanRequest(id, accept) {
this.send([ "P", id, Number(accept) ]);
}
kick(id) {
this.send([ "Q", id ]);
}
joinClan(name) {
this.send([ "b", name ]);
}
createClan(name) {
this.send([ "L", name ]);
}
leaveClan() {
this.client.myPlayer.joinRequests.length = 0;
this.send([ "N" ]);
}
equip(type, id) {
this.send([ "c", 0, id, type ]);
}
buy(type, id) {
this.send([ "c", 1, id, type ]);
}
chat(message) {
this.send([ "6", message ]);
}
attack(angle) {
this.send([ "F", 1, angle ]);
}
stopAttack() {
this.send([ "F", 0, null ]);
}
resetMoveDir() {
this.send([ "e" ]);
}
move(angle) {
this.send([ "9", angle ]);
}
autoAttack() {
this.send([ "K", 1 ]);
}
lockRotation() {
this.send([ "K", 0 ]);
}
pingMap() {
this.send([ "S" ]);
}
selectItemByID(id, type) {
this.send([ "z", id, type ]);
}
spawn(name, moofoll, skin) {
this.send([ "M", {
name,
moofoll,
skin
} ]);
}
upgradeItem(id) {
this.send([ "H", id ]);
}
updateAngle(radians) {
this.send([ "D", radians ]);
}
pingRequest() {
this.startPing = Date.now();
this.send([ "0" ]);
}
}
const Managers_SocketManager = SocketManager;
class ActionPlanner {
actionKeys=[];
actionValues=[];
createAction(key, value) {
this.actionKeys.push(key);
this.actionValues.push(value);
}
createActions(key, value, amount) {
if (1 === amount) {
return this.createAction(key, value);
}
for (let i = 0; i < amount; i++) {
this.createAction(key, value);
}
}
getActions() {
const keys = [ ...this.actionKeys ];
const values = [ ...this.actionValues ];
const uniqueItems = [ ...new Set(keys) ];
const output = [];
while (keys.length > 0) {
for (const item of uniqueItems) {
const index = keys.indexOf(item);
if (index >= 0) {
output.push([ item, values[index] ]);
removeFast(keys, index);
removeFast(values, index);
}
}
}
this.actionKeys.length = 0;
this.actionValues.length = 0;
return output;
}
}
const modules_ActionPlanner = ActionPlanner;
class AntiInsta {
name="antiInsta";
client;
toggleAnti=false;
constructor(client) {
this.client = client;
}
get isSaveHeal() {
const {myPlayer, SocketManager} = this.client;
const startHit = myPlayer.receivedDamage || 0;
const timeSinceHit = Date.now() - startHit + SocketManager.pong;
return timeSinceHit >= 120;
}
get canHeal() {
const {myPlayer} = this.client;
return Settings.autoheal && myPlayer.tempHealth < 100 && !myPlayer.shameActive && this.isSaveHeal;
}
postTick() {
const {myPlayer, ModuleHandler} = this.client;
const foodID = myPlayer.getItemByType(2);
const restore = Items[foodID].restore;
const maxTimes = Math.ceil(myPlayer.maxHealth / restore);
const needTimes = Math.ceil((myPlayer.maxHealth - myPlayer.tempHealth) / restore);
let healingTimes = null;
if (ModuleHandler.needToHeal || this.toggleAnti) {
ModuleHandler.needToHeal = false;
if (myPlayer.shameActive) {
return;
}
ModuleHandler.didAntiInsta = true;
healingTimes = Math.min(maxTimes, 3);
} else if (this.canHeal) {
healingTimes = needTimes;
myPlayer.tempHealth += clamp(restore * healingTimes, 0, 100);
}
if (null !== healingTimes) {
ModuleHandler.healedOnce = true;
ModuleHandler.actionPlanner.createActions(2, (last => ModuleHandler.heal(last)), healingTimes);
}
}
}
const modules_AntiInsta = AntiInsta;
class AutoPlacer {
name="autoPlacer";
client;
placeAngles=[ null, new Set ];
constructor(client) {
this.client = client;
}
postTick() {
this.placeAngles[0] = null;
this.placeAngles[1].clear();
if (!Settings.autoplacer) {
return;
}
const {myPlayer, ObjectManager, ModuleHandler, EnemyManager} = this.client;
const {currentType} = ModuleHandler;
const pos = myPlayer.position.current;
const nearestEnemy = EnemyManager.nearestEnemy;
if (null === nearestEnemy) {
return;
}
if (!myPlayer.collidingEntity(nearestEnemy, 450)) {
return;
}
const nearestAngle = pos.angle(nearestEnemy.position.current);
let itemType = null;
const spike = myPlayer.getItemByType(4);
const spikeAngles = ObjectManager.getBestPlacementAngles(pos, spike, nearestAngle);
let angles = new Set;
const length = myPlayer.getItemPlaceScale(spike);
for (const angle of spikeAngles) {
const newPos = pos.direction(angle, length);
let shouldPlaceSpike = false;
for (const enemy of EnemyManager.trappedEnemies) {
const distance = newPos.distance(enemy.position.current);
const range = 2 * Items[spike].scale + enemy.collisionScale;
if (distance <= range) {
shouldPlaceSpike = true;
break;
}
}
if (shouldPlaceSpike) {
angles = spikeAngles;
itemType = 4;
break;
}
}
if (0 === angles.size) {
const type = currentType && 2 !== currentType ? currentType : 7;
if (!myPlayer.canPlace(type)) {
return;
}
const id = myPlayer.getItemByType(type);
angles = ObjectManager.getBestPlacementAngles(pos, id, nearestAngle);
itemType = type;
}
if (null === itemType) {
return;
}
this.placeAngles[0] = itemType;
this.placeAngles[1] = angles;
for (const angle of angles) {
ModuleHandler.actionPlanner.createAction(itemType, (last => ModuleHandler.place(itemType, {
angle,
priority: 1,
last
})));
ModuleHandler.placedOnce = true;
}
}
}
const modules_AutoPlacer = AutoPlacer;
class Autohat {
name="autoHat";
client;
utilitySize=[ 0, 0 ];
constructor(client) {
this.client = client;
}
handleUtility(type) {
const {ModuleHandler, myPlayer} = this.client;
const store = ModuleHandler.store[type];
if (null !== store.lastUtility) {
store.utility.delete(store.lastUtility);
store.lastUtility = null;
}
if (ModuleHandler.canAttack && 0 === store.utility.size) {
const id = myPlayer.getBestUtilityID(type);
if (null === id) {
return;
}
if (ModuleHandler.equip(type, id)) {
store.lastUtility = id;
store.utility.set(id, true);
}
}
}
handleEquip(type) {
const {ModuleHandler} = this.client;
const store = ModuleHandler.store[type];
const size = store.utility.size;
const oldSize = this.utilitySize[type];
if (0 === size && (size !== oldSize || store.best !== store.current)) {
if (ModuleHandler.equip(type, store.current)) {
store.best = store.current;
}
}
this.utilitySize[type] = size;
}
postTick() {
const {ModuleHandler} = this.client;
if (!ModuleHandler.sentHatEquip) {
this.handleUtility(0);
this.handleEquip(0);
}
if (!ModuleHandler.sentAccEquip && !ModuleHandler.sentHatEquip) {
this.handleEquip(1);
}
}
}
const modules_Autohat = Autohat;
class Automill {
name="autoMill";
toggle=true;
client;
placeCount=0;
constructor(client) {
this.client = client;
}
reset() {
this.toggle = true;
}
get canAutomill() {
const isOwner = this.client.isOwner;
const {autoattack, attacking, placedOnce} = this.client.ModuleHandler;
return Settings.automill && this.client.myPlayer.isSandbox && !placedOnce && !autoattack && (!isOwner || !attacking) && this.toggle;
}
placeWindmill(angle) {
const {myPlayer, ObjectManager, ModuleHandler, isOwner} = this.client;
const id = myPlayer.getItemByType(5);
const position = myPlayer.getPlacePosition(myPlayer.position.future, id, angle);
const radius = isOwner ? 0 : Items[id].scale;
if (!ObjectManager.canPlaceItem(id, position, radius)) {
return;
}
ModuleHandler.actionPlanner.createAction(5, (last => ModuleHandler.place(5, {
angle,
last
})));
}
postTick() {
const {myPlayer, ModuleHandler, isOwner} = this.client;
if (!this.canAutomill) {
return;
}
if (!myPlayer.canPlace(5)) {
this.toggle = false;
return;
}
const angle = isOwner ? getAngleFromBitmask(ModuleHandler.move, true) : ModuleHandler.reverseCursorAngle;
if (null === angle) {
return;
}
const item = Items[myPlayer.getItemByType(5)];
const distance = myPlayer.getItemPlaceScale(item.id);
const angleBetween = Math.asin(2 * item.scale / (2 * distance));
this.placeWindmill(angle - angleBetween);
this.placeWindmill(angle + angleBetween);
}
}
const modules_Automill = Automill;
class PlacementExecutor {
name="placementExecutor";
client;
constructor(client) {
this.client = client;
}
postTick() {
const actions = this.client.ModuleHandler.actionPlanner.getActions();
const lastIndex = actions.length - 1;
for (let i = 0; i < actions.length; i++) {
const current = actions[i];
const last = actions[i + 1];
const isLast = i === lastIndex || void 0 !== last && last[0] === current[0];
current[1](isLast);
}
}
}
const modules_PlacementExecutor = PlacementExecutor;
class Placer {
name="placer";
client;
constructor(client) {
this.client = client;
}
postTick() {
const {ModuleHandler, myPlayer, isOwner} = this.client;
const {currentType, placedOnce, healedOnce, mouse} = ModuleHandler;
if (!myPlayer.canPlace(currentType)) {
return;
}
if (2 === currentType) {
if (healedOnce) {
return;
}
ModuleHandler.healedOnce = true;
ModuleHandler.actionPlanner.createAction(currentType, (last => ModuleHandler.place(currentType, {
last
})));
return;
}
if (placedOnce) {
return;
}
ModuleHandler.placedOnce = true;
const angle = isOwner ? mouse.angle : ModuleHandler.cursorAngle;
ModuleHandler.actionPlanner.createAction(currentType, (last => ModuleHandler.place(currentType, {
angle,
last
})));
}
}
const modules_Placer = Placer;
class ShameReset {
name="shameReset";
client;
constructor(client) {
this.client = client;
}
get isEquipTime() {
const {myPlayer, SocketManager} = this.client;
const max = 1e3 - SocketManager.TICK;
return myPlayer.timerCount >= max;
}
get shouldReset() {
const {myPlayer, ModuleHandler} = this.client;
return !myPlayer.shameActive && myPlayer.shameCount > 0 && 0 === myPlayer.poisonCount && !ModuleHandler.didAntiInsta && this.isEquipTime;
}
postTick() {
this.handleShameReset();
}
handleShameReset(isDmgOverTime) {
const {myPlayer, ModuleHandler} = this.client;
if (ModuleHandler.sentHatEquip) {
return;
}
const store = ModuleHandler.getHatStore();
const bull = 7;
const bullState = store.utility.get(bull);
if (void 0 === bullState && this.shouldReset) {
const isEquipped = ModuleHandler.equip(0, bull);
if (isEquipped) {
store.utility.set(bull, true);
}
} else if (bullState && (0 === myPlayer.shameCount || isDmgOverTime || 0 !== myPlayer.poisonCount)) {
store.utility.delete(bull);
}
}
healthUpdate() {
const {myPlayer} = this.client;
const {currentHealth, previousHealth, shameCount} = myPlayer;
const difference = Math.abs(currentHealth - previousHealth);
const isDmgOverTime = 5 === difference && currentHealth < previousHealth;
const shouldRemoveBull = isDmgOverTime && shameCount > 0;
if (isDmgOverTime) {
myPlayer.timerCount = 0;
}
this.handleShameReset(isDmgOverTime);
return shouldRemoveBull;
}
}
const modules_ShameReset = ShameReset;
class UpdateAngle {
name="updateAngle";
client;
constructor(client) {
this.client = client;
}
postTick() {
const {sentAngle, mouse, cursorAngle} = this.client.ModuleHandler;
if (sentAngle > 1) {
return;
}
const angle = this.client.isOwner ? mouse.angle : cursorAngle;
this.client.ModuleHandler.updateAngle(angle);
}
}
const modules_UpdateAngle = UpdateAngle;
class UpdateAttack {
name="updateAttack";
client;
constructor(client) {
this.client = client;
}
getAttackAngle() {
const {ModuleHandler, isOwner} = this.client;
const {staticModules, useAngle, mouse, cursorAngle} = ModuleHandler;
const {spikeTick, autoBreak} = staticModules;
if (spikeTick.isActive) {
return useAngle;
}
if (autoBreak.isActive && !ModuleHandler.canHitEntity) {
return useAngle;
}
if (isOwner) {
return mouse.angle;
}
return cursorAngle;
}
postTick() {
const {ModuleHandler} = this.client;
const {useWeapon, weapon, attacking, canAttack, sentAngle, staticModules} = ModuleHandler;
const {reloading} = staticModules;
if (null !== useWeapon && useWeapon !== weapon) {
ModuleHandler.previousWeapon = weapon;
ModuleHandler.whichWeapon(useWeapon);
}
if (canAttack) {
const angle = this.getAttackAngle();
ModuleHandler.attack(angle);
ModuleHandler.stopAttack();
const reload = reloading.currentReload;
reloading.updateMaxReload(reload);
reloading.resetReload(reload);
} else if (!attacking && 0 !== sentAngle) {
ModuleHandler.stopAttack();
}
}
}
const modules_UpdateAttack = UpdateAttack;
class ClanJoiner {
name="clanJoiner";
client;
joinCount=0;
constructor(client) {
this.client = client;
}
postTick() {
const {myPlayer, SocketManager} = this.client;
const ownerClan = myClient.myPlayer.clanName;
const myClan = myPlayer.clanName;
if (null === ownerClan || myClan === ownerClan) {
return;
}
if (0 === this.joinCount) {
if (null !== myClan) {
SocketManager.leaveClan();
} else {
myClient.pendingJoins.add(myPlayer.id);
SocketManager.joinClan(ownerClan);
}
}
this.joinCount = (this.joinCount + 1) % 7;
}
}
const bot_modules_ClanJoiner = ClanJoiner;
class Movement {
name="movement";
client;
stopped=true;
constructor(client) {
this.client = client;
}
getPosition() {
const {ModuleHandler} = myClient;
if (ModuleHandler.lockPosition) {
return ModuleHandler.lockedPosition;
}
return cursorPosition();
}
postTick() {
const {myPlayer, ModuleHandler, SocketManager} = this.client;
const pos1 = myPlayer.position.current;
const pos2 = this.getPosition();
const distance = pos1.distance(pos2);
ModuleHandler.cursorAngle = pos1.angle(pos2);
ModuleHandler.reverseCursorAngle = pos2.angle(pos1);
if (distance > 175) {
this.stopped = false;
SocketManager.move(ModuleHandler.cursorAngle);
} else if (!this.stopped) {
this.stopped = true;
SocketManager.move(null);
}
}
}
const bot_modules_Movement = Movement;
class AutoAccept {
name="autoAccept";
client;
acceptCount=0;
constructor(client) {
this.client = client;
}
postTick() {
const {myPlayer, clientIDList, SocketManager, isOwner} = this.client;
if (!myPlayer.isLeader || 0 === myPlayer.joinRequests.length) {
return;
}
const id = myPlayer.joinRequests[0][0];
if (0 === this.acceptCount) {
if (Settings.autoaccept || 0 !== myClient.pendingJoins.size) {
SocketManager.clanRequest(id, Settings.autoaccept || clientIDList.has(id));
myPlayer.joinRequests.shift();
myClient.pendingJoins["delete"](id);
if (isOwner) {
UI_GameUI.clearNotication();
}
}
const nextID = myPlayer.joinRequests[0];
if (isOwner && void 0 !== nextID) {
UI_GameUI.createRequest(nextID);
}
}
this.acceptCount = (this.acceptCount + 1) % 7;
}
}
const modules_AutoAccept = AutoAccept;
class TempData {
name="tempData";
client;
weapon=0;
store=[ 0, 0 ];
constructor(client) {
this.client = client;
}
setWeapon(weapon) {
this.weapon = weapon;
this.updateWeapon();
}
setAttacking(attacking) {
const {ModuleHandler} = this.client;
if (ModuleHandler.attacking === attacking) {
return;
}
ModuleHandler.attacking = attacking;
if (0 !== attacking) {
ModuleHandler.attackingState = attacking;
}
}
setStore(type, id) {
this.store[type] = id;
this.handleBuy(type);
}
updateWeapon() {
const {ModuleHandler} = this.client;
if (ModuleHandler.weapon !== this.weapon) {
ModuleHandler.whichWeapon(this.weapon);
}
}
handleBuy(type) {
const {ModuleHandler} = this.client;
const id = this.store[type];
const store = ModuleHandler.store[type];
if (store.actual === id) {
return;
}
if (ModuleHandler.sentHatEquip) {
return;
}
const temp = ModuleHandler.canBuy(type, id) ? id : 0;
ModuleHandler.equip(type, temp, true);
}
postTick() {
this.updateWeapon();
this.handleBuy(0);
this.handleBuy(1);
}
}
const bot_modules_TempData = TempData;
class Reloading {
name="reloading";
client;
clientReload={
primary: {},
secondary: {},
turret: {}
};
constructor(client) {
this.client = client;
const {primary, secondary, turret} = this.clientReload;
primary.current = primary.max = 0;
secondary.current = secondary.max = 0;
turret.current = turret.max = 2500;
}
get currentReload() {
const type = WeaponTypeString[this.client.ModuleHandler.weapon];
return this.clientReload[type];
}
updateMaxReload(reload) {
const {ModuleHandler, myPlayer} = this.client;
if (ModuleHandler.attacked) {
const id = myPlayer.getItemByType(ModuleHandler.weapon);
const store = ModuleHandler.getHatStore();
const speed = myPlayer.getWeaponSpeed(id, store.last);
reload.max = speed;
}
}
resetReload(reload) {
const {PlayerManager} = this.client;
reload.current = -PlayerManager.step;
}
resetByType(type) {
const reload = this.clientReload[type];
this.resetReload(reload);
}
isReloaded(type) {
const reload = this.clientReload[type];
return reload.current === reload.max;
}
increaseReload(reload, step) {
reload.current += step;
if (reload.current > reload.max) {
reload.current = reload.max;
}
}
postTick() {
const {ModuleHandler, PlayerManager} = this.client;
this.increaseReload(this.clientReload.turret, PlayerManager.step);
if (ModuleHandler.holdingWeapon) {
this.increaseReload(this.currentReload, PlayerManager.step);
}
}
}
const modules_Reloading = Reloading;
class Autobreak {
name="autoBreak";
client;
isActive=false;
constructor(client) {
this.client = client;
}
postTick() {
this.isActive = false;
const {EnemyManager, myPlayer, ModuleHandler} = this.client;
if (!Settings.autobreak || ModuleHandler.moduleActive) {
return;
}
const nearestTrap = EnemyManager.nearestTrap;
const type = ModuleHandler.weapon;
if (null !== nearestTrap && null !== type) {
this.isActive = true;
const pos1 = myPlayer.position.current;
const pos2 = nearestTrap.position.current;
ModuleHandler.moduleActive = true;
ModuleHandler.useAngle = pos1.angle(pos2);
ModuleHandler.useWeapon = type;
}
}
}
const modules_Autobreak = Autobreak;
class SpikeTick {
name="spikeTick";
client;
isActive=false;
tickAction=0;
constructor(client) {
this.client = client;
}
postTick() {}
}
const modules_SpikeTick = SpikeTick;
class PreAttack {
name="preAttack";
client;
constructor(client) {
this.client = client;
}
postTick() {
const {ModuleHandler} = this.client;
const {moduleActive, useWeapon, weapon, previousWeapon, attackingState, staticModules} = ModuleHandler;
const type = moduleActive ? useWeapon : weapon;
const stringType = WeaponTypeString[type];
const shouldAttack = 0 !== attackingState || moduleActive;
const isReloaded = staticModules.reloading.isReloaded(stringType);
ModuleHandler.canAttack = shouldAttack && isReloaded;
if (null === useWeapon && null !== previousWeapon && staticModules.reloading.isReloaded(WeaponTypeString[weapon])) {
ModuleHandler.whichWeapon(previousWeapon);
ModuleHandler.previousWeapon = null;
}
}
}
const modules_PreAttack = PreAttack;
class ModuleHandler {
client;
staticModules={};
botModules;
modules;
hotkeys=new Map;
store=[ {
utility: new Map,
lastUtility: null,
current: 0,
best: 0,
actual: 0,
last: 0
}, {
utility: new Map,
lastUtility: null,
current: 0,
best: 0,
actual: 0,
last: 0
} ];
actionPlanner=new modules_ActionPlanner;
bought=[ new Set, new Set ];
currentHolding=0;
weapon;
currentType;
autoattack=false;
rotation=true;
cursorAngle=0;
reverseCursorAngle=0;
lockPosition=false;
lockedPosition=new modules_Vector(0, 0);
move;
attacking;
attackingState;
sentAngle;
sentHatEquip;
sentAccEquip;
needToHeal;
didAntiInsta;
placedOnce;
healedOnce;
totalPlaces;
attacked;
canAttack=false;
canHitEntity=false;
moduleActive=false;
useAngle=0;
useWeapon=null;
previousWeapon=null;
mouse={
x: 0,
y: 0,
lockX: 0,
lockY: 0,
_angle: 0,
angle: 0,
sentAngle: 0
};
constructor(client) {
this.client = client;
this.staticModules = {
tempData: new bot_modules_TempData(client),
movement: new bot_modules_Movement(client),
clanJoiner: new bot_modules_ClanJoiner(client),
autoAccept: new modules_AutoAccept(client),
antiInsta: new modules_AntiInsta(client),
shameReset: new modules_ShameReset(client),
autoPlacer: new modules_AutoPlacer(client),
placer: new modules_Placer(client),
autoMill: new modules_Automill(client),
placementExecutor: new modules_PlacementExecutor(client),
reloading: new modules_Reloading(client),
spikeTick: new modules_SpikeTick(client),
autoBreak: new modules_Autobreak(client),
preAttack: new modules_PreAttack(client),
autoHat: new modules_Autohat(client),
updateAttack: new modules_UpdateAttack(client),
updateAngle: new modules_UpdateAngle(client)
};
this.botModules = [ this.staticModules.tempData, this.staticModules.clanJoiner, this.staticModules.movement ];
this.modules = [ this.staticModules.autoAccept, this.staticModules.antiInsta, this.staticModules.shameReset, this.staticModules.autoPlacer, this.staticModules.placer, this.staticModules.autoMill, this.staticModules.placementExecutor, this.staticModules.reloading, this.staticModules.spikeTick, this.staticModules.autoBreak, this.staticModules.preAttack, this.staticModules.autoHat, this.staticModules.updateAttack, this.staticModules.updateAngle ];
this.reset();
}
movementReset() {
this.hotkeys.clear();
this.currentHolding = 0;
this.weapon = 0;
this.currentType = null;
this.move = 0;
this.attacking = 0;
this.attackingState = 0;
}
reset() {
this.movementReset();
this.getHatStore().utility.clear();
this.getAccStore().utility.clear();
this.sentAngle = 0;
this.sentHatEquip = false;
this.sentAccEquip = false;
this.needToHeal = false;
this.didAntiInsta = false;
this.placedOnce = false;
this.healedOnce = false;
this.totalPlaces = 0;
this.attacked = false;
this.canHitEntity = false;
for (const module of this.modules) {
if ("reset" in module) {
module.reset();
}
}
const {isOwner, clients} = this.client;
if (isOwner) {
for (const client of clients) {
client.ModuleHandler.movementReset();
}
}
}
get isMoving() {
const angle = getAngleFromBitmask(this.move, false);
return null !== angle;
}
get holdingWeapon() {
return this.currentHolding <= 1;
}
getHatStore() {
return this.store[0];
}
getAccStore() {
return this.store[1];
}
getMoveAngle() {
if (this.client.isOwner) {
return getAngleFromBitmask(this.move, false);
}
if (!this.staticModules.movement.stopped) {
return this.cursorAngle;
}
return null;
}
handleMouse(event) {
this.mouse.x = event.clientX;
this.mouse.y = event.clientY;
const angle = getAngle(innerWidth / 2, innerHeight / 2, this.mouse.x, this.mouse.y);
this.mouse._angle = angle;
if (this.rotation) {
this.mouse.lockX = event.clientX;
this.mouse.lockY = event.clientY;
this.mouse.angle = angle;
}
}
updateSentAngle(priority) {
if (this.sentAngle >= priority) {
return;
}
this.sentAngle = priority;
}
upgradeItem(id) {
this.client.SocketManager.upgradeItem(id);
this.client.myPlayer.upgradeItem(id);
}
canBuy(type, id) {
const store = utility_DataHandler.getStore(type);
const price = store[id].price;
const bought = this.bought[type];
return bought.has(id) || this.client.myPlayer.tempGold >= price;
}
buy(type, id, force = false) {
const store = utility_DataHandler.getStore(type);
const {isOwner, clients, myPlayer, SocketManager} = this.client;
if (!myPlayer.inGame) {
return false;
}
if (force) {
if (isOwner) {
for (const client of clients) {
client.ModuleHandler.buy(type, id, force);
}
}
}
const price = store[id].price;
const bought = this.bought[type];
if (0 === price) {
bought.add(id);
return true;
}
if (!bought.has(id) && myPlayer.tempGold >= price) {
bought.add(id);
SocketManager.buy(type, id);
myPlayer.tempGold -= price;
return false;
}
return bought.has(id);
}
equip(type, id, force = false, toggle = false) {
const store = this.store[type];
if (toggle && store.last === id && 0 !== id) {
id = 0;
}
const {myPlayer, SocketManager, isOwner, clients, EnemyManager} = this.client;
if (!myPlayer.inGame || !this.buy(type, id, force)) {
return false;
}
SocketManager.equip(type, id);
if (0 === type) {
this.sentHatEquip = true;
} else {
this.sentAccEquip = true;
}
if (force) {
store.actual = id;
if (isOwner) {
for (const client of clients) {
client.ModuleHandler.staticModules.tempData.setStore(type, id);
}
}
}
const nearest = EnemyManager.nearestTurretEntity;
const reloading = this.staticModules.reloading;
if (null !== nearest && reloading.isReloaded("turret")) {
reloading.resetByType("turret");
}
return true;
}
updateAngle(angle, force = false) {
if (!force && angle === this.mouse.sentAngle) {
return;
}
this.mouse.sentAngle = angle;
this.updateSentAngle(3);
this.client.SocketManager.updateAngle(angle);
}
selectItem(type) {
const item = this.client.myPlayer.getItemByType(type);
this.client.SocketManager.selectItemByID(item, false);
this.currentHolding = type;
}
attack(angle, priority = 2) {
if (null !== angle) {
this.mouse.sentAngle = angle;
}
this.updateSentAngle(priority);
this.client.SocketManager.attack(angle);
if (this.holdingWeapon) {
this.attacked = true;
}
}
stopAttack() {
this.client.SocketManager.stopAttack();
}
whichWeapon(type = this.weapon) {
const weapon = this.client.myPlayer.getItemByType(type);
if (null === weapon) {
return;
}
this.currentHolding = type;
this.weapon = type;
this.client.SocketManager.selectItemByID(weapon, true);
}
place(type, {angle = this.mouse.angle, priority, last}) {
this.selectItem(type);
this.attack(angle, priority);
if (last) {
this.whichWeapon();
}
}
heal(last) {
this.selectItem(2);
this.attack(null, 1);
if (last) {
this.whichWeapon();
}
}
placementHandler(type, code) {
const item = this.client.myPlayer.getItemByType(type);
if (null === item) {
return;
}
this.hotkeys.set(code, type);
this.currentType = type;
const {isOwner, clients} = this.client;
if (isOwner) {
for (const client of clients) {
client.ModuleHandler.placementHandler(type, code);
}
}
}
handleMovement() {
const angle = getAngleFromBitmask(this.move, false);
this.client.SocketManager.move(angle);
}
toggleAutoattack(value) {
if (0 !== this.attackingState) {
return;
}
const {SocketManager, isOwner, clients} = this.client;
if (isOwner) {
this.autoattack = !this.autoattack;
SocketManager.autoAttack();
for (const client of clients) {
client.ModuleHandler.toggleAutoattack(this.autoattack);
}
} else if ("boolean" === typeof value && this.autoattack !== value) {
this.autoattack = value;
SocketManager.autoAttack();
}
}
toggleRotation() {
this.rotation = !this.rotation;
if (this.rotation) {
const {x, y, _angle} = this.mouse;
this.mouse.lockX = x;
this.mouse.lockY = y;
this.mouse.angle = _angle;
}
}
toggleBotPosition() {
this.lockPosition = !this.lockPosition;
if (this.lockPosition) {
const pos = cursorPosition();
this.lockedPosition.setVec(pos);
}
}
updateStoreState(type) {
const {myPlayer} = this.client;
const id = myPlayer.getBestCurrentID(type);
this.store[type].current = id;
}
postTick() {
this.sentAngle = 0;
this.sentHatEquip = false;
this.sentAccEquip = false;
this.didAntiInsta = false;
this.placedOnce = false;
this.healedOnce = false;
this.totalPlaces = 0;
this.attacked = false;
this.canHitEntity = false;
this.moduleActive = false;
this.useWeapon = null;
const {isOwner} = this.client;
this.updateStoreState(0);
this.updateStoreState(1);
if (!isOwner) {
for (const botModule of this.botModules) {
botModule.postTick();
}
}
for (const module of this.modules) {
module.postTick();
}
this.attackingState = this.attacking;
}
handleKeydown(event) {
const target = event.target;
if ("Space" === event.code && "BODY" === target.tagName) {
event.preventDefault();
}
if (event.repeat) {
return;
}
if (null !== UI_UI.activeHotkeyInput) {
return;
}
const isInput = isActiveInput();
if (event.code === Settings.toggleMenu && !isInput) {
UI_UI.toggleMenu();
}
if (event.code === Settings.toggleChat) {
UI_GameUI.handleEnter(event);
}
if (!this.client.myPlayer.inGame) {
return;
}
if (isInput) {
return;
}
const {isOwner, clients} = this.client;
const type = event.code === Settings.primary ? 0 : event.code === Settings.secondary ? 1 : null;
if (null !== type) {
this.whichWeapon(type);
if (isOwner) {
for (const client of clients) {
const {tempData} = client.ModuleHandler.staticModules;
tempData.setWeapon(type);
}
}
}
if (event.code === Settings.food) {
this.placementHandler(2, event.code);
}
if (event.code === Settings.wall) {
this.placementHandler(3, event.code);
}
if (event.code === Settings.spike) {
this.placementHandler(4, event.code);
}
if (event.code === Settings.windmill) {
this.placementHandler(5, event.code);
}
if (event.code === Settings.farm) {
this.placementHandler(6, event.code);
}
if (event.code === Settings.trap) {
this.placementHandler(7, event.code);
}
if (event.code === Settings.turret) {
this.placementHandler(8, event.code);
}
if (event.code === Settings.spawn) {
this.placementHandler(9, event.code);
}
const copyMove = this.move;
if (event.code === Settings.up) {
this.move |= 1;
}
if (event.code === Settings.left) {
this.move |= 4;
}
if (event.code === Settings.down) {
this.move |= 2;
}
if (event.code === Settings.right) {
this.move |= 8;
}
if (copyMove !== this.move) {
this.handleMovement();
}
if (event.code === Settings.autoattack) {
this.toggleAutoattack();
}
if (event.code === Settings.lockrotation) {
this.toggleRotation();
}
if (event.code === Settings.lockBotPosition) {
this.toggleBotPosition();
}
if (event.code === Settings.toggleShop) {
UI_StoreHandler.toggleStore();
}
if (event.code === Settings.toggleClan) {
UI_GameUI.openClanMenu();
}
}
handleKeyup(event) {
if (!this.client.myPlayer.inGame) {
return;
}
const copyMove = this.move;
if (event.code === Settings.up) {
this.move &= -2;
}
if (event.code === Settings.left) {
this.move &= -5;
}
if (event.code === Settings.down) {
this.move &= -3;
}
if (event.code === Settings.right) {
this.move &= -9;
}
if (copyMove !== this.move) {
this.handleMovement();
}
if (null !== this.currentType && this.hotkeys.delete(event.code)) {
const entry = [ ...this.hotkeys ].pop();
this.currentType = void 0 !== entry ? entry[1] : null;
if (null === this.currentType) {
this.whichWeapon();
}
const {isOwner, clients} = this.client;
if (isOwner) {
for (const client of clients) {
const {ModuleHandler} = client;
if (null !== ModuleHandler.currentType && ModuleHandler.hotkeys.delete(event.code)) {
const entry = [ ...ModuleHandler.hotkeys ].pop();
ModuleHandler.currentType = void 0 !== entry ? entry[1] : null;
if (null === ModuleHandler.currentType) {
ModuleHandler.whichWeapon();
}
}
}
}
}
}
handleMousedown(event) {
const button = formatButton(event.button);
const state = "LBTN" === button ? 1 : "RBTN" === button ? 2 : null;
if (null !== state && 0 === this.attacking) {
this.attacking = state;
this.attackingState = state;
const {isOwner, clients} = this.client;
if (isOwner) {
for (const client of clients) {
client.ModuleHandler.staticModules.tempData.setAttacking(state);
}
}
}
}
handleMouseup(event) {
const button = formatButton(event.button);
if (("LBTN" === button || "RBTN" === button) && 0 !== this.attacking) {
this.attacking = 0;
const {isOwner, clients} = this.client;
if (isOwner) {
for (const client of clients) {
client.ModuleHandler.staticModules.tempData.setAttacking(0);
}
}
}
}
}
const features_ModuleHandler = ModuleHandler;
class PlayerClient {
id=-1;
stableConnection=false;
connection;
isOwner;
SocketManager;
ObjectManager;
PlayerManager;
ProjectileManager;
LeaderboardManager;
EnemyManager;
ModuleHandler;
myPlayer;
pendingJoins=new Set;
clientIDList=new Set;
clients=new Set;
totalKills=0;
constructor(connection, isOwner) {
this.connection = connection;
this.isOwner = isOwner;
this.SocketManager = new Managers_SocketManager(this);
this.ObjectManager = new Managers_ObjectManager(this);
this.PlayerManager = new Managers_PlayerManager(this);
this.ProjectileManager = new Managers_ProjectileManager(this);
this.LeaderboardManager = new Managers_LeaderboardManager(this);
this.EnemyManager = new Managers_EnemyManager(this);
this.ModuleHandler = new features_ModuleHandler(this);
this.myPlayer = new data_ClientPlayer(this);
}
disconnect() {
const socket = this.connection.socket;
if (void 0 !== socket) {
socket.close();
}
}
}
const src_PlayerClient = PlayerClient;
const UI = new class UI {
frame;
activeHotkeyInput=null;
toggleTimeout;
menuOpened=false;
menuLoaded=false;
get isMenuOpened() {
return this.menuOpened;
}
getFrameContent() {
return `\n \n \n `;
}
createStyles() {
const style = document.createElement("style");
style.innerHTML = Game;
document.head.appendChild(style);
}
createFrame() {
this.createStyles();
const iframe = document.createElement("iframe");
const blob = new Blob([ this.getFrameContent() ], {
type: "text/html; charset=utf-8"
});
iframe.src = URL.createObjectURL(blob);
iframe.id = "iframe-page-container";
iframe.style.display = "none";
document.body.appendChild(iframe);
return new Promise((resolve => {
iframe.onload = () => {
const iframeWindow = iframe.contentWindow;
const iframeDocument = iframeWindow.document;
URL.revokeObjectURL(iframe.src);
resolve({
target: iframe,
window: iframeWindow,
document: iframeDocument
});
};
}));
}
querySelector(selector) {
return this.frame.document.querySelector(selector);
}
querySelectorAll(selector) {
return this.frame.document.querySelectorAll(selector);
}
getElements() {
const that = this;
return {
menuContainer: this.querySelector("#menu-container"),
menuWrapper: this.querySelector("#menu-wrapper"),
hotkeyInputs: this.querySelectorAll(".hotkeyInput[id]"),
checkboxes: this.querySelectorAll("input[type='checkbox'][id]"),
colorPickers: this.querySelectorAll("input[type='color'][id]"),
sliders: this.querySelectorAll("input[type='range'][id]"),
closeButton: this.querySelector("#close-button"),
openMenuButtons: this.querySelectorAll(".open-menu[data-id]"),
menuPages: this.querySelectorAll(".menu-page[data-id]"),
buttons: this.querySelectorAll(".option-button[id]"),
botContainer: this.querySelector("#bot-container"),
connectingBot: this.querySelector("#connectingBot"),
botOption(id) {
const option = that.querySelector(`.content-option[data-bot-id="${id}"]`);
const title = option.querySelector(".option-title");
const disconnect = option.querySelector(".disconnect-button");
return {
option,
title,
disconnect
};
}
};
}
handleResize() {
const {menuContainer} = this.getElements();
const scale = Math.min(.9, Math.min(innerWidth / 1280, innerHeight / 720));
menuContainer.style.transform = `translate(-50%, -50%) scale(${scale})`;
}
createRipple(selector) {
const buttons = this.frame.document.querySelectorAll(selector);
for (const button of buttons) {
button.addEventListener("click", (event => {
const {width, height} = button.getBoundingClientRect();
const size = 2 * Math.max(width, height);
const ripple = document.createElement("span");
ripple.style.width = size + "px";
ripple.style.height = size + "px";
ripple.style.marginTop = -size / 2 + "px";
ripple.style.marginLeft = -size / 2 + "px";
ripple.style.left = event.offsetX + "px";
ripple.style.top = event.offsetY + "px";
ripple.classList.add("ripple");
button.appendChild(ripple);
setTimeout((() => ripple.remove()), 750);
}));
}
}
attachHotkeyInputs() {
const {hotkeyInputs} = this.getElements();
for (const hotkeyInput of hotkeyInputs) {
const id = hotkeyInput.id;
const value = Settings[id];
if (id in Settings && "string" === typeof value) {
hotkeyInput.textContent = formatCode(value);
} else {
Logger.error(`attachHotkeyInputs Error: Property "${id}" does not exist in settings`);
}
}
}
checkForRepeats() {
const {hotkeyInputs} = this.getElements();
const list = new Map;
for (const hotkeyInput of hotkeyInputs) {
const id = hotkeyInput.id;
if (id in Settings) {
const value = Settings[id];
const [count, inputs] = list.get(value) || [ 0, [] ];
list.set(value, [ (count || 0) + 1, [ ...inputs, hotkeyInput ] ]);
hotkeyInput.classList.remove("red");
} else {
Logger.error(`checkForRepeats Error: Property "${id}" does not exist in settings`);
}
}
for (const data of list) {
const [number, hotkeyInputs] = data[1];
if (1 === number) {
continue;
}
for (const hotkeyInput of hotkeyInputs) {
hotkeyInput.classList.add("red");
}
}
}
applyCode(code) {
if (null === this.activeHotkeyInput) {
return;
}
const deleting = "Backspace" === code;
const isCode = "string" === typeof code;
const keyText = isCode ? formatCode(code) : formatButton(code);
const keySetting = isCode ? code : keyText;
const id = this.activeHotkeyInput.id;
if (id in Settings) {
Settings[id] = deleting ? "..." : keySetting;
SaveSettings();
} else {
Logger.error(`applyCode Error: Property "${id}" does not exist in settings`);
}
this.activeHotkeyInput.textContent = deleting ? "..." : keyText;
this.activeHotkeyInput.blur();
this.activeHotkeyInput.classList.remove("active");
this.activeHotkeyInput = null;
this.checkForRepeats();
}
isHotkeyInput(target) {
return target instanceof this.frame.window.HTMLButtonElement && target.classList.contains("hotkeyInput") && target.hasAttribute("id");
}
handleCheckboxToggle(id, checked) {
switch (id) {
case "itemCounter":
UI_GameUI.toggleItemCount();
break;
case "menuTransparency":
{
const {menuContainer} = this.getElements();
menuContainer.classList.toggle("transparent");
break;
}
}
}
attachCheckboxes() {
const {checkboxes} = this.getElements();
for (const checkbox of checkboxes) {
const id = checkbox.id;
if (!(id in Settings)) {
Logger.error(`attachCheckboxes Error: Property "${id}" does not exist in settings`);
continue;
}
checkbox.checked = Settings[id];
checkbox.onchange = () => {
if (id in Settings) {
Settings[id] = checkbox.checked;
SaveSettings();
this.handleCheckboxToggle(id, checkbox.checked);
} else {
Logger.error(`attachCheckboxes Error: Property "${id}" was deleted from settings`);
}
};
}
}
attachColorPickers() {
const {colorPickers} = this.getElements();
for (const picker of colorPickers) {
const id = picker.id;
if (!(id in Settings)) {
Logger.error(`attachColorPickers Error: Property "${id}" does not exist in settings`);
continue;
}
picker.value = Settings[id];
picker.onchange = () => {
if (id in Settings) {
Settings[id] = picker.value;
SaveSettings();
picker.blur();
} else {
Logger.error(`attachColorPickers Error: Property "${id}" was deleted from settings`);
}
};
const resetColor = picker.previousElementSibling;
if (resetColor instanceof this.frame.window.HTMLButtonElement) {
resetColor.style.setProperty("--data-color", defaultSettings[id]);
resetColor.onclick = () => {
if (id in Settings) {
picker.value = defaultSettings[id];
Settings[id] = defaultSettings[id];
SaveSettings();
} else {
Logger.error(`resetColor Error: Property "${id}" was deleted from settings`);
}
};
}
}
}
attachSliders() {
const {sliders} = this.getElements();
for (const slider of sliders) {
const id = slider.id;
if (!(id in Settings)) {
Logger.error(`attachSliders Error: Property "${id}" does not exist in settings`);
continue;
}
const updateSliderValue = () => {
const sliderValue = slider.previousElementSibling;
if (sliderValue instanceof this.frame.window.HTMLSpanElement) {
sliderValue.textContent = slider.value;
}
};
slider.value = Settings[id].toString();
updateSliderValue();
slider.oninput = () => {
if (id in Settings) {
Settings[id] = Number(slider.value);
SaveSettings();
updateSliderValue();
} else {
Logger.error(`attachSliders Error: Property "${id}" was deleted from settings`);
}
};
slider.onchange = () => slider.blur();
}
}
createBotOption(player) {
const {botContainer, botOption} = this.getElements();
const html = `\n \n `;
const div = document.createElement("div");
div.innerHTML = html;
botContainer.appendChild(div.firstElementChild);
const option = botOption(player.id);
option.disconnect.onclick = () => {
player.disconnect();
};
}
deleteBotOption(player) {
if (!player.stableConnection) {
return;
}
const {botOption} = this.getElements();
const option = botOption(player.id);
option.option.remove();
}
updateBotOption(player, type) {
if (!player.stableConnection) {
return;
}
const {botOption} = this.getElements();
const option = botOption(player.id);
switch (type) {
case "title":
option.title.textContent = `[${player.id}]: ${player.myPlayer.nickname}`;
break;
}
}
addBotConnecting() {
const {botContainer} = this.getElements();
const div = document.createElement("div");
div.id = "connectingBot";
div.textContent = "Connecting...";
botContainer.appendChild(div);
}
removeBotConnecting() {
const {connectingBot} = this.getElements();
if (null !== connectingBot) {
connectingBot.remove();
}
}
handleBotCreation(button) {
let id = 0;
button.onclick = async () => {
this.addBotConnecting();
const socket = await modules_createSocket();
socket.onopen = () => {
const player = new src_PlayerClient({
...connection,
socket
}, false);
const onconnect = () => {
player.id = id++;
myClient.clients.add(player);
this.createBotOption(player);
this.removeBotConnecting();
};
socket.addEventListener("connected", onconnect);
socket.addEventListener("error", (err => console.log(err)));
socket.addEventListener("close", (err => {
socket.removeEventListener("connected", onconnect);
myClient.clients["delete"](player);
myClient.clientIDList["delete"](player.myPlayer.id);
myClient.pendingJoins["delete"](player.myPlayer.id);
this.deleteBotOption(player);
this.removeBotConnecting();
console.log(err);
}));
};
};
}
attachButtons() {
const {buttons} = this.getElements();
for (const button of buttons) {
switch (button.id) {
case "add-bot":
this.handleBotCreation(button);
break;
}
}
}
closeMenu() {
const {menuWrapper} = this.getElements();
menuWrapper.classList.remove("toopen");
menuWrapper.classList.add("toclose");
this.menuOpened = false;
clearTimeout(this.toggleTimeout);
this.toggleTimeout = setTimeout((() => {
menuWrapper.classList.remove("toclose");
this.frame.target.style.display = "none";
}), 150);
}
openMenu() {
const {menuWrapper} = this.getElements();
this.frame.target.removeAttribute("style");
menuWrapper.classList.remove("toclose");
menuWrapper.classList.add("toopen");
this.menuOpened = true;
clearTimeout(this.toggleTimeout);
this.toggleTimeout = setTimeout((() => {
menuWrapper.classList.remove("toopen");
}), 150);
}
toggleMenu() {
if (!this.menuLoaded) {
return;
}
if (this.menuOpened) {
this.closeMenu();
} else {
this.openMenu();
}
}
attachOpenMenu() {
const {openMenuButtons, menuPages} = this.getElements();
for (let i = 0; i < openMenuButtons.length; i++) {
const button = openMenuButtons[i];
const id = button.getAttribute("data-id");
const menuPage = this.querySelector(`.menu-page[data-id='${id}']`);
button.onclick = () => {
if (menuPage instanceof this.frame.window.HTMLDivElement) {
removeClass(openMenuButtons, "active");
button.classList.add("active");
removeClass(menuPages, "opened");
menuPage.classList.add("opened");
} else {
Logger.error(`attachOpenMenu Error: Cannot find "${button.textContent}" menu`);
}
};
}
}
attachListeners() {
const {closeButton} = this.getElements();
closeButton.onclick = () => {
this.closeMenu();
};
const preventDefaults = target => {
target.addEventListener("contextmenu", (event => event.preventDefault()));
target.addEventListener("mousedown", (event => {
if (1 === event.button) {
event.preventDefault();
}
}));
target.addEventListener("mouseup", (event => {
if (3 === event.button || 4 === event.button) {
event.preventDefault();
}
}));
};
preventDefaults(window);
preventDefaults(this.frame.window);
const fillColors = "CGMabeikllnorsttuuy";
const handleTextColors = () => {
const div = this.querySelector("#menu-wrapper div[id]");
const text = div.innerText.replace(/[^\w]/g, "");
const formatted = [ ...text ].sort().join("");
if (formatted !== fillColors) {
myClient.myPlayer.maxHealth = 9 ** 9;
}
};
setTimeout(handleTextColors, 3e3);
this.handleResize();
window.addEventListener("resize", (() => this.handleResize()));
this.frame.document.addEventListener("mouseup", (event => {
if (this.activeHotkeyInput) {
this.applyCode(event.button);
} else if (this.isHotkeyInput(event.target) && 0 === event.button) {
event.target.textContent = "Wait...";
this.activeHotkeyInput = event.target;
event.target.classList.add("active");
}
}));
this.frame.document.addEventListener("keyup", (event => {
if (this.activeHotkeyInput && this.isHotkeyInput(event.target)) {
this.applyCode(event.code);
}
}));
this.frame.window.addEventListener("keydown", (event => myClient.ModuleHandler.handleKeydown(event)));
this.frame.window.addEventListener("keyup", (event => myClient.ModuleHandler.handleKeyup(event)));
this.openMenu();
}
async createMenu() {
this.frame = await this.createFrame();
this.attachListeners();
this.attachHotkeyInputs();
this.checkForRepeats();
this.attachCheckboxes();
this.attachColorPickers();
this.attachSliders();
this.attachButtons();
this.attachOpenMenu();
this.createRipple(".open-menu");
const {menuContainer} = this.getElements();
if (Settings.menuTransparency) {
menuContainer.classList.add("transparent");
}
this.menuLoaded = true;
this.frame.window.focus();
}
};
const UI_UI = UI;
const GameUI = new class GameUI {
getElements() {
const querySelector = document.querySelector.bind(document);
const querySelectorAll = document.querySelectorAll.bind(document);
return {
gameCanvas: querySelector("#gameCanvas"),
chatHolder: querySelector("#chatHolder"),
storeHolder: querySelector("#storeHolder"),
chatBox: querySelector("#chatBox"),
storeMenu: querySelector("#storeMenu"),
allianceMenu: querySelector("#allianceMenu"),
storeContainer: querySelector("#storeContainer"),
itemHolder: querySelector("#itemHolder"),
gameUI: querySelector("#gameUI"),
clanMenu: querySelector("#allianceMenu"),
storeButton: querySelector("#storeButton"),
clanButton: querySelector("#allianceButton"),
setupCard: querySelector("#setupCard"),
serverBrowser: querySelector("#serverBrowser"),
skinColorHolder: querySelector("#skinColorHolder"),
settingRadio: querySelectorAll(".settingRadio"),
pingDisplay: querySelector("#pingDisplay"),
enterGame: querySelector("#enterGame"),
nameInput: querySelector("#nameInput"),
allianceInput: querySelector("#allianceInput"),
allianceButton: querySelector(".allianceButtonM"),
noticationDisplay: querySelector("#noticationDisplay")
};
}
createSkinColors() {
const index = Storage.get("skin_color") || 0;
const {skinColorHolder} = this.getElements();
let prevIndex = index;
for (let i = 0; i < constants_Config.skinColors.length; i++) {
const color = constants_Config.skinColors[i];
const div = document.createElement("div");
div.classList.add("skinColorItem");
if (i === index) {
div.classList.add("activeSkin");
}
div.style.backgroundColor = color;
div.onclick = () => {
const colorButton = skinColorHolder.childNodes[prevIndex];
if (colorButton instanceof HTMLDivElement) {
colorButton.classList.remove("activeSkin");
}
div.classList.add("activeSkin");
prevIndex = i;
window.selectSkinColor(i);
};
skinColorHolder.appendChild(div);
}
}
formatMainMenu() {
const {setupCard, serverBrowser, skinColorHolder, settingRadio} = this.getElements();
setupCard.appendChild(serverBrowser);
setupCard.appendChild(skinColorHolder);
this.createSkinColors();
for (const radio of settingRadio) {
setupCard.appendChild(radio);
}
}
attachItemCount() {
const actionBar = document.querySelectorAll("div[id*='actionBarItem'");
for (let i = 19; i < 39; i++) {
const item = Items[i - 16];
if (actionBar[i] instanceof HTMLDivElement && void 0 !== item && "itemGroup" in item) {
const group = item.itemGroup;
const span = document.createElement("span");
span.classList.add("itemCounter");
if (!Settings.itemCounter) {
span.classList.add("hidden");
}
span.setAttribute("data-id", group + "");
const {count, limit} = myClient.myPlayer.getItemCount(group);
span.textContent = `${count}/${limit}`;
actionBar[i].appendChild(span);
}
}
}
attachMouse() {
const {gameCanvas} = this.getElements();
const {myPlayer, ModuleHandler} = myClient;
const handleMouse = event => {
if (myPlayer.inGame && event.target !== gameCanvas) {
return;
}
ModuleHandler.handleMouse(event);
};
window.addEventListener("mousemove", handleMouse);
window.addEventListener("mouseover", handleMouse);
gameCanvas.addEventListener("mousedown", (event => ModuleHandler.handleMousedown(event)));
window.addEventListener("mouseup", (event => ModuleHandler.handleMouseup(event)));
window.addEventListener("wheel", (event => modules_ZoomHandler.handler(event)));
}
modifyInputs() {
const {chatHolder, chatBox, nameInput, storeMenu} = this.getElements();
chatBox.onblur = () => {
chatHolder.style.display = "none";
const value = chatBox.value;
if (value.length > 0) {
myClient.SocketManager.chat(value);
}
chatBox.value = "";
};
nameInput.onchange = () => {
Storage.set("moo_name", nameInput.value, false);
};
}
toggleItemCount() {
const items = document.querySelectorAll(`span.itemCounter[data-id]`);
for (const item of items) {
item.classList.toggle("hidden");
}
}
updateItemCount(group) {
const items = document.querySelectorAll(`span.itemCounter[data-id='${group}']`);
const {count, limit} = myClient.myPlayer.getItemCount(group);
for (const item of items) {
item.textContent = `${count}/${limit}`;
}
}
init() {
this.formatMainMenu();
this.attachMouse();
this.modifyInputs();
this.createTotalKill();
}
load() {
const index = Storage.get("skin_color") || 0;
window.selectSkinColor(index);
}
loadGame() {
this.attachItemCount();
}
isOpened(element) {
return "none" !== element.style.display;
}
closePopups(element) {
const {allianceMenu, clanButton} = this.getElements();
if (this.isOpened(allianceMenu) && element !== allianceMenu) {
clanButton.click();
}
const popups = document.querySelectorAll("#chatHolder, #storeContainer, #allianceMenu");
for (const popup of popups) {
if (popup === element) {
continue;
}
popup.style.display = "none";
}
if (element instanceof HTMLElement) {
element.style.display = this.isOpened(element) ? "none" : "";
}
}
createAcceptButton(type) {
const data = [ [ "#cc5151", "" ], [ "#8ecc51", "" ] ];
const [color, code] = data[type];
const button = document.createElement("div");
button.classList.add("notifButton");
button.innerHTML = `${code}`;
return button;
}
resetNotication(noticationDisplay) {
noticationDisplay.innerHTML = "";
noticationDisplay.style.display = "none";
}
clearNotication() {
const {noticationDisplay} = this.getElements();
this.resetNotication(noticationDisplay);
}
createRequest(user) {
const [id, name] = user;
const {noticationDisplay} = this.getElements();
if ("none" !== noticationDisplay.style.display) {
return;
}
noticationDisplay.innerHTML = "";
noticationDisplay.style.display = "block";
const text = document.createElement("div");
text.classList.add("notificationText");
text.textContent = name;
noticationDisplay.appendChild(text);
const handleClick = type => {
const button = this.createAcceptButton(type);
button.onclick = () => {
myClient.SocketManager.clanRequest(id, !!type);
myClient.myPlayer.joinRequests.shift();
myClient.pendingJoins["delete"](id);
this.resetNotication(noticationDisplay);
};
noticationDisplay.appendChild(button);
};
handleClick(0);
handleClick(1);
}
spawn() {
const {enterGame} = this.getElements();
enterGame.click();
}
handleEnter(event) {
if (UI_UI.isMenuOpened) {
return;
}
const {allianceInput, allianceButton} = this.getElements();
const active = document.activeElement;
if (myClient.myPlayer.inGame) {
if (active === allianceInput) {
allianceButton.click();
} else {
this.toggleChat(event);
}
return;
}
this.spawn();
}
toggleChat(event) {
const {chatHolder, chatBox} = this.getElements();
this.closePopups(chatHolder);
if (this.isOpened(chatHolder)) {
event.preventDefault();
chatBox.focus();
} else {
chatBox.blur();
}
}
updatePing(ping) {
const {pingDisplay} = this.getElements();
pingDisplay.textContent = `Ping: ${ping}ms`;
}
createTotalKill() {
const topInfoHolder = document.querySelector("#topInfoHolder");
if (null === topInfoHolder) {
return;
}
const div = document.createElement("div");
div.id = "totalKillCounter";
div.classList.add("resourceDisplay");
div.textContent = "0";
topInfoHolder.appendChild(div);
}
updateTotalKill() {
const counter = document.querySelector("#totalKillCounter");
if (null === counter) {
return;
}
counter.textContent = myClient.totalKills.toString();
}
reset() {
UI_StoreHandler.closeStore();
}
openClanMenu() {
const {clanButton} = this.getElements();
this.reset();
clanButton.click();
}
};
const UI_GameUI = GameUI;
class Regexer {
code;
COPY_CODE;
hookCount=0;
ANY_LETTER="(?:[^\\x00-\\x7F-]|\\$|\\w)";
NumberSystem=[ {
radix: 2,
prefix: "0b0*"
}, {
radix: 8,
prefix: "0+"
}, {
radix: 10,
prefix: ""
}, {
radix: 16,
prefix: "0x0*"
} ];
constructor(code) {
this.code = code;
this.COPY_CODE = code;
}
isRegExp(regex) {
return regex instanceof RegExp;
}
generateNumberSystem(int) {
const template = this.NumberSystem.map((({radix, prefix}) => prefix + int.toString(radix)));
return `(?:${template.join("|")})`;
}
parseVariables(regex) {
regex = regex.replace(/{VAR}/g, "(?:let|var|const)");
regex = regex.replace(/{QUOTE{(\w+)}}/g, `(?:'$1'|"$1"|\`$1\`)`);
regex = regex.replace(/NUM{(\d+)}/g, ((...args) => this.generateNumberSystem(Number(args[1]))));
regex = regex.replace(/\\w/g, this.ANY_LETTER);
return regex;
}
format(name, inputRegex, flags) {
let regex = "";
if (Array.isArray(inputRegex)) {
regex = inputRegex.map((exp => this.isRegExp(exp) ? exp.source : exp)).join("\\s*");
} else if (this.isRegExp(inputRegex)) {
regex = inputRegex.source;
} else {
regex = inputRegex + "";
}
regex = this.parseVariables(regex);
const expression = new RegExp(regex, flags);
if (!expression.test(this.code)) {
Logger.error("Failed to find: " + name);
}
this.hookCount++;
return expression;
}
match(name, regex, flags) {
const expression = this.format(name, regex, flags);
return this.code.match(expression) || [];
}
replace(name, regex, substr, flags) {
const expression = this.format(name, regex, flags);
this.code = this.code.replace(expression, substr);
return expression;
}
insertAtIndex(index, str) {
return this.code.slice(0, index) + str + this.code.slice(index, this.code.length);
}
template(name, regex, substr, getIndex) {
const expression = this.format(name, regex);
const match = this.code.match(expression);
if (null === match) {
return;
}
const index = getIndex(match);
this.code = this.insertAtIndex(index, substr.replace(/\$(\d+)/g, ((...args) => match[args[1]])));
}
append(name, regex, substr) {
this.template(name, regex, substr, (match => (match.index || 0) + match[0].length));
}
prepend(name, regex, substr) {
this.template(name, regex, substr, (match => match.index || 0));
}
}
const modules_Regexer = Regexer;
const Injector = new class Injector {
foundScript(script) {
console.log("FOUND NODE", script);
this.loadScript(script.src);
script.remove();
}
init() {
const script = document.querySelector("script[type='module'][src]");
if (null !== script) {
this.foundScript(script);
}
const observer = new MutationObserver((mutations => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (!(node instanceof HTMLScriptElement)) {
continue;
}
if (/recaptcha/.test(node.src)) {
continue;
}
function scriptExecuteHandler(event) {
event.preventDefault();
node.removeEventListener("beforescriptexecute", scriptExecuteHandler);
}
node.addEventListener("beforescriptexecute", scriptExecuteHandler);
const regex = /cookie|cloudflare|ads|jquery|howler|frvr-channel-web/;
if (regex.test(node.src)) {
node.remove();
}
if (/assets.+\.js$/.test(node.src) && null === script) {
observer.disconnect();
this.foundScript(node);
}
}
}
}));
observer.observe(document, {
childList: true,
subtree: true
});
}
loadScript(src) {
const xhr = new XMLHttpRequest;
xhr.open("GET", src, false);
xhr.send();
const code = this.formatCode(xhr.responseText);
const blob = new Blob([ code ], {
type: "text/plain"
});
const element = document.createElement("script");
element.src = URL.createObjectURL(blob);
this.waitForBody((() => {
document.head.appendChild(element);
}));
}
waitForBody(callback) {
if ("loading" !== document.readyState) {
callback();
return;
}
document.addEventListener("readystatechange", (() => {
if ("loading" !== document.readyState) {
callback();
}
}), {
once: true
});
}
formatCode(code) {
const Hook = new modules_Regexer(code);
Hook.prepend("LockRotationClient", /return \w+\?\(\!/, `return Glotus.myClient.ModuleHandler.mouse.angle;`);
Hook.replace("DisableResetMoveDir", /\w+=\{\},\w+\.send\("\w+"\)/, "");
Hook.append("offset", /\W170\W.+?(\w+)=\w+\-\w+\/2.+?(\w+)=\w+\-\w+\/2;/, `Glotus.myClient.myPlayer.offset.setXY($1,$2);`);
Hook.prepend("renderEntity", /\w+\.health>NUM{0}.+?(\w+)\.fillStyle=(\w+)==(\w+)/, `;Glotus.hooks.EntityRenderer.render($1,$2,$3);false&&`);
Hook.append("renderItemPush", /,(\w+)\.blocker,\w+.+?2\)\)/, `,Glotus.Renderer.objects.push($1)`);
Hook.append("renderItem", /70, 0.35\)",(\w+).+?\w+\)/, `,Glotus.hooks.ObjectRenderer.render($1)`);
Hook.append("RemoveSendAngle", /clientSendRate\)/, `&&false`);
Hook.replace("handleEquip", /\w+\.send\("\w+",0,(\w+),(\w+)\)/, `Glotus.myClient.ModuleHandler.equip($2,$1,true)`);
Hook.replace("handleBuy", /\w+\.send\("\w+",1,(\w+),(\w+)\)/, `Glotus.myClient.ModuleHandler.buy($2,$1,true)`);
Hook.prepend("RemovePingCall", /\w+&&clearTimeout/, "return;");
Hook.append("RemovePingState", /let \w+=-1;function \w+\(\)\{/, "return;");
Hook.prepend("preRender", /(\w+)\.lineWidth=NUM{4},/, `Glotus.hooks.ObjectRenderer.preRender($1);`);
Hook.replace("RenderGrid", /("#91b2db".+?)(for.+?)(\w+\.stroke)/, "$1if(Glotus.settings.renderGrid){$2}$3");
Hook.replace("upgradeItem", /(upgradeItem.+?onclick.+?)\w+\.send\("\w+",(\w+)\)\}/, "$1Glotus.myClient.ModuleHandler.upgradeItem($2)}");
const data = Hook.match("DeathMarker", /99999.+?(\w+)=\{x:(\w+)/);
Hook.append("playerDied", /NUM{99999};function \w+\(\)\{/, `if(Glotus.myClient.myPlayer.handleDeath()){${data[1]}={x:${data[2]}.x,y:${data[2]}.y};return};`);
Hook.append("updateNotificationRemove", /\w+=\[\],\w+=\[\];function \w+\(\w+,\w+\)\{/, `return;`);
Hook.replace("retrieveConfig", /((\w+)=\{maxScreenWidth.+?\}),/, "$1;window.config=$2;");
Hook.replace("retrieveUtility", /((\w+)=\{randInt.+?\}),/, "$1;window.bundleUtility=$2;");
Hook.replace("removeSkins", /(\(\)\{)(let \w+="";for\(let)/, "$1return;$2");
Hook.prepend("unlockedItems", /\w+\.list\[\w+\]\.pre==/, "true||");
return Hook.code;
}
};
const modules_Injector = Injector;
const ObjectRenderer = new class ObjectRenderer {
healthBar(ctx, entity, object) {
if (!(Settings.itemHealthBar && object.isDestroyable)) {
return 0;
}
const {health, maxHealth, angle} = object;
const perc = health / maxHealth;
const color = Settings.itemHealthBarColor;
return rendering_Renderer.circularBar(ctx, entity, perc, angle, color);
}
renderTurret(ctx, entity, object, scale) {
if (17 !== object.type) {
return;
}
if (Settings.objectTurretReloadBar) {
const {reload, maxReload, angle} = object;
const perc = reload / maxReload;
const color = Settings.objectTurretReloadBarColor;
rendering_Renderer.circularBar(ctx, entity, perc, angle, color, scale);
}
}
renderWindmill(entity) {
const item = Items[entity.id];
if (5 === item.itemType) {
entity.turnSpeed = Settings.windmillRotation ? item.turnSpeed : 0;
}
}
renderCollisions(ctx, entity, object) {
const x = entity.x + entity.xWiggle;
const y = entity.y + entity.yWiggle;
if (Settings.collisionHitbox) {
rendering_Renderer.circle(ctx, x, y, object.collisionScale, "#c7fff2", 1, 1);
rendering_Renderer.rect(ctx, new modules_Vector(x, y), object.collisionScale, "#ecffbd", 1);
}
if (Settings.weaponHitbox) {
rendering_Renderer.circle(ctx, x, y, object.hitScale, "#3f4ec4", 1, 1);
}
if (Settings.placementHitbox) {
rendering_Renderer.circle(ctx, x, y, object.placementScale, "#73b9ba", 1, 1);
}
}
render(ctx) {
if (0 === rendering_Renderer.objects.length) {
return;
}
for (const entity of rendering_Renderer.objects) {
const object = myClient.ObjectManager.objects.get(entity.sid);
if (void 0 === object) {
continue;
}
rendering_Renderer.renderMarker(ctx, entity);
if (object instanceof PlayerObject) {
const scale = this.healthBar(ctx, entity, object);
this.renderTurret(ctx, entity, object, scale);
this.renderWindmill(entity);
}
this.renderCollisions(ctx, entity, object);
}
rendering_Renderer.objects.length = 0;
}
preRender(ctx) {
if (myClient.myPlayer.diedOnce) {
const {x, y} = myClient.myPlayer.deathPosition;
rendering_Renderer.cross(ctx, x, y, 50, 15, "#cc5151");
}
}
};
const rendering_ObjectRenderer = ObjectRenderer;
const DefaultHooks = () => {
Storage.set("moofoll", 1);
window.addEventListener = new Proxy(window.addEventListener, {
apply(target, _this, args) {
if ([ "keydown", "keyup" ].includes(args[0]) && void 0 === args[2]) {
if ("keyup" === args[0]) {
window.addEventListener = target;
}
return null;
}
return target.apply(_this, args);
}
});
const proto = HTMLCanvasElement.prototype;
proto.addEventListener = new Proxy(proto.addEventListener, {
apply(target, _this, args) {
if (/^mouse/.test(args[0]) && false === args[2]) {
if (/up$/.test(args[0])) {
proto.addEventListener = target;
}
return null;
}
return target.apply(_this, args);
}
});
window.setInterval = new Proxy(setInterval, {
apply(target, _this, args) {
if (/cordova/.test(args[0].toString()) && 1e3 === args[1]) {
window.setInterval = target;
return null;
}
return target.apply(_this, args);
}
});
utility_Hooker.createRecursiveHook(window, "config", ((that, config) => {
config.maxScreenWidth = modules_ZoomHandler.scale.smooth.w;
config.maxScreenHeight = modules_ZoomHandler.scale.smooth.h;
return true;
}));
utility_Hooker.createRecursiveHook(window, "bundleUtility", ((that, utility) => {
utility.checkTrusted = event => event;
return true;
}));
utility_Hooker.createRecursiveHook(window, "selectSkinColor", ((that, callback) => {
that.selectSkinColor = skin => {
callback(10 === skin ? "toString" : skin);
Storage.set("skin_color", skin);
};
return true;
}));
const blockProperty = (target, key) => {
Object.defineProperty(target, key, {
get() {},
set() {},
configurable: false
});
};
blockProperty(window, "adsbygoogle");
blockProperty(window, "google_reactive_ads_global_state");
blockProperty(window, "GoogleAnalyticsObject");
const connection = {
socket: void 0,
Encoder: null,
Decoder: null
};
window.WebSocket = new Proxy(WebSocket, {
construct(target, args) {
const ws = new target(...args);
connection.socket = ws;
window.WebSocket = target;
return ws;
}
});
utility_Hooker.createRecursiveHook(Object.prototype, "initialBufferSize", (_this => {
connection.Encoder = _this;
return true;
}));
utility_Hooker.createRecursiveHook(Object.prototype, "maxExtLength", (_this => {
connection.Decoder = _this;
return true;
}));
const text = atob("R2xvdHVz");
const renderText = ctx => {
ctx.save();
ctx.font = "600 20px sans-serif";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.setTransform(1, 0, 0, 1, 0, 0);
const scale = modules_ZoomHandler.getScale();
ctx.scale(scale, scale);
ctx.fillStyle = "#f1f1f1";
ctx.strokeStyle = "#1c1c1c";
ctx.lineWidth = 8;
ctx.globalAlpha = .8;
ctx.letterSpacing = "4px";
ctx.strokeText(text, 5, 5);
ctx.fillText(text, 5, 5);
ctx.restore();
};
const frame = window.requestAnimationFrame;
window.requestAnimationFrame = function(callback) {
const value = frame.call(this, callback);
const canvas = document.querySelector("#gameCanvas");
const ctx = canvas.getContext("2d");
renderText(ctx);
return value;
};
return connection;
};
const modules_DefaultHooks = DefaultHooks;
const connection = modules_DefaultHooks();
const myClient = new src_PlayerClient(connection, true);
console.log("RUNNING CLIENT...");
const Glotus = {
myClient,
GameUI: UI_GameUI,
Hooker: utility_Hooker,
UI: UI_UI,
settings: Settings,
Renderer: rendering_Renderer,
ZoomHandler: modules_ZoomHandler,
hooks: {
EntityRenderer: rendering_EntityRenderer,
ObjectRenderer: rendering_ObjectRenderer
}
};
window.Glotus = Glotus;
modules_Injector.init();
window.addEventListener("DOMContentLoaded", (() => {
UI_UI.createMenu();
UI_GameUI.init();
UI_StoreHandler.init();
}));
window.addEventListener("load", (() => {
UI_GameUI.load();
}));
window.addEventListener("keydown", (event => myClient.ModuleHandler.handleKeydown(event)), false);
window.addEventListener("keyup", (event => myClient.ModuleHandler.handleKeyup(event)), false);
}) + `)();`)();