// ==UserScript== // @name AlterAle - zombs.io // @namespace http://tampermonkey.net/ // @version -.4.2 // @description weeb mod de-weeb'd // @author rdm / AyuBloom // @match zombs.io // @icon https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/AlterAle%20Arcaea.webp?v=1717835928442 // @grant none // @license GNU GPLv3 // @downloadURL none // ==/UserScript== /* @Dependencies */ /* bytebuffer.js (c) 2015 Daniel Wirtz Backing buffer: ArrayBuffer, Accessor: Uint8Array Released under the Apache License, Version 2.0 see: https://github.com/dcodeIO/bytebuffer.js for details */ (function (h, l) { if ("function" === typeof define && define.amd) define(["long"], l); else if ("function" === typeof require && "object" === typeof module && module && module.exports) { h = module; try { var t = require("long"); } catch (v) {} l = l(t); h.exports = l; } else (h.dcodeIO = h.dcodeIO || {}).ByteBuffer = l(h.dcodeIO.Long); })(this, function (h) { function l(a) { var b = 0; return function () { return b < a.length ? a.charCodeAt(b++) : null; }; } function t() { var a = [], b = []; return function () { if (0 === arguments.length) return b.join("") + x.apply(String, a); 1024 < a.length + arguments.length && (b.push(x.apply(String, a)), (a.length = 0)); Array.prototype.push.apply(a, arguments); }; } function v(a, b, c, e, k) { var f = 8 * k - e - 1; var d = (1 << f) - 1, g = d >> 1, n = -7; k = c ? k - 1 : 0; var h = c ? -1 : 1, q = a[b + k]; k += h; c = q & ((1 << -n) - 1); q >>= -n; for (n += f; 0 < n; c = 256 * c + a[b + k], k += h, n -= 8); f = c & ((1 << -n) - 1); c >>= -n; for (n += e; 0 < n; f = 256 * f + a[b + k], k += h, n -= 8); if (0 === c) c = 1 - g; else { if (c === d) return f ? NaN : Infinity * (q ? -1 : 1); f += Math.pow(2, e); c -= g; } return (q ? -1 : 1) * f * Math.pow(2, c - e); } function y(a, b, c, e, k, f) { var d, g = 8 * f - k - 1, n = (1 << g) - 1, h = n >> 1, q = 23 === k ? Math.pow(2, -24) - Math.pow(2, -77) : 0; f = e ? 0 : f - 1; var l = e ? 1 : -1, m = 0 > b || (0 === b && 0 > 1 / b) ? 1 : 0; b = Math.abs(b); isNaN(b) || Infinity === b ? ((b = isNaN(b) ? 1 : 0), (e = n)) : ((e = Math.floor(Math.log(b) / Math.LN2)), 1 > b * (d = Math.pow(2, -e)) && (e--, (d *= 2)), (b = 1 <= e + h ? b + q / d : b + q * Math.pow(2, 1 - h)), 2 <= b * d && (e++, (d /= 2)), e + h >= n ? ((b = 0), (e = n)) : 1 <= e + h ? ((b = (b * d - 1) * Math.pow(2, k)), (e += h)) : ((b = b * Math.pow(2, h - 1) * Math.pow(2, k)), (e = 0))); for (; 8 <= k; a[c + f] = b & 255, f += l, b /= 256, k -= 8); e = (e << k) | b; for (g += k; 0 < g; a[c + f] = e & 255, f += l, e /= 256, g -= 8); a[c + f - l] |= 128 * m; } var g = function (a, b, c) { "undefined" === typeof a && (a = g.DEFAULT_CAPACITY); "undefined" === typeof b && (b = g.DEFAULT_ENDIAN); "undefined" === typeof c && (c = g.DEFAULT_NOASSERT); if (!c) { a |= 0; if (0 > a) throw RangeError("Illegal capacity"); b = !!b; c = !!c; } this.buffer = 0 === a ? w : new ArrayBuffer(a); this.view = 0 === a ? null : new Uint8Array(this.buffer); this.offset = 0; this.markedOffset = -1; this.limit = a; this.littleEndian = b; this.noAssert = c; }; g.VERSION = "5.0.1"; g.LITTLE_ENDIAN = !0; g.BIG_ENDIAN = !1; g.DEFAULT_CAPACITY = 16; g.DEFAULT_ENDIAN = g.BIG_ENDIAN; g.DEFAULT_NOASSERT = !1; g.Long = h || null; var d = g.prototype; Object.defineProperty(d, "__isByteBuffer__", { value: !0, enumerable: !1, configurable: !1 }); var w = new ArrayBuffer(0), x = String.fromCharCode; g.accessor = function () { return Uint8Array; }; g.allocate = function (a, b, c) { return new g(a, b, c); }; g.concat = function (a, b, c, e) { if ("boolean" === typeof b || "string" !== typeof b) (e = c), (c = b), (b = void 0); for (var k = 0, f = 0, d = a.length, u; f < d; ++f) g.isByteBuffer(a[f]) || (a[f] = g.wrap(a[f], b)), (u = a[f].limit - a[f].offset), 0 < u && (k += u); if (0 === k) return new g(0, c, e); b = new g(k, c, e); for (f = 0; f < d; ) (c = a[f++]), (u = c.limit - c.offset), 0 >= u || (b.view.set(c.view.subarray(c.offset, c.limit), b.offset), (b.offset += u)); b.limit = b.offset; b.offset = 0; return b; }; g.isByteBuffer = function (a) { return !0 === (a && a.__isByteBuffer__); }; g.type = function () { return ArrayBuffer; }; g.wrap = function (a, b, c, e) { "string" !== typeof b && ((e = c), (c = b), (b = void 0)); if ("string" === typeof a) switch (("undefined" === typeof b && (b = "utf8"), b)) { case "base64": return g.fromBase64(a, c); case "hex": return g.fromHex(a, c); case "binary": return g.fromBinary(a, c); case "utf8": return g.fromUTF8(a, c); case "debug": return g.fromDebug(a, c); default: throw Error("Unsupported encoding: " + b); } if (null === a || "object" !== typeof a) throw TypeError("Illegal buffer"); if (g.isByteBuffer(a)) return (b = d.clone.call(a)), (b.markedOffset = -1), b; if (a instanceof Uint8Array) (b = new g(0, c, e)), 0 < a.length && ((b.buffer = a.buffer), (b.offset = a.byteOffset), (b.limit = a.byteOffset + a.byteLength), (b.view = new Uint8Array(a.buffer))); else if (a instanceof ArrayBuffer) (b = new g(0, c, e)), 0 < a.byteLength && ((b.buffer = a), (b.offset = 0), (b.limit = a.byteLength), (b.view = 0 < a.byteLength ? new Uint8Array(a) : null)); else if ("[object Array]" === Object.prototype.toString.call(a)) for (b = new g(a.length, c, e), b.limit = a.length, c = 0; c < a.length; ++c) b.view[c] = a[c]; else throw TypeError("Illegal buffer"); return b; }; d.writeBitSet = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if (!(a instanceof Array)) throw TypeError("Illegal BitSet: Not an array"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } var e = b, k = a.length, f = k >> 3, d = 0; for (b += this.writeVarint32(k, b); f--; ) { var g = (!!a[d++] & 1) | ((!!a[d++] & 1) << 1) | ((!!a[d++] & 1) << 2) | ((!!a[d++] & 1) << 3) | ((!!a[d++] & 1) << 4) | ((!!a[d++] & 1) << 5) | ((!!a[d++] & 1) << 6) | ((!!a[d++] & 1) << 7); this.writeByte(g, b++); } if (d < k) { for (g = f = 0; d < k; ) g |= (!!a[d++] & 1) << f++; this.writeByte(g, b++); } return c ? ((this.offset = b), this) : b - e; }; d.readBitSet = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); var c = this.readVarint32(a), e = c.value, k = e >> 3, f = 0, d = []; for (a += c.length; k--; ) (c = this.readByte(a++)), (d[f++] = !!(c & 1)), (d[f++] = !!(c & 2)), (d[f++] = !!(c & 4)), (d[f++] = !!(c & 8)), (d[f++] = !!(c & 16)), (d[f++] = !!(c & 32)), (d[f++] = !!(c & 64)), (d[f++] = !!(c & 128)); if (f < e) for (k = 0, c = this.readByte(a++); f < e; ) d[f++] = !!((c >> k++) & 1); b && (this.offset = a); return d; }; d.readBytes = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + a > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+" + a + ") <= " + this.buffer.byteLength); } b = this.slice(b, b + a); c && (this.offset += a); return b; }; d.writeInt8 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a |= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 1; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); this.view[b - 1] = a; c && (this.offset += 1); return this; }; d.writeByte = d.writeInt8; d.readInt8 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 1 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+1) <= " + this.buffer.byteLength); } a = this.view[a]; 128 === (a & 128) && (a = -(255 - a + 1)); b && (this.offset += 1); return a; }; d.readByte = d.readInt8; d.writeUint8 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 1; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); this.view[b - 1] = a; c && (this.offset += 1); return this; }; d.writeUInt8 = d.writeUint8; d.readUint8 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 1 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+1) <= " + this.buffer.byteLength); } a = this.view[a]; b && (this.offset += 1); return a; }; d.readUInt8 = d.readUint8; d.writeInt16 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a |= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 2; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= 2; this.littleEndian ? ((this.view[b + 1] = (a & 65280) >>> 8), (this.view[b] = a & 255)) : ((this.view[b] = (a & 65280) >>> 8), (this.view[b + 1] = a & 255)); c && (this.offset += 2); return this; }; d.writeShort = d.writeInt16; d.readInt16 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 2 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+2) <= " + this.buffer.byteLength); } if (this.littleEndian) { var c = this.view[a]; c |= this.view[a + 1] << 8; } else (c = this.view[a] << 8), (c |= this.view[a + 1]); 32768 === (c & 32768) && (c = -(65535 - c + 1)); b && (this.offset += 2); return c; }; d.readShort = d.readInt16; d.writeUint16 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 2; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= 2; this.littleEndian ? ((this.view[b + 1] = (a & 65280) >>> 8), (this.view[b] = a & 255)) : ((this.view[b] = (a & 65280) >>> 8), (this.view[b + 1] = a & 255)); c && (this.offset += 2); return this; }; d.writeUInt16 = d.writeUint16; d.readUint16 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 2 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+2) <= " + this.buffer.byteLength); } if (this.littleEndian) { var c = this.view[a]; c |= this.view[a + 1] << 8; } else (c = this.view[a] << 8), (c |= this.view[a + 1]); b && (this.offset += 2); return c; }; d.readUInt16 = d.readUint16; d.writeInt32 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a |= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 4; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= 4; this.littleEndian ? ((this.view[b + 3] = (a >>> 24) & 255), (this.view[b + 2] = (a >>> 16) & 255), (this.view[b + 1] = (a >>> 8) & 255), (this.view[b] = a & 255)) : ((this.view[b] = (a >>> 24) & 255), (this.view[b + 1] = (a >>> 16) & 255), (this.view[b + 2] = (a >>> 8) & 255), (this.view[b + 3] = a & 255)); c && (this.offset += 4); return this; }; d.writeInt = d.writeInt32; d.readInt32 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 4 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+4) <= " + this.buffer.byteLength); } if (this.littleEndian) { var c = this.view[a + 2] << 16; c |= this.view[a + 1] << 8; c |= this.view[a]; c += (this.view[a + 3] << 24) >>> 0; } else (c = this.view[a + 1] << 16), (c |= this.view[a + 2] << 8), (c |= this.view[a + 3]), (c += (this.view[a] << 24) >>> 0); b && (this.offset += 4); return c | 0; }; d.readInt = d.readInt32; d.writeUint32 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 4; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= 4; this.littleEndian ? ((this.view[b + 3] = (a >>> 24) & 255), (this.view[b + 2] = (a >>> 16) & 255), (this.view[b + 1] = (a >>> 8) & 255), (this.view[b] = a & 255)) : ((this.view[b] = (a >>> 24) & 255), (this.view[b + 1] = (a >>> 16) & 255), (this.view[b + 2] = (a >>> 8) & 255), (this.view[b + 3] = a & 255)); c && (this.offset += 4); return this; }; d.writeUInt32 = d.writeUint32; d.readUint32 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 4 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+4) <= " + this.buffer.byteLength); } if (this.littleEndian) { var c = this.view[a + 2] << 16; c |= this.view[a + 1] << 8; c |= this.view[a]; c += (this.view[a + 3] << 24) >>> 0; } else (c = this.view[a + 1] << 16), (c |= this.view[a + 2] << 8), (c |= this.view[a + 3]), (c += (this.view[a] << 24) >>> 0); b && (this.offset += 4); return c; }; d.readUInt32 = d.readUint32; h && ((d.writeInt64 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" === typeof a) a = h.fromNumber(a); else if ("string" === typeof a) a = h.fromString(a); else if (!(a && a instanceof h)) throw TypeError("Illegal value: " + a + " (not an integer or Long)"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } "number" === typeof a ? (a = h.fromNumber(a)) : "string" === typeof a && (a = h.fromString(a)); b += 8; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= 8; e = a.low; a = a.high; this.littleEndian ? ((this.view[b + 3] = (e >>> 24) & 255), (this.view[b + 2] = (e >>> 16) & 255), (this.view[b + 1] = (e >>> 8) & 255), (this.view[b] = e & 255), (b += 4), (this.view[b + 3] = (a >>> 24) & 255), (this.view[b + 2] = (a >>> 16) & 255), (this.view[b + 1] = (a >>> 8) & 255), (this.view[b] = a & 255)) : ((this.view[b] = (a >>> 24) & 255), (this.view[b + 1] = (a >>> 16) & 255), (this.view[b + 2] = (a >>> 8) & 255), (this.view[b + 3] = a & 255), (b += 4), (this.view[b] = (e >>> 24) & 255), (this.view[b + 1] = (e >>> 16) & 255), (this.view[b + 2] = (e >>> 8) & 255), (this.view[b + 3] = e & 255)); c && (this.offset += 8); return this; }), (d.writeLong = d.writeInt64), (d.readInt64 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 8 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+8) <= " + this.buffer.byteLength); } if (this.littleEndian) { var c = this.view[a + 2] << 16; c |= this.view[a + 1] << 8; c |= this.view[a]; c += (this.view[a + 3] << 24) >>> 0; a += 4; var e = this.view[a + 2] << 16; e |= this.view[a + 1] << 8; e |= this.view[a]; e += (this.view[a + 3] << 24) >>> 0; } else (e = this.view[a + 1] << 16), (e |= this.view[a + 2] << 8), (e |= this.view[a + 3]), (e += (this.view[a] << 24) >>> 0), (a += 4), (c = this.view[a + 1] << 16), (c |= this.view[a + 2] << 8), (c |= this.view[a + 3]), (c += (this.view[a] << 24) >>> 0); a = new h(c, e, !1); b && (this.offset += 8); return a; }), (d.readLong = d.readInt64), (d.writeUint64 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" === typeof a) a = h.fromNumber(a); else if ("string" === typeof a) a = h.fromString(a); else if (!(a && a instanceof h)) throw TypeError("Illegal value: " + a + " (not an integer or Long)"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } "number" === typeof a ? (a = h.fromNumber(a)) : "string" === typeof a && (a = h.fromString(a)); b += 8; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= 8; e = a.low; a = a.high; this.littleEndian ? ((this.view[b + 3] = (e >>> 24) & 255), (this.view[b + 2] = (e >>> 16) & 255), (this.view[b + 1] = (e >>> 8) & 255), (this.view[b] = e & 255), (b += 4), (this.view[b + 3] = (a >>> 24) & 255), (this.view[b + 2] = (a >>> 16) & 255), (this.view[b + 1] = (a >>> 8) & 255), (this.view[b] = a & 255)) : ((this.view[b] = (a >>> 24) & 255), (this.view[b + 1] = (a >>> 16) & 255), (this.view[b + 2] = (a >>> 8) & 255), (this.view[b + 3] = a & 255), (b += 4), (this.view[b] = (e >>> 24) & 255), (this.view[b + 1] = (e >>> 16) & 255), (this.view[b + 2] = (e >>> 8) & 255), (this.view[b + 3] = e & 255)); c && (this.offset += 8); return this; }), (d.writeUInt64 = d.writeUint64), (d.readUint64 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 8 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+8) <= " + this.buffer.byteLength); } if (this.littleEndian) { var c = this.view[a + 2] << 16; c |= this.view[a + 1] << 8; c |= this.view[a]; c += (this.view[a + 3] << 24) >>> 0; a += 4; var e = this.view[a + 2] << 16; e |= this.view[a + 1] << 8; e |= this.view[a]; e += (this.view[a + 3] << 24) >>> 0; } else (e = this.view[a + 1] << 16), (e |= this.view[a + 2] << 8), (e |= this.view[a + 3]), (e += (this.view[a] << 24) >>> 0), (a += 4), (c = this.view[a + 1] << 16), (c |= this.view[a + 2] << 8), (c |= this.view[a + 3]), (c += (this.view[a] << 24) >>> 0); a = new h(c, e, !0); b && (this.offset += 8); return a; }), (d.readUInt64 = d.readUint64)); d.writeFloat32 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a) throw TypeError("Illegal value: " + a + " (not a number)"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 4; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); y(this.view, a, b - 4, this.littleEndian, 23, 4); c && (this.offset += 4); return this; }; d.writeFloat = d.writeFloat32; d.readFloat32 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 4 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+4) <= " + this.buffer.byteLength); } a = v(this.view, a, this.littleEndian, 23, 4); b && (this.offset += 4); return a; }; d.readFloat = d.readFloat32; d.writeFloat64 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a) throw TypeError("Illegal value: " + a + " (not a number)"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } b += 8; var e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); y(this.view, a, b - 8, this.littleEndian, 52, 8); c && (this.offset += 8); return this; }; d.writeDouble = d.writeFloat64; d.readFloat64 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 8 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+8) <= " + this.buffer.byteLength); } a = v(this.view, a, this.littleEndian, 52, 8); b && (this.offset += 8); return a; }; d.readDouble = d.readFloat64; g.MAX_VARINT32_BYTES = 5; g.calculateVarint32 = function (a) { a >>>= 0; return 128 > a ? 1 : 16384 > a ? 2 : 2097152 > a ? 3 : 268435456 > a ? 4 : 5; }; g.zigZagEncode32 = function (a) { return (((a |= 0) << 1) ^ (a >> 31)) >>> 0; }; g.zigZagDecode32 = function (a) { return ((a >>> 1) ^ -(a & 1)) | 0; }; d.writeVarint32 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a |= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } var e = g.calculateVarint32(a); b += e; var k = this.buffer.byteLength; b > k && this.resize((k *= 2) > b ? k : b); b -= e; for (a >>>= 0; 128 <= a; ) (k = (a & 127) | 128), (this.view[b++] = k), (a >>>= 7); this.view[b++] = a; return c ? ((this.offset = b), this) : e; }; d.writeVarint32ZigZag = function (a, b) { return this.writeVarint32(g.zigZagEncode32(a), b); }; d.readVarint32 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 1 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+1) <= " + this.buffer.byteLength); } var c = 0, e = 0; do { if (!this.noAssert && a > this.limit) throw ((a = Error("Truncated")), (a.truncated = !0), a); var k = this.view[a++]; 5 > c && (e |= (k & 127) << (7 * c)); ++c; } while (0 !== (k & 128)); e |= 0; return b ? ((this.offset = a), e) : { value: e, length: c }; }; d.readVarint32ZigZag = function (a) { a = this.readVarint32(a); "object" === typeof a ? (a.value = g.zigZagDecode32(a.value)) : (a = g.zigZagDecode32(a)); return a; }; h && ((g.MAX_VARINT64_BYTES = 10), (g.calculateVarint64 = function (a) { "number" === typeof a ? (a = h.fromNumber(a)) : "string" === typeof a && (a = h.fromString(a)); var b = a.toInt() >>> 0, c = a.shiftRightUnsigned(28).toInt() >>> 0; a = a.shiftRightUnsigned(56).toInt() >>> 0; return 0 == a ? (0 == c ? (16384 > b ? (128 > b ? 1 : 2) : 2097152 > b ? 3 : 4) : 16384 > c ? (128 > c ? 5 : 6) : 2097152 > c ? 7 : 8) : 128 > a ? 9 : 10; }), (g.zigZagEncode64 = function (a) { "number" === typeof a ? (a = h.fromNumber(a, !1)) : "string" === typeof a ? (a = h.fromString(a, !1)) : !1 !== a.unsigned && (a = a.toSigned()); return a.shiftLeft(1).xor(a.shiftRight(63)).toUnsigned(); }), (g.zigZagDecode64 = function (a) { "number" === typeof a ? (a = h.fromNumber(a, !1)) : "string" === typeof a ? (a = h.fromString(a, !1)) : !1 !== a.unsigned && (a = a.toSigned()); return a.shiftRightUnsigned(1).xor(a.and(h.ONE).toSigned().negate()).toSigned(); }), (d.writeVarint64 = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" === typeof a) a = h.fromNumber(a); else if ("string" === typeof a) a = h.fromString(a); else if (!(a && a instanceof h)) throw TypeError("Illegal value: " + a + " (not an integer or Long)"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } "number" === typeof a ? (a = h.fromNumber(a, !1)) : "string" === typeof a ? (a = h.fromString(a, !1)) : !1 !== a.unsigned && (a = a.toSigned()); var e = g.calculateVarint64(a), k = a.toInt() >>> 0, f = a.shiftRightUnsigned(28).toInt() >>> 0; a = a.shiftRightUnsigned(56).toInt() >>> 0; b += e; var d = this.buffer.byteLength; b > d && this.resize((d *= 2) > b ? d : b); b -= e; switch (e) { case 10: this.view[b + 9] = (a >>> 7) & 1; case 9: this.view[b + 8] = 9 !== e ? a | 128 : a & 127; case 8: this.view[b + 7] = 8 !== e ? (f >>> 21) | 128 : (f >>> 21) & 127; case 7: this.view[b + 6] = 7 !== e ? (f >>> 14) | 128 : (f >>> 14) & 127; case 6: this.view[b + 5] = 6 !== e ? (f >>> 7) | 128 : (f >>> 7) & 127; case 5: this.view[b + 4] = 5 !== e ? f | 128 : f & 127; case 4: this.view[b + 3] = 4 !== e ? (k >>> 21) | 128 : (k >>> 21) & 127; case 3: this.view[b + 2] = 3 !== e ? (k >>> 14) | 128 : (k >>> 14) & 127; case 2: this.view[b + 1] = 2 !== e ? (k >>> 7) | 128 : (k >>> 7) & 127; case 1: this.view[b] = 1 !== e ? k | 128 : k & 127; } return c ? ((this.offset += e), this) : e; }), (d.writeVarint64ZigZag = function (a, b) { return this.writeVarint64(g.zigZagEncode64(a), b); }), (d.readVarint64 = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 1 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+1) <= " + this.buffer.byteLength); } var c = a, e = 0, k = 0; var d = this.view[a++]; var g = d & 127; if ( d & 128 && ((d = this.view[a++]), (g |= (d & 127) << 7), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (g |= (d & 127) << 14), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (g |= (d & 127) << 21), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (e = d & 127), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (e |= (d & 127) << 7), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (e |= (d & 127) << 14), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (e |= (d & 127) << 21), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (k = d & 127), d & 128 || (this.noAssert && "undefined" === typeof d)) && ((d = this.view[a++]), (k |= (d & 127) << 7), d & 128 || (this.noAssert && "undefined" === typeof d)) ) throw Error("Buffer overrun"); g = h.fromBits(g | (e << 28), (e >>> 4) | (k << 24), !1); return b ? ((this.offset = a), g) : { value: g, length: a - c }; }), (d.readVarint64ZigZag = function (a) { (a = this.readVarint64(a)) && a.value instanceof h ? (a.value = g.zigZagDecode64(a.value)) : (a = g.zigZagDecode64(a)); return a; })); d.writeCString = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); var e, d = a.length; if (!this.noAssert) { if ("string" !== typeof a) throw TypeError("Illegal str: Not a string"); for (e = 0; e < d; ++e) if (0 === a.charCodeAt(e)) throw RangeError("Illegal str: Contains NULL-characters"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } d = m.calculateUTF16asUTF8(l(a))[1]; b += d + 1; e = this.buffer.byteLength; b > e && this.resize((e *= 2) > b ? e : b); b -= d + 1; m.encodeUTF16toUTF8( l(a), function (a) { this.view[b++] = a; }.bind(this) ); this.view[b++] = 0; return c ? ((this.offset = b), this) : d; }; d.readCString = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 1 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+1) <= " + this.buffer.byteLength); } var c = a, e, d = -1; m.decodeUTF8toUTF16( function () { if (0 === d) return null; if (a >= this.limit) throw RangeError("Illegal range: Truncated data, " + a + " < " + this.limit); d = this.view[a++]; return 0 === d ? null : d; }.bind(this), (e = t()), !0 ); return b ? ((this.offset = a), e()) : { string: e(), length: a - c }; }; d.writeIString = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("string" !== typeof a) throw TypeError("Illegal str: Not a string"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } var e = b; var d = m.calculateUTF16asUTF8(l(a), this.noAssert)[1]; b += 4 + d; var f = this.buffer.byteLength; b > f && this.resize((f *= 2) > b ? f : b); b -= 4 + d; this.littleEndian ? ((this.view[b + 3] = (d >>> 24) & 255), (this.view[b + 2] = (d >>> 16) & 255), (this.view[b + 1] = (d >>> 8) & 255), (this.view[b] = d & 255)) : ((this.view[b] = (d >>> 24) & 255), (this.view[b + 1] = (d >>> 16) & 255), (this.view[b + 2] = (d >>> 8) & 255), (this.view[b + 3] = d & 255)); b += 4; m.encodeUTF16toUTF8( l(a), function (a) { this.view[b++] = a; }.bind(this) ); if (b !== e + 4 + d) throw RangeError("Illegal range: Truncated data, " + b + " == " + (b + 4 + d)); return c ? ((this.offset = b), this) : b - e; }; d.readIString = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 4 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+4) <= " + this.buffer.byteLength); } var c = a, e = this.readUint32(a); e = this.readUTF8String(e, g.METRICS_BYTES, (a += 4)); a += e.length; return b ? ((this.offset = a), e.string) : { string: e.string, length: a - c }; }; g.METRICS_CHARS = "c"; g.METRICS_BYTES = "b"; d.writeUTF8String = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } var e = b; var d = m.calculateUTF16asUTF8(l(a))[1]; b += d; var f = this.buffer.byteLength; b > f && this.resize((f *= 2) > b ? f : b); b -= d; m.encodeUTF16toUTF8( l(a), function (a) { this.view[b++] = a; }.bind(this) ); return c ? ((this.offset = b), this) : b - e; }; d.writeString = d.writeUTF8String; g.calculateUTF8Chars = function (a) { return m.calculateUTF16asUTF8(l(a))[0]; }; g.calculateUTF8Bytes = function (a) { return m.calculateUTF16asUTF8(l(a))[1]; }; g.calculateString = g.calculateUTF8Bytes; d.readUTF8String = function (a, b, c) { "number" === typeof b && ((c = b), (b = void 0)); var e = "undefined" === typeof c; e && (c = this.offset); "undefined" === typeof b && (b = g.METRICS_CHARS); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal length: " + a + " (not an integer)"); a |= 0; if ("number" !== typeof c || 0 !== c % 1) throw TypeError("Illegal offset: " + c + " (not an integer)"); c >>>= 0; if (0 > c || c + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + c + " (+0) <= " + this.buffer.byteLength); } var d = 0, f = c; if (b === g.METRICS_CHARS) { var p = t(); m.decodeUTF8( function () { return d < a && c < this.limit ? this.view[c++] : null; }.bind(this), function (a) { ++d; m.UTF8toUTF16(a, p); } ); if (d !== a) throw RangeError("Illegal range: Truncated data, " + d + " == " + a); return e ? ((this.offset = c), p()) : { string: p(), length: c - f }; } if (b === g.METRICS_BYTES) { if (!this.noAssert) { if ("number" !== typeof c || 0 !== c % 1) throw TypeError("Illegal offset: " + c + " (not an integer)"); c >>>= 0; if (0 > c || c + a > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + c + " (+" + a + ") <= " + this.buffer.byteLength); } var h = c + a; m.decodeUTF8toUTF16( function () { return c < h ? this.view[c++] : null; }.bind(this), (p = t()), this.noAssert ); if (c !== h) throw RangeError("Illegal range: Truncated data, " + c + " == " + h); return e ? ((this.offset = c), p()) : { string: p(), length: c - f }; } throw TypeError("Unsupported metrics: " + b); }; d.readString = d.readUTF8String; d.writeVString = function (a, b) { var c = "undefined" === typeof b; c && (b = this.offset); if (!this.noAssert) { if ("string" !== typeof a) throw TypeError("Illegal str: Not a string"); if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: " + b + " (not an integer)"); b >>>= 0; if (0 > b || b + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + b + " (+0) <= " + this.buffer.byteLength); } var e = b; var d = m.calculateUTF16asUTF8(l(a), this.noAssert)[1]; var f = g.calculateVarint32(d); b += f + d; var p = this.buffer.byteLength; b > p && this.resize((p *= 2) > b ? p : b); b -= f + d; b += this.writeVarint32(d, b); m.encodeUTF16toUTF8( l(a), function (a) { this.view[b++] = a; }.bind(this) ); if (b !== e + d + f) throw RangeError("Illegal range: Truncated data, " + b + " == " + (b + d + f)); return c ? ((this.offset = b), this) : b - e; }; d.readVString = function (a) { var b = "undefined" === typeof a; b && (a = this.offset); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 1 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+1) <= " + this.buffer.byteLength); } var c = a, e = this.readVarint32(a); e = this.readUTF8String(e.value, g.METRICS_BYTES, (a += e.length)); a += e.length; return b ? ((this.offset = a), e.string) : { string: e.string, length: a - c }; }; d.append = function (a, b, c) { if ("number" === typeof b || "string" !== typeof b) (c = b), (b = void 0); var e = "undefined" === typeof c; e && (c = this.offset); if (!this.noAssert) { if ("number" !== typeof c || 0 !== c % 1) throw TypeError("Illegal offset: " + c + " (not an integer)"); c >>>= 0; if (0 > c || c + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + c + " (+0) <= " + this.buffer.byteLength); } a instanceof g || (a = g.wrap(a, b)); b = a.limit - a.offset; if (0 >= b) return this; c += b; var d = this.buffer.byteLength; c > d && this.resize((d *= 2) > c ? d : c); c -= b; this.view.set(a.view.subarray(a.offset, a.limit), c); a.offset += b; e && (this.offset += b); return this; }; d.appendTo = function (a, b) { a.append(this, b); return this; }; d.writeBytes = d.append; d.assert = function (a) { this.noAssert = !a; return this; }; d.capacity = function () { return this.buffer.byteLength; }; d.clear = function () { this.offset = 0; this.limit = this.buffer.byteLength; this.markedOffset = -1; return this; }; d.clone = function (a) { var b = new g(0, this.littleEndian, this.noAssert); a ? ((b.buffer = new ArrayBuffer(this.buffer.byteLength)), (b.view = new Uint8Array(b.buffer))) : ((b.buffer = this.buffer), (b.view = this.view)); b.offset = this.offset; b.markedOffset = this.markedOffset; b.limit = this.limit; return b; }; d.compact = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal begin: Not an integer"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal end: Not an integer"); b >>>= 0; if (0 > a || a > b || b > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + a + " <= " + b + " <= " + this.buffer.byteLength); } if (0 === a && b === this.buffer.byteLength) return this; var c = b - a; if (0 === c) return (this.buffer = w), (this.view = null), 0 <= this.markedOffset && (this.markedOffset -= a), (this.limit = this.offset = 0), this; var e = new ArrayBuffer(c), d = new Uint8Array(e); d.set(this.view.subarray(a, b)); this.buffer = e; this.view = d; 0 <= this.markedOffset && (this.markedOffset -= a); this.offset = 0; this.limit = c; return this; }; d.copy = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal begin: Not an integer"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal end: Not an integer"); b >>>= 0; if (0 > a || a > b || b > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + a + " <= " + b + " <= " + this.buffer.byteLength); } if (a === b) return new g(0, this.littleEndian, this.noAssert); var c = b - a, e = new g(c, this.littleEndian, this.noAssert); e.offset = 0; e.limit = c; 0 <= e.markedOffset && (e.markedOffset -= a); this.copyTo(e, 0, a, b); return e; }; d.copyTo = function (a, b, c, e) { var d, f; if (!this.noAssert && !g.isByteBuffer(a)) throw TypeError("Illegal target: Not a ByteBuffer"); b = (f = "undefined" === typeof b) ? a.offset : b | 0; c = (d = "undefined" === typeof c) ? this.offset : c | 0; e = "undefined" === typeof e ? this.limit : e | 0; if (0 > b || b > a.buffer.byteLength) throw RangeError("Illegal target range: 0 <= " + b + " <= " + a.buffer.byteLength); if (0 > c || e > this.buffer.byteLength) throw RangeError("Illegal source range: 0 <= " + c + " <= " + this.buffer.byteLength); var p = e - c; if (0 === p) return a; a.ensureCapacity(b + p); a.view.set(this.view.subarray(c, e), b); d && (this.offset += p); f && (a.offset += p); return this; }; d.ensureCapacity = function (a) { var b = this.buffer.byteLength; return b < a ? this.resize((b *= 2) > a ? b : a) : this; }; d.fill = function (a, b, c) { var e = "undefined" === typeof b; e && (b = this.offset); "string" === typeof a && 0 < a.length && (a = a.charCodeAt(0)); "undefined" === typeof b && (b = this.offset); "undefined" === typeof c && (c = this.limit); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal value: " + a + " (not an integer)"); a |= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal begin: Not an integer"); b >>>= 0; if ("number" !== typeof c || 0 !== c % 1) throw TypeError("Illegal end: Not an integer"); c >>>= 0; if (0 > b || b > c || c > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + b + " <= " + c + " <= " + this.buffer.byteLength); } if (b >= c) return this; for (; b < c; ) this.view[b++] = a; e && (this.offset = b); return this; }; d.flip = function () { this.limit = this.offset; this.offset = 0; return this; }; d.mark = function (a) { a = "undefined" === typeof a ? this.offset : a; if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal offset: " + a + " (not an integer)"); a >>>= 0; if (0 > a || a + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + a + " (+0) <= " + this.buffer.byteLength); } this.markedOffset = a; return this; }; d.order = function (a) { if (!this.noAssert && "boolean" !== typeof a) throw TypeError("Illegal littleEndian: Not a boolean"); this.littleEndian = !!a; return this; }; d.LE = function (a) { this.littleEndian = "undefined" !== typeof a ? !!a : !0; return this; }; d.BE = function (a) { this.littleEndian = "undefined" !== typeof a ? !a : !1; return this; }; d.prepend = function (a, b, c) { if ("number" === typeof b || "string" !== typeof b) (c = b), (b = void 0); var e = "undefined" === typeof c; e && (c = this.offset); if (!this.noAssert) { if ("number" !== typeof c || 0 !== c % 1) throw TypeError("Illegal offset: " + c + " (not an integer)"); c >>>= 0; if (0 > c || c + 0 > this.buffer.byteLength) throw RangeError("Illegal offset: 0 <= " + c + " (+0) <= " + this.buffer.byteLength); } a instanceof g || (a = g.wrap(a, b)); b = a.limit - a.offset; if (0 >= b) return this; var d = b - c; if (0 < d) { var f = new ArrayBuffer(this.buffer.byteLength + d), p = new Uint8Array(f); p.set(this.view.subarray(c, this.buffer.byteLength), b); this.buffer = f; this.view = p; this.offset += d; 0 <= this.markedOffset && (this.markedOffset += d); this.limit += d; c += d; } else new Uint8Array(this.buffer); this.view.set(a.view.subarray(a.offset, a.limit), c - b); a.offset = a.limit; e && (this.offset -= b); return this; }; d.prependTo = function (a, b) { a.prepend(this, b); return this; }; d.printDebug = function (a) { "function" !== typeof a && (a = console.log.bind(console)); a(this.toString() + "\n-------------------------------------------------------------------\n" + this.toDebug(!0)); }; d.remaining = function () { return this.limit - this.offset; }; d.reset = function () { 0 <= this.markedOffset ? ((this.offset = this.markedOffset), (this.markedOffset = -1)) : (this.offset = 0); return this; }; d.resize = function (a) { if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal capacity: " + a + " (not an integer)"); a |= 0; if (0 > a) throw RangeError("Illegal capacity: 0 <= " + a); } if (this.buffer.byteLength < a) { a = new ArrayBuffer(a); var b = new Uint8Array(a); b.set(this.view); this.buffer = a; this.view = b; } return this; }; d.reverse = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal begin: Not an integer"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal end: Not an integer"); b >>>= 0; if (0 > a || a > b || b > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + a + " <= " + b + " <= " + this.buffer.byteLength); } if (a === b) return this; Array.prototype.reverse.call(this.view.subarray(a, b)); return this; }; d.skip = function (a) { if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal length: " + a + " (not an integer)"); a |= 0; } var b = this.offset + a; if (!this.noAssert && (0 > b || b > this.buffer.byteLength)) throw RangeError("Illegal length: 0 <= " + this.offset + " + " + a + " <= " + this.buffer.byteLength); this.offset = b; return this; }; d.slice = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal begin: Not an integer"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal end: Not an integer"); b >>>= 0; if (0 > a || a > b || b > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + a + " <= " + b + " <= " + this.buffer.byteLength); } var c = this.clone(); c.offset = a; c.limit = b; return c; }; d.toBuffer = function (a) { var b = this.offset, c = this.limit; if (!this.noAssert) { if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal offset: Not an integer"); b >>>= 0; if ("number" !== typeof c || 0 !== c % 1) throw TypeError("Illegal limit: Not an integer"); c >>>= 0; if (0 > b || b > c || c > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + b + " <= " + c + " <= " + this.buffer.byteLength); } if (!a && 0 === b && c === this.buffer.byteLength) return this.buffer; if (b === c) return w; a = new ArrayBuffer(c - b); new Uint8Array(a).set(new Uint8Array(this.buffer).subarray(b, c), 0); return a; }; d.toArrayBuffer = d.toBuffer; d.toString = function (a, b, c) { if ("undefined" === typeof a) return "ByteBufferAB(offset=" + this.offset + ",markedOffset=" + this.markedOffset + ",limit=" + this.limit + ",capacity=" + this.capacity() + ")"; "number" === typeof a && (c = b = a = "utf8"); switch (a) { case "utf8": return this.toUTF8(b, c); case "base64": return this.toBase64(b, c); case "hex": return this.toHex(b, c); case "binary": return this.toBinary(b, c); case "debug": return this.toDebug(); case "columns": return this.toColumns(); default: throw Error("Unsupported encoding: " + a); } }; var z = (function () { for ( var a = {}, b = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, ], c = [], e = 0, d = b.length; e < d; ++e ) c[b[e]] = e; a.encode = function (a, c) { for (var e, d; null !== (e = a()); ) c(b[(e >> 2) & 63]), (d = (e & 3) << 4), null !== (e = a()) ? ((d |= (e >> 4) & 15), c(b[(d | ((e >> 4) & 15)) & 63]), (d = (e & 15) << 2), null !== (e = a()) ? (c(b[(d | ((e >> 6) & 3)) & 63]), c(b[e & 63])) : (c(b[d & 63]), c(61))) : (c(b[d & 63]), c(61), c(61)); }; a.decode = function (a, b) { function e(a) { throw Error("Illegal character code: " + a); } for (var d, k, f; null !== (d = a()); ) if (((k = c[d]), "undefined" === typeof k && e(d), null !== (d = a()) && ((f = c[d]), "undefined" === typeof f && e(d), b(((k << 2) >>> 0) | ((f & 48) >> 4)), null !== (d = a())))) { k = c[d]; if ("undefined" === typeof k) if (61 === d) break; else e(d); b((((f & 15) << 4) >>> 0) | ((k & 60) >> 2)); if (null !== (d = a())) { f = c[d]; if ("undefined" === typeof f) if (61 === d) break; else e(d); b((((k & 3) << 6) >>> 0) | f); } } }; a.test = function (a) { return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(a); }; return a; })(); d.toBase64 = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); a |= 0; b |= 0; if (0 > a || b > this.capacity || a > b) throw RangeError("begin, end"); var c; z.encode( function () { return a < b ? this.view[a++] : null; }.bind(this), (c = t()) ); return c(); }; g.fromBase64 = function (a, b) { if ("string" !== typeof a) throw TypeError("str"); var c = new g((a.length / 4) * 3, b), e = 0; z.decode(l(a), function (a) { c.view[e++] = a; }); c.limit = e; return c; }; g.btoa = function (a) { return g.fromBinary(a).toBase64(); }; g.atob = function (a) { return g.fromBase64(a).toBinary(); }; d.toBinary = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); a |= 0; b |= 0; if (0 > a || b > this.capacity() || a > b) throw RangeError("begin, end"); if (a === b) return ""; for (var c = [], e = []; a < b; ) c.push(this.view[a++]), 1024 <= c.length && (e.push(String.fromCharCode.apply(String, c)), (c = [])); return e.join("") + String.fromCharCode.apply(String, c); }; g.fromBinary = function (a, b) { if ("string" !== typeof a) throw TypeError("str"); for (var c = 0, e = a.length, d = new g(e, b); c < e; ) { b = a.charCodeAt(c); if (255 < b) throw RangeError("illegal char code: " + b); d.view[c++] = b; } d.limit = e; return d; }; d.toDebug = function (a) { for (var b = -1, c = this.buffer.byteLength, e, d = "", f = "", g = ""; b < c; ) { -1 !== b && ((e = this.view[b]), (d = 16 > e ? d + ("0" + e.toString(16).toUpperCase()) : d + e.toString(16).toUpperCase()), a && (f += 32 < e && 127 > e ? String.fromCharCode(e) : ".")); ++b; if (a && 0 < b && 0 === b % 16 && b !== c) { for (; 51 > d.length; ) d += " "; g += d + f + "\n"; d = f = ""; } d = b === this.offset && b === this.limit ? d + (b === this.markedOffset ? "!" : "|") : b === this.offset ? d + (b === this.markedOffset ? "[" : "<") : b === this.limit ? d + (b === this.markedOffset ? "]" : ">") : d + (b === this.markedOffset ? "'" : a || (0 !== b && b !== c) ? " " : ""); } if (a && " " !== d) { for (; 51 > d.length; ) d += " "; g += d + f + "\n"; } return a ? g : d; }; g.fromDebug = function (a, b, c) { var e = a.length; b = new g(((e + 1) / 3) | 0, b, c); for (var d = 0, f = 0, h, l = !1, n = !1, m = !1, q = !1, r = !1; d < e; ) { switch ((h = a.charAt(d++))) { case "!": if (!c) { if (n || m || q) { r = !0; break; } n = m = q = !0; } b.offset = b.markedOffset = b.limit = f; l = !1; break; case "|": if (!c) { if (n || q) { r = !0; break; } n = q = !0; } b.offset = b.limit = f; l = !1; break; case "[": if (!c) { if (n || m) { r = !0; break; } n = m = !0; } b.offset = b.markedOffset = f; l = !1; break; case "<": if (!c) { if (n) { r = !0; break; } n = !0; } b.offset = f; l = !1; break; case "]": if (!c) { if (q || m) { r = !0; break; } q = m = !0; } b.limit = b.markedOffset = f; l = !1; break; case ">": if (!c) { if (q) { r = !0; break; } q = !0; } b.limit = f; l = !1; break; case "'": if (!c) { if (m) { r = !0; break; } m = !0; } b.markedOffset = f; l = !1; break; case " ": l = !1; break; default: if (!c && l) r = !0; else { h = parseInt(h + a.charAt(d++), 16); if (!c && (isNaN(h) || 0 > h || 255 < h)) throw TypeError("Illegal str: Not a debug encoded string"); b.view[f++] = h; l = !0; } } if (r) throw TypeError("Illegal str: Invalid symbol at " + d); } if (!c) { if (!n || !q) throw TypeError("Illegal str: Missing offset or limit"); if (f < b.buffer.byteLength) throw TypeError("Illegal str: Not a debug encoded string (is it hex?) " + f + " < " + e); } return b; }; d.toHex = function (a, b) { a = "undefined" === typeof a ? this.offset : a; b = "undefined" === typeof b ? this.limit : b; if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal begin: Not an integer"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal end: Not an integer"); b >>>= 0; if (0 > a || a > b || b > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + a + " <= " + b + " <= " + this.buffer.byteLength); } for (var c = Array(b - a), e; a < b; ) (e = this.view[a++]), 16 > e ? c.push("0", e.toString(16)) : c.push(e.toString(16)); return c.join(""); }; g.fromHex = function (a, b, c) { if (!c) { if ("string" !== typeof a) throw TypeError("Illegal str: Not a string"); if (0 !== a.length % 2) throw TypeError("Illegal str: Length not a multiple of 2"); } var e = a.length; b = new g((e / 2) | 0, b); for (var d, f = 0, h = 0; f < e; f += 2) { d = parseInt(a.substring(f, f + 2), 16); if (!c && (!isFinite(d) || 0 > d || 255 < d)) throw TypeError("Illegal str: Contains non-hex characters"); b.view[h++] = d; } b.limit = h; return b; }; var m = (function () { var a = { MAX_CODEPOINT: 1114111, encodeUTF8: function (a, c) { var b = null; "number" === typeof a && ((b = a), (a = function () { return null; })); for (; null !== b || null !== (b = a()); ) 128 > b ? c(b & 127) : (2048 > b ? c(((b >> 6) & 31) | 192) : 65536 > b ? (c(((b >> 12) & 15) | 224), c(((b >> 6) & 63) | 128)) : (c(((b >> 18) & 7) | 240), c(((b >> 12) & 63) | 128), c(((b >> 6) & 63) | 128)), c((b & 63) | 128)), (b = null); }, decodeUTF8: function (a, c) { for ( var b, d, f, g, h = function (a) { a = a.slice(0, a.indexOf(null)); var b = Error(a.toString()); b.name = "TruncatedError"; b.bytes = a; throw b; }; null !== (b = a()); ) if (0 === (b & 128)) c(b); else if (192 === (b & 224)) null === (d = a()) && h([b, d]), c(((b & 31) << 6) | (d & 63)); else if (224 === (b & 240)) (null !== (d = a()) && null !== (f = a())) || h([b, d, f]), c(((b & 15) << 12) | ((d & 63) << 6) | (f & 63)); else if (240 === (b & 248)) (null !== (d = a()) && null !== (f = a()) && null !== (g = a())) || h([b, d, f, g]), c(((b & 7) << 18) | ((d & 63) << 12) | ((f & 63) << 6) | (g & 63)); else throw RangeError("Illegal starting byte: " + b); }, UTF16toUTF8: function (a, c) { for (var b, d = null; null !== (b = null !== d ? d : a()); ) 55296 <= b && 57343 >= b && null !== (d = a()) && 56320 <= d && 57343 >= d ? (c(1024 * (b - 55296) + d - 56320 + 65536), (d = null)) : c(b); null !== d && c(d); }, UTF8toUTF16: function (a, c) { var b = null; "number" === typeof a && ((b = a), (a = function () { return null; })); for (; null !== b || null !== (b = a()); ) 65535 >= b ? c(b) : ((b -= 65536), c((b >> 10) + 55296), c((b % 1024) + 56320)), (b = null); }, encodeUTF16toUTF8: function (b, c) { a.UTF16toUTF8(b, function (b) { a.encodeUTF8(b, c); }); }, decodeUTF8toUTF16: function (b, c) { a.decodeUTF8(b, function (b) { a.UTF8toUTF16(b, c); }); }, calculateCodePoint: function (a) { return 128 > a ? 1 : 2048 > a ? 2 : 65536 > a ? 3 : 4; }, calculateUTF8: function (a) { for (var b, d = 0; null !== (b = a()); ) d += 128 > b ? 1 : 2048 > b ? 2 : 65536 > b ? 3 : 4; return d; }, calculateUTF16asUTF8: function (b) { var c = 0, d = 0; a.UTF16toUTF8(b, function (a) { ++c; d += 128 > a ? 1 : 2048 > a ? 2 : 65536 > a ? 3 : 4; }); return [c, d]; }, }; return a; })(); d.toUTF8 = function (a, b) { "undefined" === typeof a && (a = this.offset); "undefined" === typeof b && (b = this.limit); if (!this.noAssert) { if ("number" !== typeof a || 0 !== a % 1) throw TypeError("Illegal begin: Not an integer"); a >>>= 0; if ("number" !== typeof b || 0 !== b % 1) throw TypeError("Illegal end: Not an integer"); b >>>= 0; if (0 > a || a > b || b > this.buffer.byteLength) throw RangeError("Illegal range: 0 <= " + a + " <= " + b + " <= " + this.buffer.byteLength); } var c; try { m.decodeUTF8toUTF16( function () { return a < b ? this.view[a++] : null; }.bind(this), (c = t()) ); } catch (e) { if (a !== b) throw RangeError("Illegal range: Truncated data, " + a + " != " + b); } return c(); }; g.fromUTF8 = function (a, b, c) { if (!c && "string" !== typeof a) throw TypeError("Illegal str: Not a string"); var d = new g(m.calculateUTF16asUTF8(l(a), !0)[1], b, c), h = 0; m.encodeUTF16toUTF8(l(a), function (a) { d.view[h++] = a; }); d.limit = h; return d; }; return g; }); const { ByteBuffer } = dcodeIO; /** * Priority Queue Implementation (Min-Heap) * Stores elements with associated priorities (keys in D* Lite). */ class PriorityQueue { constructor() { // Array to store heap elements: { element: any, priority: number[] } this.heap = []; } // Helper function to compare priorities (keys in D* Lite) // Key: [k1, k2] where k1 = min(g, rhs) + h, k2 = min(g, rhs) compare(keyA, keyB) { if (keyA[0] < keyB[0]) return -1; if (keyA[0] > keyB[0]) return 1; // If k1 is equal, compare k2 if (keyA[1] < keyB[1]) return -1; if (keyA[1] > keyB[1]) return 1; return 0; // Keys are equal } // Add an element with its priority push(element, priority) { this.heap.push({ element, priority }); this.bubbleUp(this.heap.length - 1); } // Remove and return the element with the lowest priority pop() { if (this.isEmpty()) { return null; } this.swap(0, this.heap.length - 1); const item = this.heap.pop(); if (!this.isEmpty()) { this.bubbleDown(0); } return item.element; // Return only the element } // Get the element with the lowest priority without removing it peek() { return this.isEmpty() ? null : this.heap[0]; // Return the full {element, priority} object } // Update the priority of an element (requires finding the element first) update(element, newPriority) { for (let i = 0; i < this.heap.length; i++) { if (this.heap[i].element === element) { // Simple equality check, might need adjustment for complex objects const oldPriority = this.heap[i].priority; this.heap[i].priority = newPriority; // Decide whether to bubble up or down based on priority change if (this.compare(newPriority, oldPriority) < 0) { this.bubbleUp(i); } else { this.bubbleDown(i); } return; } } // If element not found, push it (optional, depends on use case) // this.push(element, newPriority); } // Remove an element from the queue remove(element) { for (let i = 0; i < this.heap.length; i++) { if (this.heap[i].element === element) { this.swap(i, this.heap.length - 1); this.heap.pop(); if (i < this.heap.length) { // Check if the swapped element is still in the heap // Need to potentially bubble up or down the element that was swapped into position i // Re-heapify is safer if unsure if (!this.isEmpty()) { // Try bubbling up first, then down if necessary if (i > 0 && this.compare(this.heap[i].priority, this.heap[this.parent(i)].priority) < 0) { this.bubbleUp(i); } else { this.bubbleDown(i); } } } return true; // Indicate removal was successful } } return false; // Element not found } // Check if the queue is empty isEmpty() { return this.heap.length === 0; } // Get the size of the queue size() { return this.heap.length; } // Check if an element exists in the queue includes(element) { return this.heap.some(item => item.element === element); // Simple equality check } // --- Heap Helper Methods --- parent(index) { return Math.floor((index - 1) / 2); } leftChild(index) { return 2 * index + 1; } rightChild(index) { return 2 * index + 2; } swap(i, j) { [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]]; } bubbleUp(index) { while (index > 0) { const parentIndex = this.parent(index); if (this.compare(this.heap[index].priority, this.heap[parentIndex].priority) < 0) { this.swap(index, parentIndex); index = parentIndex; } else { break; } } } bubbleDown(index) { const size = this.heap.length; while (true) { let smallestIndex = index; const leftIndex = this.leftChild(index); const rightIndex = this.rightChild(index); if (leftIndex < size && this.compare(this.heap[leftIndex].priority, this.heap[smallestIndex].priority) < 0) { smallestIndex = leftIndex; } if (rightIndex < size && this.compare(this.heap[rightIndex].priority, this.heap[smallestIndex].priority) < 0) { smallestIndex = rightIndex; } if (smallestIndex !== index) { this.swap(index, smallestIndex); index = smallestIndex; } else { break; } } } } /** * D* Lite Algorithm Implementation */ class DStarLite { constructor(gridWidth, gridHeight) { this.gridWidth = gridWidth; this.gridHeight = gridHeight; // --- Core D* Lite Data Structures --- // Store g, rhs, and cell data. Key: "x,y" string this.cellData = new Map(); // Priority queue (U) storing nodes to be expanded this.openList = new PriorityQueue(); // Key modifier for dynamic updates this.km = 0; // --- Start and Goal --- this.start = null; // {x, y} this.goal = null; // {x, y} this.lastStart = null; // Keep track of the last start position for km updates // --- Obstacles --- // Set of obstacle coordinates (Key: "x,y" string) this.obstacles = new Set(); // Store costs of edges that have changed (for dynamic updates) // Key: "x1,y1_x2,y2", Value: new_cost this.updatedEdgeCosts = new Map(); // --- Heuristic --- // Using Manhattan distance for grid environments this.heuristic = (a, b) => { return Math.abs(a.x - b.x) + Math.abs(a.y - b.y); }; // --- Cost Function --- // Cost between adjacent cells (can be dynamic) this.cost = (nodeA, nodeB) => { const edgeKey = `${nodeA.x},${nodeA.y}_${nodeB.x},${nodeB.y}`; const reverseEdgeKey = `${nodeB.x},${nodeB.y}_${nodeA.x},${nodeA.y}`; // Check if this specific edge has an updated cost if (this.updatedEdgeCosts.has(edgeKey)) { return this.updatedEdgeCosts.get(edgeKey); } if (this.updatedEdgeCosts.has(reverseEdgeKey)) { return this.updatedEdgeCosts.get(reverseEdgeKey); } // Check if either node is an obstacle if (this.isObstacle(nodeA) || this.isObstacle(nodeB)) { return Infinity; // Moving into or out of an obstacle has infinite cost } // Basic cost for adjacent non-obstacle cells (can be adjusted e.g., for diagonals) return 1; }; } // --- Getters for Node Data (with default values) --- getKey(node) { const nodeStr = `${node.x},${node.y}`; return nodeStr; } getG(node) { return this.cellData.get(this.getKey(node))?.g ?? Infinity; } getRHS(node) { return this.cellData.get(this.getKey(node))?.rhs ?? Infinity; } setG(node, value) { const key = this.getKey(node); if (!this.cellData.has(key)) this.cellData.set(key, {}); this.cellData.get(key).g = value; } setRHS(node, value) { const key = this.getKey(node); if (!this.cellData.has(key)) this.cellData.set(key, {}); this.cellData.get(key).rhs = value; } // --- Obstacle Management --- addObstacle(x, y) { this.obstacles.add(`${x},${y}`); } removeObstacle(x, y) { this.obstacles.delete(`${x},${y}`); } isObstacle(node) { return this.obstacles.has(this.getKey(node)); } // --- Initialization --- initialize(start, goal) { this.openList = new PriorityQueue(); this.cellData = new Map(); this.km = 0; this.start = start; this.goal = goal; this.lastStart = start; // Initialize lastStart // Initialize goal node this.setRHS(this.goal, 0); this.setG(this.goal, Infinity); // g(goal) is initially infinity // Add goal to the open list this.openList.push(this.goal, this.calculateKey(this.goal)); } // --- Core D* Lite Functions --- // Calculate the key (priority) for a node calculateKey(node) { const g = this.getG(node); const rhs = this.getRHS(node); const minVal = Math.min(g, rhs); // Key: [k1, k2] // k1 = min(g, rhs) + h(start, node) + km // k2 = min(g, rhs) return [minVal + this.heuristic(this.start, node) + this.km, minVal]; } // Update the rhs value of a node and manage its presence in the open list updateVertex(node) { const nodeKey = this.getKey(node); // Don't update the goal node's rhs based on neighbors if (nodeKey !== this.getKey(this.goal)) { let minRhs = Infinity; const neighbors = this.getNeighbors(node); for (const successor of neighbors) { const costVal = this.cost(node, successor); if (costVal === Infinity) continue; // Skip impassable successors minRhs = Math.min(minRhs, costVal + this.getG(successor)); } this.setRHS(node, minRhs); } // Remove node from open list if it's already there if (this.openList.includes(node)) { this.openList.remove(node); } // If node is locally inconsistent (g != rhs), add it to the open list if (this.getG(node) !== this.getRHS(node)) { // Check if it's an obstacle before adding if (!this.isObstacle(node)) { this.openList.push(node, this.calculateKey(node)); } } } // Compute the shortest path from start to goal computeShortestPath() { if (!this.start || !this.goal) { console.error("Start or Goal not set!"); return false; // Indicate failure } // Continue while the start node is inconsistent or the top key is less than start's key while ( !this.openList.isEmpty() && (this.openList.compare(this.openList.peek().priority, this.calculateKey(this.start)) < 0 || this.getRHS(this.start) !== this.getG(this.start)) ) { const topItem = this.openList.peek(); // { element: node, priority: key } if (!topItem) break; // Should not happen if !isEmpty, but safety check const k_old = topItem.priority; const u = topItem.element; // The node to process const k_new = this.calculateKey(u); // Condition 1: Node needs update (key increased) if (this.openList.compare(k_old, k_new) < 0) { this.openList.update(u, k_new); } // Condition 2: Node is locally consistent (g > rhs) -> Make it overconsistent else if (this.getG(u) > this.getRHS(u)) { this.setG(u, this.getRHS(u)); this.openList.pop(); // Remove u from queue // Update predecessors (neighbors) const predecessors = this.getNeighbors(u); // In grid, neighbors are predecessors for (const pred of predecessors) { if (!this.isObstacle(pred)) { // Only update non-obstacle neighbors this.updateVertex(pred); } } } // Condition 3: Node is locally inconsistent (g < rhs) -> Make it consistent or overconsistent else { const g_old = this.getG(u); this.setG(u, Infinity); // Make g infinite temporarily // Update u itself first if (!this.isObstacle(u)) { this.updateVertex(u); } // Update predecessors (neighbors) const predecessors = this.getNeighbors(u); for (const pred of predecessors) { if (!this.isObstacle(pred)) { this.updateVertex(pred); } } } // Safety break for potential infinite loops (e.g., if goal is unreachable) // A better approach would be to limit iterations or detect cycles if needed. // if (iterations++ > MAX_ITERATIONS) { console.warn("Max iterations reached"); break; } } // Check if a path was found return this.getRHS(this.start) !== Infinity; } // --- Path Reconstruction --- getPath() { if (!this.start || !this.goal || this.getG(this.start) === Infinity) { return []; // No path found or algorithm not run/finished } const path = [this.start]; let current = this.start; while (this.getKey(current) !== this.getKey(this.goal)) { let bestNextNode = null; let minCost = Infinity; const neighbors = this.getNeighbors(current); for (const neighbor of neighbors) { if (this.isObstacle(neighbor)) continue; // Skip obstacles const costVal = this.cost(current, neighbor); if (costVal === Infinity) continue; // Skip impassable edges const costToGo = costVal + this.getG(neighbor); if (costToGo < minCost) { minCost = costToGo; bestNextNode = neighbor; } } if (bestNextNode === null) { console.warn("Path reconstruction failed - stuck at", current); return []; // Path is blocked } path.push(bestNextNode); current = bestNextNode; // Safety break for path reconstruction loops if (path.length > this.gridWidth * this.gridHeight * 2) { console.error("Path reconstruction seems to be looping."); return []; } } return path; } // --- Handling Dynamic Changes --- // Update the cost of moving between two adjacent nodes updateEdgeCost(nodeA, nodeB, newCost) { const edgeKey = `${nodeA.x},${nodeA.y}_${nodeB.x},${nodeB.y}`; this.updatedEdgeCosts.set(edgeKey, newCost); // Update the vertex whose cost calculation might be affected // In D* Lite, we update the node *from* which the edge originates // when considering rhs(u) = min(c(u, v) + g(v)). // So, if cost c(A, B) changes, updateVertex(A) is needed. this.updateVertex(nodeA); // Recompute the shortest path if necessary // this.computeShortestPath(); // Call this after all updates for a batch } // Add or remove an obstacle dynamically updateObstacle(x, y, isObstacle) { const node = { x, y }; const nodeKey = this.getKey(node); const currentlyIsObstacle = this.obstacles.has(nodeKey); if (isObstacle && !currentlyIsObstacle) { // --- Adding an obstacle --- this.obstacles.add(nodeKey); // Make g and rhs infinite for the obstacle node itself const oldG = this.getG(node); const oldRhs = this.getRHS(node); this.setG(node, Infinity); this.setRHS(node, Infinity); // If the node was in the open list, remove it if (this.openList.includes(node)) { this.openList.remove(node); } // Update neighbors (now predecessors of the obstacle) // Their RHS might increase because the path through the obstacle is gone const neighbors = this.getNeighbors(node); for (const neighbor of neighbors) { if (!this.isObstacle(neighbor)) { // Don't update other obstacles // Check if the neighbor used the now-obstacle node in its RHS calculation const costToObstacle = this.cost(neighbor, node); // This will now be Infinity implicitly const gOfObstacle = oldG; // Use the G value *before* it became infinity // If the neighbor's RHS depended on the path through the new obstacle node if (this.getRHS(neighbor) === costToObstacle + gOfObstacle) { this.updateVertex(neighbor); // Recalculate its RHS } // Also update the neighbor if its G value depended on the obstacle // This check is implicitly handled by updateVertex if RHS changes } } // Update the obstacle node itself (its RHS is now Inf, G is Inf) // This ensures consistency checks work correctly if it becomes non-obstacle later this.updateVertex(node); } else if (!isObstacle && currentlyIsObstacle) { // --- Removing an obstacle --- this.obstacles.delete(nodeKey); // Reset the node's g/rhs potentially (updateVertex will recalculate rhs) // We need to trigger recalculation. updateVertex does this. this.updateVertex(node); // Recalculate RHS based on its non-obstacle neighbors // Update neighbors (now potential successors) // Their RHS might decrease if the path through the cleared node is better const neighbors = this.getNeighbors(node); for (const neighbor of neighbors) { if (!this.isObstacle(neighbor)) { this.updateVertex(neighbor); } } } // If state didn't change, do nothing } // Handle changes when the start position moves handleStartMoved(newStart) { this.lastStart = this.start; // Store the previous start this.start = newStart; // Update the key modifier based on the heuristic change this.km += this.heuristic(this.lastStart, this.goal); // Heuristic relative to the *goal* matters here for km // No need to re-initialize everything. // The existing g and rhs values are still valid estimates. // The change in heuristic is handled by recalculating keys on the fly // and by the km value. // We might need to trigger updates if the start node itself or its neighbors // were obstacles or had specific costs, but typically computeShortestPath // handles the propagation of changes. // It's crucial that calculateKey uses the *current* this.start // and the current this.km. // Optional: Force update neighbors of the new start? Usually not needed. // for (const neighbor of this.getNeighbors(this.start)) { // this.updateVertex(neighbor); // } // this.updateVertex(this.start); // Recompute the path with the new start position and km value // this.computeShortestPath(); // Call this after the move } // --- Utility Functions --- // Get valid neighbors of a node (within grid bounds) getNeighbors(node) { const neighbors = []; const directions = [ { dx: 1, dy: 0 }, { dx: -1, dy: 0 }, { dx: 0, dy: 1 }, { dx: 0, dy: -1 }, // Add diagonals if needed: // { dx: 1, dy: 1 }, { dx: 1, dy: -1 }, // { dx: -1, dy: 1 }, { dx: -1, dy: -1 } ]; for (const dir of directions) { const nextX = node.x + dir.dx; const nextY = node.y + dir.dy; // Check grid boundaries if (nextX >= 0 && nextX < this.gridWidth && nextY >= 0 && nextY < this.gridHeight) { // We don't check for obstacles here, the cost function handles that neighbors.push({ x: nextX, y: nextY }); } } return neighbors; } } // @WasmModule let PacketIds_1 = { default: { 0: "PACKET_ENTITY_UPDATE", 1: "PACKET_PLAYER_COUNTER_UPDATE", 2: "PACKET_SET_WORLD_DIMENSIONS", 3: "PACKET_INPUT", 4: "PACKET_ENTER_WORLD", 5: "PACKET_PRE_ENTER_WORLD", 6: "PACKET_ENTER_WORLD2", 7: "PACKET_PING", 9: "PACKET_RPC", 10: "PACKET_BLEND", PACKET_PRE_ENTER_WORLD: 5, PACKET_ENTER_WORLD: 4, PACKET_ENTER_WORLD2: 6, PACKET_ENTITY_UPDATE: 0, PACKET_INPUT: 3, PACKET_PING: 7, PACKET_PLAYER_COUNTER_UPDATE: 1, PACKET_RPC: 9, PACKET_SET_WORLD_DIMENSIONS: 2, PACKET_BLEND: 10, }, }, e_AttributeType = { 0: "Uninitialized", 1: "Uint32", 2: "Int32", 3: "Float", 4: "String", 5: "Vector2", 6: "EntityType", 7: "ArrayVector2", 8: "ArrayUint32", 9: "Uint16", 10: "Uint8", 11: "Int16", 12: "Int8", 13: "Uint64", 14: "Int64", 15: "Double", Uninitialized: 0, Uint32: 1, Int32: 2, Float: 3, String: 4, Vector2: 5, EntityType: 6, ArrayVector2: 7, ArrayUint32: 8, Uint16: 9, Uint8: 10, Int16: 11, Int8: 12, Uint64: 13, Int64: 14, Double: 15, }, e_ParameterType = { 0: "Uint32", 1: "Int32", 2: "Float", 3: "String", 4: "Uint64", 5: "Int64", Uint32: 0, Int32: 1, Float: 2, String: 3, Uint64: 4, Int64: 5 }; class BinCodec { constructor() { (this.attributeMaps = {}), (this.entityTypeNames = {}), (this.rpcMaps = []), (this.rpcMapsByName = {}), (this.sortedUidsByType = {}), (this.removedEntities = {}), (this.absentEntitiesFlags = []), (this.updatedEntityFlags = []); } encode(e, t, a) { let r = new dcodeIO.ByteBuffer(100, !0); switch (e) { case PacketIds_1.default.PACKET_ENTER_WORLD: r.writeUint8(PacketIds_1.default.PACKET_ENTER_WORLD), this.encodeEnterWorld(r, t); break; case PacketIds_1.default.PACKET_ENTER_WORLD2: r.writeUint8(PacketIds_1.default.PACKET_ENTER_WORLD2), this.encodeEnterWorld2(r, a); break; case PacketIds_1.default.PACKET_INPUT: r.writeUint8(PacketIds_1.default.PACKET_INPUT), this.encodeInput(r, t); break; case PacketIds_1.default.PACKET_PING: r.writeUint8(PacketIds_1.default.PACKET_PING), this.encodePing(r, t); break; case PacketIds_1.default.PACKET_RPC: r.writeUint8(PacketIds_1.default.PACKET_RPC), this.encodeRpc(r, t); break; case PacketIds_1.default.PACKET_BLEND: r.writeUint8(PacketIds_1.default.PACKET_BLEND), this.encodeBlend(r, t); } return r.flip(), r.compact(), r.toArrayBuffer(!1); } decode(e, t) { let a = dcodeIO.ByteBuffer.wrap(e); a.littleEndian = !0; let r = a.readUint8(), n; switch (r) { case PacketIds_1.default.PACKET_PRE_ENTER_WORLD: n = this.decodePreEnterWorldResponse(a, t); break; case PacketIds_1.default.PACKET_ENTER_WORLD: n = this.decodeEnterWorldResponse(a); break; case PacketIds_1.default.PACKET_ENTITY_UPDATE: n = this.decodeEntityUpdate(a); break; case PacketIds_1.default.PACKET_PING: n = this.decodePing(a); break; case PacketIds_1.default.PACKET_RPC: n = this.decodeRpc(a); break; case PacketIds_1.default.PACKET_BLEND: n = this.decodeBlend(a, t); } return (n.opcode = r), n; } safeReadVString(e) { let t = e.offset, a = e.readVarint32(t); try { var r = e.readUTF8String.bind(e)(a.value, "b", (t += a.length)); return (t += r.length), (e.offset = t), r.string; } catch (n) { return (t += a.value), (e.offset = t), "?"; } } decodePreEnterWorldResponse(e, t) { return t._MakeBlendField(255, 140), { extra: this.decodeBlendInternal(e, t) }; } decodeEnterWorldResponse(e) { let t = { allowed: e.readUint32(), uid: e.readUint32(), startingTick: e.readUint32(), tickRate: e.readUint32(), effectiveTickRate: e.readUint32(), players: e.readUint32(), maxPlayers: e.readUint32(), chatChannel: e.readUint32(), effectiveDisplayName: this.safeReadVString(e), x1: e.readInt32(), y1: e.readInt32(), x2: e.readInt32(), y2: e.readInt32(), }, a = e.readUint32(); (this.attributeMaps = {}), (this.entityTypeNames = {}); for (let r = 0; r < a; r++) { let n = [], i = e.readUint32(), s = e.readVString(), d = e.readUint32(); for (let o = 0; o < d; o++) { let c = e.readVString(), l = e.readUint32(); n.push({ name: c, type: l }); } (this.attributeMaps[i] = n), (this.entityTypeNames[i] = s), (this.sortedUidsByType[i] = []); } let p = e.readUint32(); (this.rpcMaps = []), (this.rpcMapsByName = {}); for (let h = 0; h < p; h++) { let f = e.readVString(), U = e.readUint8(), u = 0 != e.readUint8(), y = []; for (let E = 0; E < U; E++) { let $ = e.readVString(), m = e.readUint8(); y.push({ name: $, type: m }); } let b = { name: f, parameters: y, isArray: u, index: this.rpcMaps.length }; this.rpcMaps.push(b), (this.rpcMapsByName[f] = b); } return t; } decodeEntityUpdate(e) { var t = e.readUint32(), a = e.readVarint32(), r = {}; for (var n in ((r.tick = t), (r.entities = {}), this.removedEntities)) delete this.removedEntities[n]; for (var i = 0; i < a; i++) { var n = e.readUint32(); this.removedEntities[n] = 1; } for (var s = e.readVarint32(), i = 0; i < s; i++) { var d = e.readVarint32(), o = e.readUint32(); this.entityTypeNames[o]; for (var c = 0; c < d; c++) { var l = e.readUint32(); this.sortedUidsByType[o].push(l); } } for (var i in this.sortedUidsByType) { for (var p = this.sortedUidsByType[i], h = [], c = 0; c < p.length; c++) { var n = p[c]; n in this.removedEntities || h.push(n); } h.sort(function (e, t) { return e < t ? -1 : e > t ? 1 : 0; }), (this.sortedUidsByType[i] = h); } for (; e.remaining(); ) { var f = e.readUint32(); if ((this.entityTypeNames[f], !(f in this.attributeMaps))) throw Error("Entity type is not in attribute map: " + f); var U = Math.floor((this.sortedUidsByType[f].length + 7) / 8); this.absentEntitiesFlags.length = 0; for (var i = 0; i < U; i++) this.absentEntitiesFlags.push(e.readUint8()); for (var u = this.attributeMaps[f], y = 0; y < this.sortedUidsByType[f].length; y++) { var n = this.sortedUidsByType[f][y]; if ((this.absentEntitiesFlags[Math.floor(y / 8)] & (1 << y % 8)) != 0) { r.entities[n] = !0; continue; } var E = { uid: n }; this.updatedEntityFlags.length = 0; for (var c = 0; c < Math.ceil(u.length / 8); c++) this.updatedEntityFlags.push(e.readUint8()); for (var c = 0; c < u.length; c++) { var $ = u[c], m = Math.floor(c / 8), b = c % 8, v = void 0, g = []; if (this.updatedEntityFlags[m] & (1 << b)) switch ($.type) { case e_AttributeType.Uint32: E[$.name] = e.readUint32(); break; case e_AttributeType.Int32: E[$.name] = e.readInt32(); break; case e_AttributeType.Float: E[$.name] = e.readInt32() / 100; break; case e_AttributeType.String: E[$.name] = this.safeReadVString(e); break; case e_AttributeType.Vector2: var T = e.readInt32() / 100, k = e.readInt32() / 100; E[$.name] = { x: T, y: k }; break; case e_AttributeType.ArrayVector2: (v = e.readInt32()), (g = []); for (var i = 0; i < v; i++) { var P = e.readInt32() / 100, I = e.readInt32() / 100; g.push({ x: P, y: I }); } E[$.name] = g; break; case e_AttributeType.ArrayUint32: (v = e.readInt32()), (g = []); for (var i = 0; i < v; i++) { var B = e.readInt32(); g.push(B); } E[$.name] = g; break; case e_AttributeType.Uint16: E[$.name] = e.readUint16(); break; case e_AttributeType.Uint8: E[$.name] = e.readUint8(); break; case e_AttributeType.Int16: E[$.name] = e.readInt16(); break; case e_AttributeType.Int8: E[$.name] = e.readInt8(); break; case e_AttributeType.Uint64: E[$.name] = e.readUint32() + 4294967296 * e.readUint32(); break; case e_AttributeType.Int64: var R = e.readUint32(), w = e.readInt32(); w < 0 && (R *= -1), (R += 4294967296 * w), (E[$.name] = R); break; case e_AttributeType.Double: var _ = e.readUint32(), A = e.readInt32(); A < 0 && (_ *= -1), (_ += 4294967296 * A), (_ /= 100), (E[$.name] = _); break; default: throw Error("Unsupported attribute type: " + $.type); } } r.entities[E.uid] = E; } } return (r.byteSize = e.capacity()), r; } decodePing() { return {}; } encodeRpc(e, t) { if (!(t.name in this.rpcMapsByName)) throw Error("RPC not in map: " + t.name); var a = this.rpcMapsByName[t.name]; e.writeUint32(a.index); for (var r = 0; r < a.parameters.length; r++) { var n = t[a.parameters[r].name]; switch (a.parameters[r].type) { case e_ParameterType.Float: e.writeInt32(Math.floor(100 * n)); break; case e_ParameterType.Int32: e.writeInt32(n); break; case e_ParameterType.String: e.writeVString(n); break; case e_ParameterType.Uint32: e.writeUint32(n); } } } decodeBlend(e, t) { return { extra: this.decodeBlendInternal(e, t) }; } decodeBlendInternal(e, t) { t._MakeBlendField(24, 132); for (let a = t._MakeBlendField(228, e.remaining()), r = 0; e.remaining(); ) (t.HEAPU8[a + r] = e.readUint8()), r++; t._MakeBlendField(172, 36); for (var n = new ArrayBuffer(64), i = new Uint8Array(n), s = t._MakeBlendField(4, 152), d = 0; d < 64; d++) i[d] = t.HEAPU8[s + d]; return n; } decodeRpcObject(e, t) { for (var a = {}, r = 0; r < t.length; r++) switch (t[r].type) { case e_ParameterType.Uint32: a[t[r].name] = e.readUint32(); break; case e_ParameterType.Int32: a[t[r].name] = e.readInt32(); break; case e_ParameterType.Float: a[t[r].name] = e.readInt32() / 100; break; case e_ParameterType.String: a[t[r].name] = this.safeReadVString(e); break; case e_ParameterType.Uint64: a[t[r].name] = e.readUint32() + 4294967296 * e.readUint32(); } return a; } decodeRpc(e) { var t = e.readUint32(), a = this.rpcMaps[t], r = { name: a.name, response: null }; if (a.isArray) { for (var n = [], i = e.readUint16(), s = 0; s < i; s++) n.push(this.decodeRpcObject(e, a.parameters)); r.response = n; } else r.response = this.decodeRpcObject(e, a.parameters); return r; } encodeBlend(e, t) { for (var a = new Uint8Array(t.extra), r = 0; r < t.extra.byteLength; r++) e.writeUint8(a[r]); } encodeEnterWorld(e, t) { e.writeVString(t.displayName); for (var a = new Uint8Array(t.extra), r = 0; r < t.extra.byteLength; r++) e.writeUint8(a[r]); } encodeEnterWorld2(e, t) { for (var a = t._MakeBlendField(187, 22), r = 0; r < 16; r++) e.writeUint8(t.HEAPU8[a + r]); } encodeInput(e, t) { e.writeVString(JSON.stringify(t)); } encodePing(e) { e.writeUint8(0); } } let codec = new BinCodec(), wasmBuffers; fetch("/asset/zombs_wasm.wasm").then((e) => e.arrayBuffer().then((e) => { wasmBuffers = e; }) ); const wasmModule = (callback, data_12, hostname) => { function _a(e, t, r) { for (var a = t + r, n = t; e[n] && !(n >= a); ) ++n; if (n - t > 16 && e.subarray && _n) return _n.decode(e.subarray(t, n)); for (var i = ""; t < n; ) { let d = e[t++]; if (128 & d) { var s = 63 & e[t++]; if (192 != (224 & d)) { var l = 63 & e[t++]; if ((d = 224 == (240 & d) ? ((15 & d) << 12) | (s << 6) | l : ((7 & d) << 18) | (s << 12) | (l << 6) | (63 & e[t++])) < 65536) i += String.fromCharCode(d); else { var c = d - 65536; i += String.fromCharCode(55296 | (c >> 10), 56320 | (1023 & c)); } } else i += String.fromCharCode(((31 & d) << 6) | s); } else i += String.fromCharCode(d); } return i; } function _b(e, t) { return e ? _a(_k, e, t) : ""; } function _c(e, t, r, a) { if (!(a > 0)) return 0; for (var n = r, i = r + a - 1, d = 0; d < e.length; ++d) { var s = e.charCodeAt(d); if ((s >= 55296 && s <= 57343 && (s = (65536 + ((1023 & s) << 10)) | (1023 & e.charCodeAt(++d))), s <= 127)) { if (r >= i) break; t[r++] = s; } else if (s <= 2047) { if (r + 1 >= i) break; (t[r++] = 192 | (s >> 6)), (t[r++] = 128 | (63 & s)); } else if (s <= 65535) { if (r + 2 >= i) break; (t[r++] = 224 | (s >> 12)), (t[r++] = 128 | ((s >> 6) & 63)), (t[r++] = 128 | (63 & s)); } else { if (r + 3 >= i) break; (t[r++] = 240 | (s >> 18)), (t[r++] = 128 | ((s >> 12) & 63)), (t[r++] = 128 | ((s >> 6) & 63)), (t[r++] = 128 | (63 & s)); } } return (t[r] = 0), r - n; } function _d(e, t, r) { return _c(e, _k, t, r); } function _e(e) { for (var t = 0, r = 0; r < e.length; ++r) { var a = e.charCodeAt(r); a >= 55296 && a <= 57343 && (a = (65536 + ((1023 & a) << 10)) | (1023 & e.charCodeAt(++r))), a <= 127 ? ++t : (t += a <= 2047 ? 2 : a <= 65535 ? 3 : 4); } return t; } function _f(e) { (_m.HEAP8 = new Int8Array(e)), (_m.HEAP16 = new Int16Array(e)), (_m.HEAP32 = _l = new Int32Array(e)), (_m.HEAPU8 = _k = new Uint8Array(e)), (_m.HEAPU16 = new Uint16Array(e)), (_m.HEAPU32 = new Uint32Array(e)), (_m.HEAPF32 = new Float32Array(e)), (_m.HEAPF64 = new Float64Array(e)); } function _g() { function e(e) { (_m.asm = e.exports), _f(_m.asm.g.buffer), _o(), _j(); } function t(t) { e(t.instance); } function r(e) { WebAssembly.instantiate(wasmBuffers, a).then((t) => { e(t), "function" == typeof callback && callback(_m.decodeOpcode5(hostname, data_12)); }); } var a = { a: { d() {}, e() {}, c() {}, f() {}, b: _h, a: _i } }; if (_m.instantiateWasm) try { return _m.instantiateWasm(a, e); } catch (n) { return console.log("Module.instantiateWasm callback failed with error: " + n), !1; } return r(t), {}; } function _h(_hh) { let e = _b(_hh); if (e.includes('typeof window === "undefined" ? 1 : 0;') || e.includes("typeof process !== 'undefined' ? 1 : 0;")) return 0; if (e.includes("Game.currentGame.network.connected ? 1 : 0")) return 1; if (e.includes("Game.currentGame.world.myUid === null ? 0 : Game.currentGame.world.myUid;")) return 0; if (e.includes('document.getElementById("hud").children.length;')) return 24; if (e.includes("hostname")) return hostname; let data; return 0 | eval(_b(_hh)); } function _i(e) { var t = hostname; if (null == t) return 0; t = String(t); var r = _i, a = _e(t); return (!r.bufferSize || r.bufferSize < a + 1) && (r.bufferSize && _s(r.buffer), (r.bufferSize = a + 1), (r.buffer = _r(r.bufferSize))), _d(t, r.buffer, r.bufferSize), r.buffer; } function _j() { (_l[1328256] = 5313008), (_l[1328257] = 0), _m._main(1, 5313024); } var _k, _l, _m = {}, _n = new TextDecoder("utf8"); _g(); var _o = (_m.___wasm_call_ctors = function () { return (_o = _m.___wasm_call_ctors = _m.asm.h).apply(null, arguments); }), _p = (_m._main = function () { return (_p = _m._main = _m.asm.i).apply(null, arguments); }), _q = (_m._MakeBlendField = function () { return (_q = _m._MakeBlendField = _m.asm.j).apply(null, arguments); }), _r = (_m._malloc = function () { return (_r = _m._malloc = _m.asm.l).apply(null, arguments); }), _s = (_m._free = function () { return (_s = _m._free = _m.asm.m).apply(null, arguments); }); return ( (_m.decodeOpcode5 = function (e, t) { _m.hostname = e; let r = codec.decode(new Uint8Array(t), _m), a = codec.encode(6, {}, _m); return { 5: r, 6: a, 10: _m }; }), _m ); }; // @FontAwesome const fontAwesome = document.createElement("script"); fontAwesome.type = "text/javascript"; fontAwesome.src = "https://kit.fontawesome.com/1c239b2e80.js"; document.head.appendChild(fontAwesome); document.querySelectorAll('#hud-respawn > div > div > div > h2, #hud-respawn > div > div > div > p, #hud-respawn > div > div > div > div, .ad-unit, #hud-intro > div.hud-intro-footer > a:nth-child(2), #hud-intro > div.hud-intro-footer > a:nth-child(4), #hud-intro > div.hud-intro-wrapper > h1, #hud-intro > div.hud-intro-wrapper > h2, .hud-intro-left, .hud-intro-guide, .hud-intro > .hud-intro-stone, .hud-intro >.hud-intro-tree, .hud-intro-youtuber, .hud-intro-more-games, .hud-intro-social, .hud-respawn-corner-bottom-left, .hud-respawn-twitter-btn, .hud-respawn-facebook-btn').forEach(el => el.remove()); const css = ` /* @Root */ :root { --normal-btn: rgb(40 152 231); --light-hover-btn: rgb(111 208 247); } ::-webkit-scrollbar { width: 12px; height: 0px; border-radius: 10px; background-color: rgba(0, 0, 0, 0); } ::-webkit-scrollbar-thumb { border-radius: 10px; background-image: url(https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/whiteslider.png?v=1714878503407); } input[type='range'] { accent-color: var(--normal-btn); } .btn-theme { background: var(--normal-btn); } .btn-theme:hover { background: var(--light-hover-btn); } /* @CustomKeyframes */ @keyframes bounce { 50% { transform: translateY(-30px); } 100% { transform: translateY(-10px); } } .hud-intro::before { background-image: var(--bg-image); background-size: cover; background-position: center; transition: all 0.5s ease-out; } .hud-intro::after { background: unset; } #hud-intro > div.hud-intro-corner-top-left > div { padding: 10px; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 10px 10px rgba(0, 0, 0, 0.2); display: flex; line-height: 20px; margin: 0 0 10px; font-size: 14px; color: rgba(255, 255, 255, 0.4); border-radius: 4px; flex-direction: column; } #hud-intro > div.hud-intro-corner-top-right > div { padding: 10px; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 10px 10px rgba(0, 0, 0, 0.2); } #hud-intro > div.hud-intro-wrapper { justify-content: flex-end; margin: 40px 0 0; } #hud-intro > div.hud-intro-wrapper > div > div { padding: 10px; background: rgba(0, 0, 0, 0.1); } #hud-intro > div.hud-intro-wrapper > div > div > input { color: white; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.2); } #hud-intro > div.hud-intro-wrapper > div > div > select { color: white; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.2); border: 0px; margin: 0 0 10px; } #hud-intro > div.hud-intro-footer { left: unset; right: 20px; } /* @CustomIntroStyles */ #intro-animation { display: block; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 0; } #background-canvas { display: block; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 1; filter: blur(2px); } #intro-animation > img { position: absolute; z-index: 2; height: calc(1400px + 5vw); max-height: 170vh; left: calc(15vw - 300px); bottom: calc(20vh - 600px - 2.5vw); top: unset; z-index: 2; transform: translateY(-10px); transition: all 0.5s ease-out; animation-name: bounce; animation-duration: 5s; animation-iteration-count: infinite; } #foreground-canvas { display: block; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 3; } #start-btn { display: flex; flex-direction: column; align-items: flex-end; justify-content: center; margin: auto; position: absolute; top: calc(50vh - 75px); right: 0px; height: 150px; zoom: 140%; cursor: pointer; background: rgba(0, 0, 0, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); border-top-left-radius: 15px; border-bottom-left-radius: 15px; } #start-btn > h1 { color: white; margin-right: 20px; font-size: 60px; text-transform: none; } #start-btn > hr { border: 2px solid white; width: 225px; } #start-btn > h2 { margin-right: 20px; font-size: 20px; color: rgba(255, 255, 255, 0.8); } #sesSelect { display: none; float: left; width: 100%; height: 50px; line-height: 34px; padding: 8px 14px; margin: 0 0 10px; border: 0; font-size: 14px; border-radius: 4px; color: white; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.2); } #sesSelect:disabled { opacity: 0.4; } #delBtn, #addBtn { display: none; width: calc(50% - 5px); height: 50px; line-height: 34px; padding: 8px 14px; font-size: 14px; border-left: 0; border-right: 0; border-top: 0; border-radius: 4px; color: #eee; cursor: pointer; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.2); } #delBtn { margin: 0 5px 10px 0; border-bottom: 2px solid red; } #addBtn { margin: 0 0 10px 5px; border-bottom: 2px solid white; } #hud-intro-overlay { display: none; opacity: 0; position: absolute; top: 0px; left: 0px; z-index: 999; backdrop-filter: blur(10px); width: 100%; height: 100%; background: rgba(0, 0, 0, 0.3); justify-content: center; align-items: center; flex-direction: column; transition: all 0.5s; } #hud-intro-overlay h2 { color: white; font-size: 30px; text-shadow: 0 0 20px black; margin-bottom: 15px; } #hud-intro-overlay strong { margin: -15px 0 5px; color: #aaa; } #hud-intro-overlay p { color: #eee; margin: 10px 0; font-weight: bold; } #hud-intro-overlay input, #hud-intro-overlay select, #hud-intro-overlay button:not(.session-back):not(#create-session):not(#create-endpoint) { width: 100%; height: 50px; line-height: 34px; padding: 8px 14px; margin: 0 0 15px; border: 0; text-align: start; font-size: 15px; border-radius: 10px; color: white; backdrop-filter: blur(10px); background: rgba(0, 0, 0, 0.1); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.2); } #hud-intro-overlay button { cursor: pointer; } #loading-div { display: none; pointer-events: none; justify-content: center; align-items: center; flex-direction: column; } #loading-div > span { width: 100px; height: 100px; } #loading-div > a { margin-top: 20px; color: rgba(255, 255, 255, 0.4); pointer-events: all; } #select-session-add, #endpoint-add-menu, #session-add-menu { width: 40vw; display: none; align-items: flex-start; flex-direction: column; padding: 20px; border-radius: 10px; background: rgba(0, 0, 0, 0.2); } .session-navigator { display: flex; justify-content: space-between; width: 100%; margin-top: 10px; } .session-navigator button { height: 20px; background: none; border: 0; color: #eee; font-weight: bold; zoom: 130%; } /* @CustomUIStyles */ .dragBox { position: absolute; background-color: rgba(233, 233, 233, 0.3); border: 5px solid rgba(255, 255, 255, 0.3); border-radius: 3px; } .dragBoxMenu { background-color: whitesmoke; border-radius: 5px; color: #111; transform: translate(-50%, -50%); position: absolute; padding: 3px; width: auto; z-index: 999; } .dragBoxMenu > button, .dragBoxMenu > input { width: 100%; background-color: whitesmoke; border: none; border-radius: 2px; cursor: default; transition: none; } .dragBoxMenu > button:hover, .dragBoxMenu > input:focus { background-color: grey; color: whitesmoke; } .dragBoxMenu > input:focus::placeholder { color: whitesmoke; } #joinWithPsk { display: none; backdrop-filter: blur(5px); background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); padding: 4px 5px; border-radius: 8px; color: white; width: 30vw; height: 40px; position: fixed; left: 35vw; top: 60vh; } .interaction-wheel { display: none; height: 250px; width: 250px; border: 80px solid rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); border-radius: 125px; position: fixed; top: calc(50vh - 125px); left: calc(50vw - 125px); } .tag-is-disabled { opacity: 0.4; pointer-events: none; cursor: no-drop; } #next-wheel { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); color: white; right: -120px; position: absolute; top: -80px; height: 60px; width: 60px; border-radius: 50%; border: none; cursor: pointer; } #next-wheel::after { content: '\\f363'; position: absolute; width: 100%; height: 100%; top: 5%; left: 5%; } #zoom-mode { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); color: white; left: -120px; position: absolute; bottom: -80px; height: 60px; width: 60px; border-radius: 50%; border: none; text-align: center; padding: 20px 0; font-family: 'Hammersmith One'; } .hud-zoom-item { width: 48px; height: 48px; color: white; position: absolute; transition: all 0.15s ease-in-out; } .zoom-reset { top: -60px; left: calc(50% - 15px); } .zoom-up { left: -60px; top: calc(50% - 15px); } .zoom-down { top: calc(50% - 15px); right: -80px; } .zoom-prop { font-size: 20px; bottom: -80px; left: 25%; } #ent-settings { flex-direction: column; padding: 20px; position: absolute; color: white; background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); border-radius: 10px; max-width: 180px; } #current-side { background: none; border: none; margin-top: 5px; color: white; width: 100%; } #ent-width { margin: 5px -1px 10px; width: calc(100% - 45px); } #ent-width::after { content: attr(data-width); position: absolute; right: 20px; color: white; } #ent-controls { display: flex; justify-content: space-between; margin: 5px -5px 0; zoom: 130%; } #ent-controls button { color: white; background: none; border: none; cursor: pointer; } #hud-debug { color: rgb(0, 255, 255); } #hud-debug > strong { font-family: "HammerSmith One"; color: red; font-weight: 700; } .hud-popup-overlay > div.hud-popup-message { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); } .hud-popup-overlay .hud-popup-confirmation .hud-confirmation-actions .btn.btn-green { background: var(--normal-btn); } .hud-tooltip { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); border-radius: 10px; } .hud-tooltip-left::after { left: calc(100% + 5px); border-left: 6px solid rgba(255, 255, 255, 0.2); } .hud-tooltip-right::after { right: calc(100% + 5px); border-right: 6px solid rgba(255, 255, 255, 0.2); } .hud-tooltip-top::after { top: calc(100% + 5px); border-top: 6px solid rgba(255, 255, 255, 0.2); } .hud-tooltip-bottom::after { bottom: calc(100% + 5px); border-bottom: 6px solid rgba(255, 255, 255, 0.2); } .hud-building-view, .hud-building-ahrc { position: relative; display: block; padding: 0 10px; margin: 0 0 6px; font-family: 'Hammersmith One', sans-serif; text-shadow: 0 1px 3px rgb(0 0 0 / 20%); text-align: left; } .hud-chat { resize: vertical; max-height: 380px; min-height: 75px; overflow-y: auto; border-radius: 4px 4px 4px 0; } .hud-chat .hud-chat-message { display: block; position: relative; width: 90%; white-space: unset; word-break: break-all; overflow: visible; } .hud-chat .hud-chat-message strong { display: inline-block; } .hud-chat .hud-chat-message > small { position: absolute; right: -12%; font-weight: bold; opacity: 0.4; } #hud-spell-icons { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); border-top-right-radius: 10px; border-bottom-right-radius: 10px; } #hud-spell-icons > div { background: none; } #hud-menu-icons { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(3px); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); border-top-left-radius: 10px; border-bottom-left-radius: 10px; } #hud-menu-icons > div { background: none; } #hud > div.hud-bottom-left { z-index: 20; backdrop-filter: blur(3px); background: rgba(255, 255, 255, 0.15); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); padding: 10px; border-radius: 4px; } #hud-map { cursor: crosshair; pointer-events: all; background: rgba(0, 0, 0, 0.4); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); } #hud-day-night-ticker { background: rgba(0, 0, 0, 0.4); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); margin-top: 0px; } #hud-resources { background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); backdrop-filter: blur(3px); } #hud-health-bar { height: 3px; padding: 0px; } #hud-health-bar::after { content: unset; } #hud-health-bar > div { height: 3px; } #hud-shield-bar { height: 3px; padding: 0px; margin: 0px 0px 5px !important; } #hud-shield-bar > div { height: 3px; border-radius: 0px; } #hud-party-icons > div { background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); backdrop-filter: blur(3px); } #hud-party-icons > div.is-empty { background: rgba(255, 255, 255, 0.1) !important; opacity: 0.4; } .hud-buff-bar .hud-buff-bar-item::before { content: attr(data-tier); } #hud-buff-bar > a { background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.1); backdrop-filter: blur(2px); } #hud-toolbar > div.hud-toolbar-buildings > a { background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); backdrop-filter: blur(3px); } #hud-toolbar > div.hud-toolbar-buildings > a.is-disabled { background: rgba(255, 255, 255, 0.1) !important; } #hud-respawn { background: none; z-index: 9999; width: 600px; height: 200px; top: calc(50vh - 100px); left: calc(50vw - 300px); } #hud-respawn > div { margin: 0px; } #hud-respawn > div > div > div { display: flex; width: 500px; backdrop-filter: blur(3px); background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 30px 10px rgba(0, 0, 0, 0.1); border-radius: 10px; align-items: flex-end; justify-content: space-between; padding: 10px 20px 30px; } /* @CustomMenuStyles */ #hud-menu-shop { top: calc(50vh - 215px); left: calc(50vw - 345px); width: 690px; height: 430px; backdrop-filter: blur(3px); background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 30px 10px rgba(0, 0, 0, 0.1); margin: 0 0 0 0; padding: 0px; z-index: 20; overflow: hidden; border-radius: 10px; } #hud-menu-shop > h3 { margin: 20px; } .hud-menu-shop .hud-shop-grid { height: 360px; border-radius: 0px; background: rgba(0, 0, 0, 0.1); } .hud-menu-shop .hud-shop-grid .hud-shop-item .hud-shop-item-actions .hud-shop-actions-equip { background: var(--normal-btn); } .hud-menu-shop .hud-shop-grid .hud-shop-item .hud-shop-item-actions .hud-shop-actions-equip:hover, .hud-menu-shop .hud-shop-grid .hud-shop-item .hud-shop-item-actions .hud-shop-actions-equip:active { background: var(--light-hover-btn); } .hud-menu-shop .hud-shop-grid .hud-shop-item .hud-shop-item-actions .hud-shop-actions-equip.is-disabled { background: none; } .hud-menu-shop .hud-shop-grid .hud-shop-item[data-item=HatComingSoon] .hud-shop-item-coming-soon { background: none; } #hud-menu-shop > div.hud-shop-tabs > a.hud-shop-tabs-link.is-active { background: rgba(0, 0, 0, 0.1); } #hud-menu-shop > div.hud-shop-tabs > a.hud-shop-tabs-link:first-child { border-radius: 0px; } .hud-menu-party .hud-party-members .hud-member-link::before { display: block; position: absolute; content: " "; left: 0px; bottom: 0px; height: 3px; width: 30%; } .hud-menu-party .hud-party-grid .hud-party-link span:nth-child(4) { display: none; position: absolute; top: 10px; right: 15px; } #hud-menu-party > div.hud-party-members > div:nth-child(1)::before { background: #8473d4; } #hud-menu-party > div.hud-party-members > div:nth-child(2)::before { background: #d6ab35; } #hud-menu-party > div.hud-party-members > div:nth-child(3)::before { background: #76bd2f; } #hud-menu-party > div.hud-party-members > div:nth-child(4)::before { background: #d67820; } #hud-menu-party { top: calc(50vh - 240px); left: calc(50vw - 305px); margin: 0; width: 610px; height: 480px; backdrop-filter: blur(3px); background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 30px 10px rgba(0, 0, 0, 0.1); z-index: 20; border-radius: 10px; /* Alternative backdrop-filter: blur(5px); background: rgba(111, 208, 247, 0.05); box-shadow: 0px 0px 10px 10px rgba(0, 0, 0, 0.1); z-index: 20; */ } .hud-menu-party .hud-party-grid .hud-party-link.is-active { background: var(--normal-btn) !important; } .hud-menu-party .hud-party-visibility { width: 275.5px; margin: 10px 3px 0 0; background: var(--normal-btn); } .hud-menu-party .hud-party-share { width: 395px; margin: 0 0 0 5px; } .hud-menu-party .hud-party-visibility:hover, .hud-menu-party .hud-party-visibility:active { background: var(--light-hover-btn); } /* @CustomModMenuStyles */ #hud-menu-settings { backdrop-filter: blur(3px); background: rgba(255, 255, 255, 0.1); box-shadow: 0px 0px 30px 10px rgba(0, 0, 0, 0.1); margin: 0px; top: calc(50vh - 250px); left: calc(50vw - 360px); width: 720px; height: 500px; padding: 0px; border-radius: 10px; overflow: hidden; z-index: 20; } .hud-menu-tabs { position: relative; height: 40px; line-height: 40px; margin-top: 30px; } .hud-menu-tabs-link { display: block; float: left; padding: 0 14px; margin: 0 1px 0 0; font-size: 14px; background: rgba(0, 0, 0, 0.1); color: rgba(255, 255, 255, 0.4); transition: all 0.15s ease-in-out; } .hud-menu-tabs-link.is-active, .hud-menu-tabs-link:hover { background: rgba(0, 0, 0, 0.36); color: #eee; } .hud-menu-tabs-link:last-child { border-top-right-radius: 3px; } .hint-controls { position: absolute; display: flex; flex-direction: column; zoom: 83%; width: fit-content; height: 65px; top: 55px; left: 53%; overflow-y: scroll; } .hint-controls > li { opacity: 0.5; -webkit-font-smoothing: antialiased; margin: 0 5px 0 0; } #hud-menu-settings > h3 { margin: 20px; } .hud-menu-settings .hud-settings-grid { height: 390px; margin: 0px; overflow: hidden; padding: unset; border-top-left-radius: 0px; border-top-right-radius: 0px; } .hud-settings-page { width: 100%; height: 100%; } .hud-settings-page > span { position: relative; margin: auto; font-size: 17px; opacity: 0.7; } .hud-settings-options { display: inline-block; position: relative; width: 50%; height: 100%; background: rgba(0, 0, 0, 0.2); overflow: auto; scrollbar-width: none; } .hud-settings-options > div { opacity: 0.8; display: block; position: relative; height: 120px; border-bottom: 3px solid rgba(255, 255, 255, 0.2); padding: 15px; } .hud-settings-options > div.disabled { pointer-events: none; opacity: 0.4; } .hud-settings-options > div:nth-child(even) { background: rgba(0, 0, 0, 0.15); } .hud-settings-options > div:last-child { border-bottom: none; } .hud-settings-options h2 { font-family: "Open Sans"; margin: 5px 0; font-size: 1.4em; } .hud-settings-options span { display: block; position: absolute; opacity: 0.7; width: 55%; -webkit-font-smoothing: antialiased; font-size: 14px; } .hud-settings-options button { position: absolute; top: calc(50% - 20px); right: 50px; height: 40px; width: 25%; background: #fff; border: none; border-bottom: 3px solid lightgreen; color: #111; font-size: 16px; cursor: pointer; transition: all 0.15s ease-in-out; border-radius: 5px; box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.05); } .hud-settings-options .underline-red { border-bottom: 3px solid red; } .hud-settings-options a { position: absolute; top: calc(50% - 10px); right: 10px; height: 20px; width: 20px; background: unset; opacity: 0.4; transition: all 0.15s ease-in-out; } .hud-settings-options a::before { font-family: "Font Awesome 5 Free"; content: "\\f054"; font-size: 20px; height: 100%; width: 100%; display: block; font-weight: 900; } .hud-settings-options a:hover { opacity: 1; } .hud-settings-more { position: relative; display: inline-block; width: 50%; height: 100%; padding: 15px; overflow: auto; } .hud-settings-more h2 { margin: 5px 0px 10px; } .hud-settings-more select { height: 40px; width: 35%; background: #fff; border: none; color: #111; cursor: pointer; transition: all 0.15s ease-in-out; border-radius: 5px; box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.1); float: right; padding: 0px 10px; } .hud-settings-more select:hover { background: #fff; color: black; } .hud-settings-more input:not([type="checkbox"]) { height: 40px; width: 100%; background: rgba(0, 0, 0, 0.2); border: none; border-radius: 4px; padding: 5px 10px; color: #eee; margin: 10px 0; box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); } .hud-settings-more span { display: block; opacity: 0.5; } .hud-menu-settings > .hud-settings-grid label > span { display: inline-block; color: white; text-transform: unset; font-size: 16px; } .hud-settings-more p { display: inline-block; height: 40px; align-content: center; margin: unset; opacity: 0.8; font-size: 15px; } .hud-settings-more span { display: block; opacity: 0.5; } .hud-settings-more hr { width: 8%; float: left; margin-top: -5px; opacity: 0.4; } /* @CustomMoreSettings */ .base-card { position: relative; display: block; width: 100%; height: 64px; margin: 0 0 10px; padding: 10px 10px 10px 10px; text-decoration: none; background: rgba(255, 255, 255, 0.1); cursor: pointer; color: #eee; border-radius: 3px; transition: all 0.15s ease-in-out; text-align: left; } .base-card:hover { background: rgba(255, 255, 255, 0.2); } #base-management { position: absolute; top: 0px; width: 100%; height: 75%; margin: 0px -15px; padding: 0 15px; overflow-y: scroll; overflow-x: visible; scrollbar-width: none; } #base-management input { margin: 0px; } #target-base-name { background: none; box-shadow: none; text-align: left; font-weight: bold; padding: 0px; font-size: 16px; } #target-base-description { background: none; box-shadow: none; text-align: left; padding: 0px; opacity: 0.8; height: 20px; margin-bottom: 10px; } #target-base-design { background: none; box-shadow: none; text-align: left; padding: 0px; opacity: 0.4; height: 20px; margin-bottom: 20px; font-size: 8px; } #action-tab { display: flex; flex-direction: row; justify-content: space-around; height: 40px; margin: 0 -15px -10px; overflow-x: visible; background: rgba(0, 0, 0, 0.1); } #action-tab > button { width: 40px; background: none; border: none; color: #eee; filter: drop-shadow(2px 2px 0px rgba(0, 0, 0, 0.1)); font-size: 16px; cursor: pointer; } #useful-info { display: flex; flex-wrap: wrap; flex-direction: column; align-items: flex-start; height: 50px; margin-top: 10px; font-size: 14px; } #switch-ses { display: flex; } #switch-ses select { display: block; width: 85%; margin: 0px 10px 0 0; box-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.1); } #switch-ses button { background: none; box-shadow: unset; border: 0px; color: white; cursor: pointer; width: 40px; font-size: 20px; } .more-title { display: flex; flex-direction: row; justify-content: space-between; align-items: stretch; margin: 20px -15px 10px; padding: 5px 15px 10px; background: rgba(0, 0, 0, 0.2); height: 50px; } .more-title:first-child { margin: -15px -15px 10px; } #socket-info { display: flex; align-items: baseline; gap: 12px; } .more-title > * { margin: 0; } #clone-status > p { position: relative; margin: 10px 0 0; } #heat-range, #scene-alpha-range { display: flex; margin: 10px 0; } #heat-range input[type="number"], #scene-alpha-range input[type="number"] { width: 26%; } #heat-range input[type="range"], #scene-alpha-range input[type="range"] { margin: 10px; box-shadow: none; } `; let styles = document.createElement("style"); styles.type = "text/css"; styles.appendChild(document.createTextNode(css)); document.head.appendChild(styles); window.getClass = (DOMClass) => { return document.getElementsByClassName(DOMClass); }; window.getId = (DOMId) => { return document.getElementById(DOMId); }; const getClass = window.getClass; const getId = window.getId; /* @IntroJsStyles */ document.querySelector("#hud-intro > div.hud-intro-wrapper > div > div").removeChild(document.querySelector("#hud-intro > div.hud-intro-wrapper > div > div > button")); getClass("hud-intro-corner-top-left")[0].insertAdjacentHTML("afterbegin", `
`); document.querySelector(".hud-intro-corner-top-left > div").insertAdjacentElement("beforeend", document.querySelector("#hud-intro > div.hud-intro-wrapper > div > div > label")); document.querySelector("#hud-intro").setAttribute("style", "--bg-image: url('https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/BG_eden_append_light.webp?v=1717285760533');"); document.querySelector("#hud-intro").insertAdjacentHTML("afterbegin", `
`); document.querySelector("#hud-intro").insertAdjacentHTML("beforeend", `

Play


Click to play

`); document.getElementsByClassName('hud-party-tag')[0].setAttribute('maxlength', 49); document.getElementsByClassName('hud-intro-name')[0].setAttribute('maxlength', 29); const allLines = {}; function createLines(linesOnScreen) { // if (Object.keys(allLines).length >= linesOnScreen) return; for (const lineId in allLines) { const line = allLines[lineId]; if (line.x < -line.length) delete allLines[lineId]; }; for (let i = 0; i < (linesOnScreen - Object.keys(allLines).length); i++) { const rctx = ["background-canvas", "foreground-canvas"][Math.floor(Math.random() * 2)]; const rLineLength = getRandomArbitrary(50, 400); const rPosY = getRandomArbitrary(50, window.innerHeight - 50); const rSpeed = getRandomArbitrary(20, 50); const rTimeout = getRandomArbitrary(20, 5000); allLines[genUUID()] = {x: window.innerWidth, y: rPosY, length: rLineLength, speed: rSpeed, ctxId: rctx, timeout: rTimeout, shouldTimeout: true}; }; }; function drawLine() { for (const lineId in allLines) { const line = allLines[lineId]; // console.log(line); // setTimeout(() => { const ctx = getId(line.ctxId).getContext("2d"); ctx.strokeStyle = "White"; ctx.lineCap = "round"; ctx.lineWidth = 5; ctx.beginPath(); ctx.moveTo(line.x, line.y); ctx.lineTo(line.x + line.length, line.y); ctx.stroke(); line.shouldTimeout = false; // }, line.shouldTimeout ? line.timeout : 0); }; } function moveLine(lineIndex) { for (const lineId in allLines) { allLines[lineId].x -= allLines[lineId].speed; }; } let drawLoopId; function drawLoop() { const linesOnScreen = 5; createLines(linesOnScreen); const background = getId("background-canvas"); const bctx = background.getContext("2d"); bctx.clearRect(0, 0, background.width, background.height); const foreground = getId("foreground-canvas"); const fctx = foreground.getContext("2d"); fctx.clearRect(0, 0, foreground.width, foreground.height); moveLine(); drawLine(); drawLoopId = requestAnimationFrame(drawLoop); } drawLoopId = requestAnimationFrame(drawLoop); game.network.addEnterWorldHandler((e) => { if (!e.allowed) return game.ui.components.Intro.hideLoadingScreen(); cancelAnimationFrame(drawLoopId); }); function resizeIntroCanvases() { const background = getId("background-canvas"); background.width = window.innerWidth; background.height = window.innerHeight; const foreground = getId("foreground-canvas"); foreground.width = window.innerWidth; foreground.height = window.innerHeight; }; resizeIntroCanvases(); window.addEventListener("resize", resizeIntroCanvases); // game.ui.components.Intro.submitElem = getId("start-btn"); game.ui.components.Intro.hideLoadingScreen = function() { getId("hud-intro-overlay").style.display = "none"; getId("hud-intro-overlay").style.opacity = 0; getId("loading-div").style.display = "none"; }; game.ui.components.Intro.showLoadingScreen = function() { getId("select-session-add").style.display = "none"; getId("endpoint-add-menu").style.display = "none"; getId("session-add-menu").style.display = "none"; getId("hud-intro-overlay").style.display = "flex"; getId("hud-intro-overlay").style.opacity = 1; getId("loading-div").style.display = "flex"; }; game.ui.components.Intro.onSubmitClick = function () { const realNicknameLength = new Blob([this.nameInputElem.value]).size; if (realNicknameLength > 29) return void game.ui.components.Intro.onConnectionError('Your nickname length is too long. Please shorten it/use less special characters.'); const server = this.ui.getOption(`servers`)[this.serverElem.value]; localStorage.setItem(`name`, this.nameInputElem.value.trim()); this.connecting || ( this.connecting = true, getId("hud-intro-overlay").style.display = "flex", getId("hud-intro-overlay").style.opacity = 1, getId("loading-div").style.display = "flex", this.connectionTimer = setTimeout(() => { const _this = game.ui.components.Intro; _this.connecting = false; game.network.disconnect(); _this.hideLoadingScreen(); _this.serverElem?.classList.add('has-error'); _this.errorElem.style.display = 'block'; _this.errorElem.innerText = `We failed to join the game - this is a known issue with anti-virus software. Please try disabling any web filtering features.`; }, 15000), this.errorElem.style.display = `none`, this.ui.setOption(`nickname`,this.nameInputElem.value.trim()), this.ui.setOption(`serverId`, this.serverElem.value), game.network.connect(server) ); } getId("start-btn").onclick = game.ui.components.Intro.onSubmitClick.bind(game.ui.components.Intro); game.ui.components.Intro.onEnterWorld = function (data) { this.connecting = false; if (this.connectionTimer) { clearInterval(this.connectionTimer); delete this.connectionTimer; } if (!data.allowed) { _this.hideLoadingScreen(); this.serverElem.classList.add('has-error'); this.errorElem.style.display = 'block'; this.errorElem.innerText = 'This server is currently full. Please try again later or select another server.'; return; } this.hide(); }; game.ui.components.Intro.onConnectionError = function (errorText) { errorText ||= `We were unable to connect to the gameserver. Please try another server.`; this.connecting = false; this.connectionTimer && ( clearInterval(this.connectionTimer), delete this.connectionTimer ); this.hideLoadingScreen(); this.serverElem.classList.add('has-error'); this.errorElem.style.display = 'block'; this.errorElem.innerText = errorText; }; document.addEventListener('keyup', function(e) { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { if (e.code == 'Escape') { game.ui.components.Intro.hideLoadingScreen(); }; if (e.key == "Enter") { (game.ui?.playerTick?.dead === 1) && game.ui.components.Chat.startTyping(); }; if (e.key == ";") { game.ui.getPlayerPetUid() && Game.currentGame.network.sendRpc({name: "DeleteBuilding", uid: game.ui.getPlayerPetUid()}); } if (e.key == "'") { if (game.ui.getPlayerPetUid()) { game.network.sendRpc({name: "BuyItem", itemName: "PetRevive", tier: 1}); game.network.sendRpc({name: "EquipItem", itemName: "PetRevive", tier: 1}); }; }; if (e.code == 'KeyC' && !e.ctrlKey) { setTimeout(() => { document.querySelector('#joinWithPsk').style.display = 'block'; document.querySelector('#joinWithPsk').focus(); document.querySelector('#joinWithPsk').value = ""; }, 100); } }; }); /* @Misc. */ getId('hud').insertAdjacentHTML('beforeend', ` `); document.querySelector('#joinWithPsk').addEventListener('keyup', (e) => { if (e.key == "Enter" || e.key == "Escape") { e.target.style.display = 'none'; e.key == "Enter" && game.network.sendRpc({name: "JoinPartyByShareKey", partyShareKey: e.target.value}); }; }); document.querySelector("#hud-resources").appendChild(getId("hud-shield-bar")); document.querySelector("#hud-resources").appendChild(getId("hud-health-bar")); document.querySelector("#hud-respawn > div > div > div").insertAdjacentHTML("afterbegin", `
`); game.ui.components.Respawn.respawnTextElem = getId("respawn-text-div"); game.ui.components.Respawn.onPlayerDeath = function (deadResponse) { window.deadPos = game.ui.playerTick.position; const player = Game.currentGame.world.getEntityByUid(Game.currentGame.world.getMyUid()), playerTick = player.getTargetTick(); this.lastTick = playerTick; this.respawnTextElem.innerHTML = `
  • Wave: ` + playerTick.wave + `
  • Score: ` + playerTick.score.toLocaleString() + `
  • Leaderboard: #` + (game.ui.components.Leaderboard.leaderboardData.find(e => e.uid == game.world.myUid).rank + 1) + `
  • `; this.show(); } game.network.addRpcHandler("Dead", game.ui.components.Respawn.onPlayerDeath.bind(game.ui.components.Respawn)); /* @ModMenuJsStyles */ getId('hud-menu-settings').innerHTML = `

    Advanced

    Build Player WS & Raid Visual Misc.
    `; getClass('hud-settings-grid')[0].innerHTML = `
    `; const hint_enum = { 0: `
  • Shift + 1 [!] toggles Wall Block.
  • Comma [,] toggles Rebuilder.
  • Period [.] toggles Auto Upgrade.
  • `, 1: `
  • Shift-click on the minimap to navigate.
  • Equal [=] toggles Auto Bow.
  • C toggles quick join via PSK input.
  • G toggles zoom menu.
  • Dash [-] toggles local info indicators.
  • Semi-colon [;] deletes your pet.
  • Back-tick ['] revives your pet.
  • `, 2: ``, 3: /*`
  • ? toggles Screenshot Mode.
  • ~ marks your position.
  • + toggles Map Inspector.
  • `*/``, 4: ``, }; function refreshMore(page) { const container = document.querySelector("#" + page + " > div.hud-settings-more"); for (let children of container.children) { children.style.display = "none"; } } function refreshPage() { for (let i = 0; i < getClass('hud-settings-grid')[0].children.length; i++) getId(`page${i}`).style.display = "none"; }; function setPage(page) { const lastPage = parseInt(getClass('hud-settings-grid')[0].getAttribute('page')); refreshPage(); getClass("hud-menu-tabs")[0].children[lastPage].classList.remove("is-active"); getClass("hud-menu-tabs")[0].children[page].classList.add("is-active"); getId(`page${page}`).style.display = "flex"; getClass('hint-controls')[0].innerHTML = hint_enum[page]; getClass('hud-settings-grid')[0].setAttribute('page', page); }; setPage('0'); Array.from(getClass("hud-menu-tabs")[0].children).forEach((e) => { e.onclick = () => { setPage(e.getAttribute("data-menu")); }; }); const menu = { page0: { AHRC: { name: `AHRC`, description: `Automatically harvests resources.`, more: { html: `

    AHRC type:



    The All option is both Harvest + Collect. `, functions: () => {}, }, }, wallBlock: { name: `Defense Block`, description: `Allows you to place mulitple defenses at once.`, more: { html: `

    Block width:



    Block height:



    Type of defense:



    Default value is 5x5, maximum is 15x15 (cells). `, functions: () => { getId("blockX").onchange = () => { game.script.wallBlock.wallElem.setAttribute("data-tier", `${getId("blockX").value}x${getId("blockY").value}`); }; getId("blockY").onchange = () => { game.script.wallBlock.wallElem.setAttribute("data-tier", `${getId("blockX").value}x${getId("blockY").value}`); }; getId("defense-select").onchange = ({target}) => { game.script.wallBlock.typeOfDefense = target.value; game.script.wallBlock.wallElem.setAttribute("data-building", target.value); }; } }, onCallback: () => { game.script.wallBlock.wallElem.style.display = "block"; }, offCallback: () => { game.script.wallBlock.wallElem.style.display = "none"; }, }, autoTrap: { name: `Auto Trap`, description: `Automatically traps players with wall blocks.`, more: { html: `

    Should trap:



    Each block is always 7x7. `, functions: () => {} }, }, rebuild: { name: `Auto Rebuild`, description: `Automatically rebuilds dead towers and upgrades them.`, more: { html: `

    Rebuild to tier:



    By behaviour, if the previously destroyed tower has a lower tier than the selected tier, it will be rebuilt to the old tier. `, functions: () => { const tierEnum = { 'current': null, 'tier-1': 1, 'tier-2': 2, 'tier-3': 3, 'tier-4': 4, 'tier-5': 5, 'tier-6': 6, 'tier-7': 7, 'tier-8': 8 }; getId("rebuilderTierOptions").onchange = function({target}) { game.script.rebuild.shouldRebuildToTier = tierEnum[target.value]; }; }, }, onCallback: () => { for (let i in Game.currentGame.ui.buildings) { const building = Game.currentGame.ui.buildings[i]; game.script.rebuild.savedBase[building.type] ||= {}; game.script.rebuild.savedBase[building.type][building.x] ||= {}; game.script.rebuild.savedBase[building.type][building.x][building.y] = building.tier; } }, offCallback: () => { game.script.rebuild.savedBase = {}; game.script.rebuild.toBeReplaced = {}; game.script.rebuild.toBeUpgraded = {}; }, }, autoUpgrade: { name: `Auto Upgrader`, description: `Automatically upgrades towers to maximum tier.`, more: null, onCallback: () => { for (let building of Object.values(game.ui.buildings)) { if (building.tier < 8) { game.script.autoUpgrade.autoUpgradeList[building.uid] = true; } } }, offCallback: () => { game.script.autoUpgrade.autoUpgradeList = {}; } }, AULHT: { name: `AULHT`, description: `Automatically upgrades low-health towers.`, }, autoBuild: { name: "Auto Builder", description: `Automatically builds a base when placing stash.`, }, baseSaver: { name: "Base Saver", description: `Manage all your saved bases here.`, more: { html: `
    `, functions: () => { localStorage.totalSlots ||= 2; getId("return-to-manager").onclick = () => document.querySelector("#more-baseSaver").click(); localStorage.baseslot0 = `3,0,-96,0;3,-96,0,0;3,0,-192,0;3,-96,-240,0;3,-192,0,0;3,-240,-96,0;4,-336,-96,0;4,-432,-288,0;4,-96,-336,0;4,-288,-432,0;8,-192,-384,0;8,-384,-192,0;8,-288,-192,0;8,-192,-192,0;8,-192,-288,0;8,-288,-288,0;8,-384,-384,0;8,-96,-96,0;7,-288,0,0;7,-384,0,0;7,-432,-96,0;7,0,-288,0;7,0,-384,0;7,-96,-432,0;0,-168,-504,0;0,-216,-504,0;0,-264,-552,0;0,-312,-552,0;0,-312,-504,0;0,-504,-216,0;0,-504,-168,0;0,-552,-264,0;0,-552,-312,0;0,-504,-312,0;1,-360,-456,0;1,-456,-360,0;9,96,-96,0;9,-96,96,0;|Lucky's 1P corner farm base|Can be changed for current session|default`; window.isSlotEmpty = function(index) { const baseData = localStorage[`baseslot${index}`]?.split("|"); return !!baseData?.[0]; }; window.createBaseSlot = function() { const oldTotalSlots = parseInt(localStorage.totalSlots); const nextItem = oldTotalSlots + 1; localStorage.totalSlots = nextItem; localStorage[`baseslot${nextItem}`] = '|||'; document.querySelector("#more-baseSaver").click(); }; window.saveCurrentBaseConfig = function() { const [design, title, description, date] = [...document.querySelectorAll("#target-base-design, #target-base-name, #target-base-description"), new Date().toLocaleDateString()]; const baseManagement = getId('base-management'); const currentBaseItem = baseManagement.getAttribute('current-item'); localStorage[`baseslot${currentBaseItem}`] = `${design.value}|${title.value}|${description.value}|${date}`; return void game.ui.components.PopupOverlay.showHint('Current base configuration saved.'); }; }, bind: () => { const baseContainer = getId('base-container'); const baseManagement = getId('base-management'); baseContainer.innerHTML = ''; baseManagement.style.display = "none"; for (let i = 0; i < parseInt(localStorage.totalSlots) + 1; i++) { const baseItem = document.createElement('div'); const baseData = localStorage[`baseslot${i}`]?.split("|"); const baseId = genUUID(); baseData?.[1] && (baseData[1] = window.filterXSS(baseData[1])); baseData?.[2] && (baseData[2] = window.filterXSS(baseData[2])); baseItem.classList.add('base-card'); baseItem.id = `base-item-${i}`; baseItem.setAttribute('item-base', i); baseItem.innerHTML = ` `; const [title, description, date] = baseItem.children; title.innerText = baseData?.[1] || "Unoccupied"; description.innerText = baseData?.[2] || "-"; date.innerText = baseData?.[3] || ""; baseItem.onclick = function() { const [titleField, descriptionField, designField] = [...document.querySelectorAll("#target-base-name, #target-base-description, #target-base-design")]; designField.value = baseData?.[0] || ""; titleField.value = baseData?.[1] || ""; descriptionField.value = baseData?.[2] || ""; getId("encode-target-design").onclick = game.script.builder.recordBase.bind(game.script.builder); getId("view-target-design").onclick = () => game.script.builder.showOverlay(designField.value, 10000); baseManagement.setAttribute('current-item', i); for (let otherItem = 0; otherItem < parseInt(localStorage.totalSlots) + 1; otherItem++) { const item = getId(`base-item-${otherItem}`); const isThisItem = item.getAttribute('item-base') == i; if (isThisItem) continue; item.style.display = "none"; }; baseItem.style.transform = 'translate(0px, 306px)'; getId('add-base-slot').style.display = "none"; baseManagement.style.display = "block"; getId("build-design").onclick = function() { game.ui.components.PopupOverlay.showConfirmation("Are you sure you want to build base?", 5000, function() { game.script.builder.buildBase(designField.value); }); }; const {wood, stone} = game.script.builder.calculateResourceNeeded(designField.value); const neededPlayers = game.script.builder.calculateNeededPlayers(designField.value); getId("useful-info").innerHTML = `
  • Wood: ${wood}
  • Needed players: ${neededPlayers}
  • Stone: ${stone}
  • `; const autoBuildElem = getId("shouldBuildBase"); autoBuildElem.checked = sessionStorage[`base-item-${i}`] && sessionStorage[`base-item-${i}`] == "true"; autoBuildElem.onchange = function() { if (autoBuildElem.checked == true) { for (let otherItem = 0; otherItem < parseInt(localStorage.totalSlots) + 1; otherItem++) sessionStorage[`base-item-${otherItem}`] = false; sessionStorage[`base-item-${i}`] = true; game.script.autoBuild.defaultBase = designField.value; } }; }; baseContainer.appendChild(baseItem); } const addItem = document.createElement('div'); addItem.classList.add('base-card'); addItem.id = "add-base-slot"; addItem.innerHTML = ` Add another base slot `; addItem.onclick = window.createBaseSlot; baseContainer.appendChild(addItem); }, }, }, }, page1: { autoAim: { name: "Auto Aim", description: "Automate attacks against enemies.", more: { html: `

    Aim for:

    `, }, }, autoHeal: { name: "Auto Heal", description: "Heals your player/pet automatically.", more: { html: ` Define the health percentage where the Auto Heal will kick in. Default value is 15%. `, functions: () => {}, } }, autoRespawn: { name: "Auto Respawn", description: "Automatically respawns player upon death.", }, spam: { name: `Chat Spam`, description: `Annoys other players with messages.`, more: { html: ` Input the text that you want to spam. If no text is inputted, random messages with be sent. `, functions: () => { const spamInputElem = getId("chat-spam-input"); spamInputElem.oninput = game.script.spam.onchange.bind(game.script.spam); } }, onCallback: () => { game.script.spam.start(); }, offCallback: () => { game.script.spam.stop(); }, }, movementCopy: { name: `Movement Copy`, description: `Copies the closest player's movement.`, more: null, offCallback: () => { game.script.movementCopy.target = null; }, }, }, page2: { sessions: { name: "Session Saver", description: "Switch sockets on the fly.", more: { html: `

    Manage

    `, functions: () => { let isInit = false; game.network.addEnterWorldHandler(() => { if (isInit) return; isInit = true; getId("switch-ses").insertBefore(getId("sesSelect"), getId("switch-btn")); getId("switch-ses").insertAdjacentElement("afterend", getId("addBtn")); getId("switch-ses").insertAdjacentElement("afterend", getId("delBtn")); getId("switch-ses").insertAdjacentHTML("afterend", `
    `); getId("sesSelect").style.display = "block"; getId("delBtn").style.display = "inline-block"; getId("addBtn").style.display = "inline-block"; }); getId("refresh-ses").onclick = () => game.script.sessions.fetchSessions(); getId("switch-btn").onclick = () => { if (!getId("useSes").checked) { getId("useSes").checked = true; getId("useSes").onchange(); }; game.network.disconnect(); game.network.connect(); }; }, }, }, cloneSockets: { name: "Clones", description: "Otherwise known as sockets, alts, etc.", more: { html: `

    Status

    0 active
    Nothing here yet...

    General

    Default socket type:

    Multibox

    Default movement type:

    `, functions: () => { getId("toggle-status").onclick = () => { const cloneStatus = getId("clone-status"); getId("toggle-status").innerHTML = ``; cloneStatus.style.display = cloneStatus.style.display === "none" ? "block" : "none"; }; const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type == 'childList') { getId("clone-status").firstChild.localName == "span" && getId("clone-status").removeChild(getId("clone-status").firstChild); }; }; }); observer.observe(getId("clone-status"), { attributes: true, childList: true, subtree: true }); for (const option in game.script.sockets.shared.options) { const toggleElem = getId("alt-" + option); toggleElem.addEventListener("change", ({target}) => { if (target.checked) { game.script.sockets.shared.options[option].enabled = true; game.script.sockets.shared.options[option]?.onCallback?.(); } else { game.script.sockets.shared.options[option].enabled = false; game.script.sockets.shared.options[option]?.offCallback?.(); }; }); } getId("movementTypeOptions").onchange = ({target}) => { for (const uuid in game.script.sockets.shared.all) { const socket = game.script.sockets.shared.all[uuid]; if (socket.typeSpecificVariables.overrides && !socket.typeSpecificVariables.overrides.autoMove) { socket.typeSpecificVariables.autoMove = parseInt(target.value); }; }; }; }, }, isToggle: false, customButtonText: "Send", callback: () => { const type = getId("socketTypeOptions").value; game.script.sockets.createSocket(type); }, }, }, page3: { optimize: { name: "Optimizers", description: "Disables rendering features for better performance.", more: { html: `

    Sprite refreshing

    Other

    `, functions: () => { for (const option in game.script.optimize) { if (["background", "init"].indexOf(option) > -1) continue; getId(option).addEventListener("change", ({target}) => { game.script.optimize[option] = target.checked; }); } } }, }, showAoe: { name: "AOE Map", description: "Shows the area of damage for all AOE towers.", more: null, }, stashIndicators: { name: "Stash Indicators", description: "Shows useful borders Gold Stash-related.", more: null, onCallback: () => { const { indicators } = game.script.stashIndicators.currentIndicators; for (const indicator of indicators) indicator.setVisible(true); }, offCallback: () => { const { indicators } = game.script.stashIndicators.currentIndicators; for (const indicator of indicators) indicator.setVisible(false); }, }, buildingLife: { name: "Building Lifetime", description: "Shows a heat map indicating buildings' lifespan.", more: { html: ` Limit the amount of towers to be indicated via heat map counting from most recently placed towers
    `, functions: () => { getId("heat-slider").addEventListener("input", ({target}) => { game.script.buildingLife.startingIndex = target.valueAsNumber; getId("heat-min").value = target.valueAsNumber; clearTimeout(game.script.buildingLife.refreshTimeout); game.script.buildingLife.refreshTimeout = setTimeout(() => { game.script.buildingLife.refreshMap(); game.options.options.buildingLife && ( game.script.buildingLife.hideMap(), game.script.buildingLife.showMap() ); }, 50); }); getId("scene-alpha-slider").addEventListener("input", ({target}) => { getId("scene-alpha-val").value = target.valueAsNumber; game.script.buildingLife.setSceneryAlpha(target.valueAsNumber); }); }, }, onCallback: () => { game.script.buildingLife.showMap(); }, offCallback: () => { game.script.buildingLife.hideMap(); }, }, grouping: { name: "Grouping Grid", description: "Shows the grids of tower ranges' groups.", more: null, isToggle: false, customButtonText: "Cycle", callback: () => { game.script.grouping.cycleGrid(); }, }, bossAlert: { name: "Boss Alert", description: "Alerts you before a boss wave.", more: null, }, }, page4: {}, }; const addFunctionToElem = ({ id, option, buttonText, colors, isToggle, callback, onCallback, offCallback }) => { const options = game.options.options; colors ||= 'btn-red?btn-theme'; getId(id).addEventListener('click', e => { if (isToggle === false) { callback?.(); } else { let toggleColor = colors.split('?'); if (options[option] === false) { options[option] = true; toggleColor[1] && e.target.classList.remove(toggleColor[1]); e.target.classList.add(toggleColor[0]); buttonText === undefined || (e.target.innerText = `Disable ${buttonText}`); onCallback?.(); } else { options[option] = false; e.target.classList.remove(toggleColor[0]); toggleColor[1] && e.target.classList.add(toggleColor[1]); buttonText === undefined || (e.target.innerText = `Enable ${buttonText}`); offCallback?.(); } }; }); }; /* @GeneralFunctions + Enums */ const towerCodes = ["Wall", "Door", "SlowTrap", "ArrowTower", "CannonTower", "MeleeTower", "BombTower", "MagicTower", "GoldMine", "Harvester"]; const allBossWaves = [9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 121]; function bindTooltip(elem, innerHTML, anchor = 'top') { elem.targetElem = elem; elem.anchor = anchor; elem.hide = function() { this.tooltipElem && (this.tooltipElem.remove(), delete this.tooltipElem); }; elem.targetElem.addEventListener('mouseenter', function() { let toolTip = innerHTML; document.body.insertAdjacentHTML(`beforeend`, toolTip); this.tooltipElem = document.getElementById(`hud-tooltip`); let clientRect = this.targetElem.getBoundingClientRect(); let position = {'left': 0, 'top': 0}; switch(this.anchor) { case 'top': position.left = clientRect.left + clientRect.width / 0x2 - this.tooltipElem.offsetWidth / 0x2; position.top = clientRect.top - this.tooltipElem.offsetHeight - 0x14; break; case 'bottom': position.left = clientRect.left + clientRect.width / 0x2 - this.tooltipElem.offsetWidth / 0x2; position.top = clientRect.top + clientRect.height + 0x14; break; case 'left': position.left = clientRect.left - this.tooltipElem.offsetWidth - 0x14; position.top = clientRect.top + clientRect.height / 0x2 - this.tooltipElem.offsetHeight / 0x2; break; case 'right': position.left = clientRect.left + clientRect.width + 0x14; position.top = clientRect.top + clientRect.height / 0x2 - this.tooltipElem.offsetHeight / 0x2; break; } this.tooltipElem.className = `hud-tooltip hud-tooltip-` + this.anchor; this.tooltipElem.style.left = position.left + 'px'; this.tooltipElem.style.top = position.top + 'px'; this.tooltipElem.style.zIndex = "999"; }); elem.targetElem.addEventListener(`mouseleave`, elem.hide); } function inRange(pos, target, range) { return pos < target + range && pos > target - range; } function getRandomArbitrary(min, max) { return Math.random() * (max - min) + min; } function isPointInCircle(circle, point, radius) { if ((point.x - circle.x)**2 + (point.y - circle.y)**2 <= radius**2) return true; return false; } function genUUID() { return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace( /[018]/g, c => ( c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4 ).toString(16) ); }; function getRandomItem(array) { return array[Math.floor(Math.random() * array.length)]; }; function msToTime(s) { // Pad to 2 or 3 digits, default is 2 function pad(n, z) { z = z || 2; return ('00' + n).slice(-z); } var ms = s % 1000; s = (s - ms) / 1000; var secs = s % 60; s = (s - secs) / 60; var mins = s % 60; var hrs = (s - mins) / 60; return pad(hrs) + ':' + pad(mins) + ':' + pad(secs) + '.' + pad(ms, 3); } function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); if(result){ var r= parseInt(result[1], 16); var g= parseInt(result[2], 16); var b= parseInt(result[3], 16); return [r, g, b]; } return null; }; function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const temp = array[i]; array[i] = array[j]; array[j] = temp; }; return array; } function garbageGenerator(garbageLength = 25) { let garbageCharacters = localStorage.literallyEveryUnicodeEver; let garbage = ""; for (let i = 0; i < garbageLength; i++) garbage += garbageCharacters[Math.floor(Math.random() * garbageCharacters.length)]; return garbage; } function counter(e = 0) { if (e <= -0.99949999999999999e24) { return Math.round(e/-1e23)/-10 + "TT"; } if (e <= -0.99949999999999999e21) { return Math.round(e/-1e20)/-10 + "TB"; } if (e <= -0.99949999999999999e18) { return Math.round(e/-1e17)/-10 + "TM"; } if (e <= -0.99949999999999999e15) { return Math.round(e/-1e14)/-10 + "TK"; } if (e <= -0.99949999999999999e12) { return Math.round(e/-1e11)/-10 + "T"; } if (e <= -0.99949999999999999e9) { return Math.round(e/-1e8)/-10 + "B"; } if (e <= -0.99949999999999999e6) { return Math.round(e/-1e5)/-10 + "M"; } if (e <= -0.99949999999999999e3) { return Math.round(e/-1e2)/-10 + "K"; } if (e <= 0.99949999999999999e3) { return Math.round(e) + ""; } if (e <= 0.99949999999999999e6) { return Math.round(e/1e2)/10 + "K"; } if (e <= 0.99949999999999999e9) { return Math.round(e/1e5)/10 + "M"; } if (e <= 0.99949999999999999e12) { return Math.round(e/1e8)/10 + "B"; } if (e <= 0.99949999999999999e15) { return Math.round(e/1e11)/10 + "T"; } if (e <= 0.99949999999999999e18) { return Math.round(e/1e14)/10 + "TK"; } if (e <= 0.99949999999999999e21) { return Math.round(e/1e17)/10 + "TM"; } if (e <= 0.99949999999999999e24) { return Math.round(e/1e20)/10 + "TB"; } if (e <= 0.99949999999999999e27) { return Math.round(e/1e+23)/10 + "TT"; } if (e >= 0.99949999999999999e27) { return Math.round(e/1e+23)/10 + "TT"; } } const getClock = (_date) => { let date = _date || new Date(), d = date.getDate(), d1 = date.getDay(), h = date.getHours(), m = date.getMinutes(), s = date.getSeconds(), session = "PM"; if (h == 2) h = 12; if (h < 13) session = "AM" if (h > 12) { session = "PM"; h -= 12; }; h = (h < 10) ? "0" + h : h; m = (m < 10) ? "0" + m : m; s = (s < 10) ? "0" + s : s; return `${h}:${m} ${session}`; } const measureDistance = (obj1, obj2) => { if (!(obj1.x && obj1.y && obj2.x && obj2.y)) return Infinity; let xDif = obj2.x - obj1.x; let yDif = obj2.y - obj1.y; return Math.abs((xDif**2) + (yDif**2)); }; const getEntityAtPos = (x, y, width = 1, height = 1) => { const cell = game.world.entityGrid.getCellIndexes(x, y, { width, height }); return game.world.entityGrid.getEntitiesInCell(cell); } const isEntityOccupied = (x, y, width = 1, height = 1) => { const cell = game.world.entityGrid.getCellIndexes(x, y, { width, height }); const entity = game.world.entityGrid.getEntitiesInCell(cell); return Object.keys(entity).length > 0; } const canAfford = (resources, costs, tier) => { const { gold, wood, stone } = resources; const goldCosts = costs.goldCosts[tier - 1]; const stoneCosts = costs.stoneCosts[tier - 1]; const woodCosts = costs.woodCosts[tier - 1]; return !(gold < goldCosts || wood < woodCosts || stone < stoneCosts); } const predictDirection = (lastPos, currentPos, acc = 2) => { const anchors = {up: 0, down: 0, left: 0, right: 0}; if (!lastPos || !currentPos) return anchors; if (acc > 100) acc = 100; // limitation might be position changes in the slightest // rounding numbers may help const xDelta = lastPos.x - currentPos.x; const yDelta = lastPos.y - currentPos.y; xDelta < -acc ? (anchors.right = 1) : xDelta > acc && (anchors.left = 1); yDelta < -acc ? (anchors.down = 1) : yDelta > acc && (anchors.up = 1); return anchors; }; const moveTowards = (a, b, acc = 24) => { const packet = {down: 0, up: 0, left: 0, right: 0}; if (a.y - b.y <= acc) packet.down = 1; if (-a.y + b.y <= acc) packet.up = 1; if (-a.x + b.x <= acc) packet.left = 1; if (a.x - b.x <= acc) packet.right = 1; return packet; }; function getIsNextWaveActive() { let isNextWaveActive = false; const allComingBossWaves = allBossWaves.map(wave => wave - 1); for (let wave of allComingBossWaves) { isNextWaveActive = game.ui.playerTick.wave == wave; } return isNextWaveActive; }; class EventEmitter extends EventTarget { constructor() { super(); this._events = {}; }; on(event, callback, options) { this._events[event] ||= []; this._events[event].push(callback); this.addEventListener(event, ({detail: data}) => { callback(data); }, options); }; once(event, callback) { this.on(event, callback, {once: true}); }; emit(event, data) { const Event = new CustomEvent(event, { detail: data }); this.dispatchEvent(Event); }; }; /* @CustomFunctions */ game.options.options = { AHRC: false, wallBlock: false, rebuild: false, autoTrap: false, autoUpgrade: false, AULHT: false, autoBuild: false, dragBox: false, lockAim: false, movementCopy: false, spam: false, autoHeal: true, getRSS: false, autoBow: false, autoAim: false, autoRespawn: false, showAoe: false, stashIndicators: false, buildingLife: false, bossAlert: true, spectate: false, navigator: false, XKey: false, }; game.script = { AHRC: { handlers: [{type: "entityUpdate", names: "onTick"}], checkedHarvesters: new Set(), workingHarvesters: new Set(), excludedHarvesters: new Set(), onTick: function() { const options = game.options.options; if (options.AHRC) { for (let uid in game.world.entities) { if (this.excludedHarvesters.has(parseInt(uid))) continue; const entity = game.world.entities[uid]; if (entity.targetTick.model == "Harvester" && entity.targetTick.partyId == game.ui.playerPartyId && game.ui.playerTick.gold > 0.69) { if (this.checkedHarvesters.has(uid)) { if (entity.targetTick.stone != 0 || entity.targetTick.wood != 0) this.workingHarvesters.add(uid); } else { this.checkedHarvesters.add(uid); game.network.sendRpc({ name: "AddDepositToHarvester", uid: parseInt(uid), deposit: 0.69 }); }; }; if (this.workingHarvesters.has(uid)) { if (entity.targetTick.stone >= entity.targetTick.harvestMax || entity.targetTick.wood >= entity.targetTick.harvestMax) continue; const amount = entity.fromTick.tier * 0.05 - 0.02; const ahrcOptions = getId('ahrcOptions'); ahrcOptions.value !== "c" && game.network.sendRpc({name: "AddDepositToHarvester", uid: parseInt(uid), deposit: amount}); ahrcOptions.value !== "h" && game.network.sendRpc({name: "CollectHarvester", uid: parseInt(uid)}); }; }; }; }, }, wallBlock: { handlers: [{type: "packetFunc", names: ["sendRpc", "onSendRpc", 9]}, {type: "keybind", names: "keyup"}], wallElem: document.createElement("a"), typeOfDefense: "Wall", isEven: function(number) { return number % 2 === 0; }, placeWallBlock: function(blockWidth, blockHeight, data, offset) { let offsetFromTarget, isOffsetUsed = !!offset; isOffsetUsed && (offsetFromTarget = 48 * offset); for (let x = -((blockWidth - (this.isEven(blockWidth) ? 0 : 1)) / 2) * 48; x <= (blockWidth - (this.isEven(blockWidth) ? 0 : 1)) / 2 * 48; x += 48) { // if (isOffsetUsed && (Math.abs(x) <= offsetFromTarget)) continue; for (let y = -((blockHeight - (this.isEven(blockHeight) ? 0 : 1)) / 2) * 48; y <= (blockHeight - (this.isEven(blockHeight) ? 0 : 1)) / 2 * 48; y += 48) { if (isOffsetUsed && Math.abs(y) <= offsetFromTarget && Math.abs(x) <= offsetFromTarget) continue; const posX = data.x + x, posY = data.y + y, shouldPlace = !isEntityOccupied(posX, posY); shouldPlace && (x !== 0 || y !== 0) && game.network.sendPacket(9, {name: "MakeBuilding", type: this.typeOfDefense, x: posX, y: posY, yaw: 0}); }; }; }, onSendRpc: function(data) { if (data.name === "MakeBuilding" && data.type === this.typeOfDefense && game.options.options.wallBlock) { if (!game.script.rebuild.savedBase?.[data.type]?.[data.x]?.[data.y]) { const blockWidth = parseInt(document.querySelector('#blockX').value); const blockHeight = parseInt(document.querySelector('#blockY').value); this.placeWallBlock(blockWidth, blockHeight, data); }; }; }, keyup: function(e) { if (e.key == "!") getId("toggle-wallBlock").click(); }, init: function() { this.wallElem.classList.add("hud-buff-bar-item"); this.wallElem.setAttribute("data-building", "Wall"); this.wallElem.style.display = "none"; this.wallElem.setAttribute("data-tier", `${getId("blockX").value}x${getId("blockY").value}`); document.getElementsByClassName("hud-buff-bar")[0].appendChild(this.wallElem); }, }, autoTrap: { handlers: [{type: "entityUpdate", names: "onEntityUpdate"}], onEntityUpdate: function() { if (game.options.options.diep) { for (let i of game.renderer.npcs.attachments) { const criteria = document.querySelector("#autoTrapOptions").value == "pl" ? i.targetTick.partyId !== game.ui.playerPartyId : i.targetTick.uid !== game.world.myUid; if (i.entityClass === "PlayerEntity" && criteria) { if (isPointInCircle(i.targetTick.position, game.ui.playerTick.position, 500)) { const position = i.targetTick.position; const data = { x: Math.round(position.x / 24) * 24, y: Math.round(position.y / 24) * 24 }; game.script.wallBlock.placeWallBlock(7, 7, data, 1); }; }; }; }; }, }, rebuild: { handlers: [{type: "entityUpdate", names: "onEntityUpdate"}, {type: "rpc", names: ["LocalBuilding"]}, {type: "keybind", names: "keyup"}], goldStashTier: null, savedBase: {}, toBeReplaced: {}, toBeUpgraded: {}, shouldRebuildToTier: null, LocalBuilding: function(buildings) { const options = game.options.options; const allBuildings = Object.values(Game.currentGame.ui.buildings); this.goldStashTier = game.ui.components.BuildingOverlay.getGoldStashTier(); if (options.rebuild) { for (let i in buildings) { const building = buildings[i]; if (building.dead === 1) { // The building has died if (this.savedBase[building.type]?.[building.x]?.[building.y]) { this.toBeReplaced[building.type] ||= {}; this.toBeReplaced[building.type][building.x] ||= {}; this.toBeReplaced[building.type][building.x][building.y] = true; delete this.toBeUpgraded[building.uid]; } } else { // The building was upgraded/placed if (this.savedBase[building.type]?.[building.x]?.[building.y] !== undefined) { if (typeof this.toBeUpgraded[building.uid] === "number" && this.toBeUpgraded[building.uid] <= 1) { this.toBeUpgraded[building.uid] = false; } else if (this.toBeUpgraded[building.uid] === undefined) { if (this.shouldRebuildToTier !== null && this.savedBase[building.type][building.x][building.y] > this.shouldRebuildToTier) { this.toBeUpgraded[building.uid] = this.shouldRebuildToTier; } else if (building.tier < this.savedBase[building.type][building.x][building.y]) { this.toBeUpgraded[building.uid] = this.savedBase[building.type][building.x][building.y]; }; }; delete this.toBeReplaced[building.type]?.[building.x]?.[building.y]; } } } } }, onEntityUpdate: function({ entities }) { const options = game.options.options; if (options.rebuild && this.goldStashTier) { for (let i in this.toBeReplaced) { for (let x in this.toBeReplaced[i]) { for (let y in this.toBeReplaced[i][x]) { Game.currentGame.network.sendRpc({ name: "MakeBuilding", type: i, x: parseInt(x), y: parseInt(y), yaw: 0 }); } } } for (let uid in this.toBeUpgraded) { if (!(uid in entities)) { delete this.toBeUpgraded[uid]; continue; }; if (this.toBeUpgraded[uid] == false || this.toBeUpgraded[uid] <= 1) continue; Game.currentGame.network.sendRpc({ name: "UpgradeBuilding", uid: parseInt(uid) }); this.toBeUpgraded[uid]--; }; }; }, keyup: function(e) { if (e.key == ",") getId("toggle-rebuild").click(); }, }, autoUpgrade: { handlers: [{type: "entityUpdate", names: "onEntityUpdate"}, {type: "rpc", names: ["LocalBuilding"]}, {type: "keybind", names: "keyup"}], autoUpgradeList: {}, goldStashTier: null, LocalBuilding: function(buildings) { const options = game.options.options; this.goldStashTier = game.ui.components.BuildingOverlay.getGoldStashTier(); if (options.autoUpgrade) { for (let i in buildings) { const building = buildings[i]; if (building.dead !== 1) { if (building.tier < 8) this.autoUpgradeList[building.uid] = true; else delete this.autoUpgradeList[building.uid]; }; }; }; }, onEntityUpdate: function({entities}) { const options = game.options.options; if (options.autoUpgrade && this.goldStashTier) { let resourcesClone = { wood: game.ui.playerTick.wood, stone: game.ui.playerTick.stone, gold: game.ui.playerTick.gold } for (let i in this.autoUpgradeList) { const building = game.ui.buildings[i]; if (building?.type !== "GoldStash" && building?.tier >= this.goldStashTier) continue; if (!(i in game.world.entities)) continue; const costs = game.ui.buildingSchema[building.type]; if (canAfford(resourcesClone, costs, building.tier + 1)) { resourcesClone.wood -= costs.woodCosts[building.tier]; resourcesClone.stone -= costs.stoneCosts[building.tier]; resourcesClone.gold -= costs.goldCosts[building.tier]; game.network.sendRpc({name: "UpgradeBuilding", uid: parseInt(i)}); } } } }, keyup: function(e) { if (e.key == ".") getId("toggle-autoUpgrade").click(); }, }, AULHT: { handlers: [{type: "entityUpdate", names: "onTick"}, {type: "rpc", names: ["LocalBuilding"]}], shouldHaveBeenUpgraded: {}, LocalBuilding: function(buildings) { if (game.options.options.AULHT) { for (let i in buildings) { const building = buildings[i]; if (building.dead === 0 && building.uid in this.shouldHaveBeenUpgraded) { building.tier > this.shouldHaveBeenUpgraded[building.uid] && delete this.shouldHaveBeenUpgraded[building.uid]; } } } }, onTick: function({entities}) { if (game.options.options.AULHT) { for (let uid in entities) { const currentEntity = entities[uid]; const worldEntity = game.world.entities[uid]; if (currentEntity == true || worldEntity == undefined) continue; if (uid in this.shouldHaveBeenUpgraded) continue; if (uid in game.ui.buildings && typeof currentEntity.health == 'number') { const buildingHealth = (currentEntity.health / worldEntity.targetTick.maxHealth) * 100; if (buildingHealth <= 20 && worldEntity.targetTick.tier != game.ui.components.BuildingOverlay.getGoldStashTier()) { game.network.sendRpc({name: "UpgradeBuilding", uid: parseInt(uid)}); this.shouldHaveBeenUpgraded[uid] = structuredClone(worldEntity.targetTick.tier); }; }; }; }; }, }, autoBuild: { handlers: [{type: "rpc", names: ["LocalBuilding"]}], defaultBase: '3,0,-96,0;3,-96,0,0;3,0,-192,0;3,-96,-240,0;3,-192,0,0;3,-240,-96,0;4,-336,-96,0;4,-432,-288,0;4,-96,-336,0;4,-288,-432,0;8,-192,-384,0;8,-384,-192,0;8,-288,-192,0;8,-192,-192,0;8,-192,-288,0;8,-288,-288,0;8,-384,-384,0;8,-96,-96,0;7,-288,0,0;7,-384,0,0;7,-432,-96,0;7,0,-288,0;7,0,-384,0;7,-96,-432,0;0,-168,-504,0;0,-216,-504,0;0,-264,-552,0;0,-312,-552,0;0,-312,-504,0;0,-504,-216,0;0,-504,-168,0;0,-552,-264,0;0,-552,-312,0;0,-504,-312,0;1,-360,-456,0;1,-456,-360,0;9,96,-96,0;9,-96,96,0;', LocalBuilding: function(buildings) { if (game.options.options.autoBuild) { for (let i in buildings) { const building = buildings[i]; if (building.type == "GoldStash" && building.dead === 0) game.script.builder.buildBase(this.defaultBase); }; }; }, }, reconstruct: { HORIZONTAL_DELTA: 500, VERTICAL_DELTA: 1, isMapping: false, isMappingNow: false, currentMap: {}, currentEntTowers: {}, currentWidth: 2, currentlyMapping: null, buildNote: document.createElement('p'), entSettings: document.createElement('div'), pathWayIndicators: {}, validCellsIndicators: {}, currentBase: '', entranceGroups: {}, entPriority: ["right", "bottom", "top", "left"], init: function() { this.buildNote.innerHTML = `Map out the entrance path by drag-clicking the right mouse button.
    Press Esc to save the current configuration.`; this.buildNote.style.display = "none"; this.buildNote.style.color = "white"; this.buildNote.style.opacity = '0.5'; this.buildNote.style.textAlign = "center"; getClass('hud-top-center')[0].appendChild(this.buildNote); this.entSettings.id = "ent-settings"; this.entSettings.innerHTML = ` Anchor:
    Entrance span:
    `; this.entSettings.style.display = "none"; getClass("hud")[0].appendChild(this.entSettings); document.addEventListener("keyup", (e) => { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { if (e.key == "Escape") { if (this.isMapping) { this.isMapping = false; this.buildNote.style.display = "none"; game.renderer.follow(game.world.entities[game.world.myUid]); this.currentMap = {}; this.currentEntTowers = {}; this.removeAllIndicators(); this.entSettings.style.display = "none"; }; }; }; }); getId("current-side").addEventListener("select", ({target}) => { this.currentlyMapping = target.value; }); getId("ent-width").addEventListener("input", ({target}) => { target.setAttribute("data-width", target.value + ` cells`); this.currentWidth = target.valueAsNumber; this.updateEntranceTowers(); }); getId("undo-ent").addEventListener("click", ({target}) => { this.currentMap = {}; this.currentEntTowers = {}; this.removeAllIndicators(); this.entSettings.style.display = "none"; }); getId("create-ent").addEventListener("click", ({target}) => { this.entranceGroups[this.currentlyMapping] = Object.values(this.currentEntTowers).map(({x, y, type, yaw}) => { return `${type},${x},${y},${yaw};`; }).join(""); this.currentMap = {}; this.currentEntTowers = {}; for (const validCellIndex in this.validCellsIndicators) { game.renderer.npcs.removeAttachment(this.validCellsIndicators[validCellIndex]); delete this.validCellsIndicators[validCellIndex]; }; this.entSettings.style.display = "none"; }); game.network.addRpcHandler("LocalBuilding", this.onLocalBuilding.bind(this)); getId("hud").addEventListener("mousedown", this.onMouseDown.bind(this)); getId("hud").addEventListener("mousemove", this.onMouseMove.bind(this)); getId("hud").addEventListener("mouseup", this.onMouseUp.bind(this)); }, reconstructBaseByPriority: function() { for (const side of this.entPriority) { game.script.builder.buildBase(this.entranceGroups[side]); }; }, removeAllIndicators: function() { for (const cellIndex in this.currentMap) { game.renderer.npcs.removeAttachment(this.pathWayIndicators[cellIndex]); delete this.pathWayIndicators[cellIndex]; }; for (const validCellIndex in this.validCellsIndicators) { game.renderer.npcs.removeAttachment(this.validCellsIndicators[validCellIndex]); delete this.validCellsIndicators[validCellIndex]; }; }, getNonEntTowers: function() { const map = {}; for (let x = this.goldStash.x - 12 * 48; x < this.goldStash.x + 12 * 48; x++) { for (let y = this.goldStash.y - 12 * 48; y < this.goldStash.y + 12 * 48; y++) { map[x] ||= {}; map[x][y] = false; }; }; for (const relativePos of Object.values(this.currentMap)) { map[this.goldStash.x - relativePos.x][this.goldStash.y - relativePos.y] = true; }; // const movementsX = [1, 0, -1, 0]; const movementsY = [0, 1, 0, -1]; let yourFuckinAnswer = []; for (let XxX = this.goldStash.x - 12 * 48; XxX < this.goldStash.x + 12 * 48; XxX++) { for (let YyY = this.goldStash.y - 12 * 48; YyY < this.goldStash.y + 12 * 48; YyY++) { if (map[XxX][YyY]) continue; let newAnswer = []; let queueX = []; let queueY = []; queueX.push(XxX); queueY.push(YyY); map[XxX][YyY] = true; while (queueX.length != 0) { let x = queueX.shift(); let y = queueY.shift(); newAnswer.push({x, y}); for (let i = 0; i < 4; i++) { let newX = x + movementsX[i]; let newY = y + movementsY[i]; if (map[newX][newY]) continue; if (newX < this.goldStash.x - 12 * 48 || newX >= this.goldStash.x + 12 * 48) continue; if (newY < this.goldStash.y - 12 * 48 || newY >= this.goldStash.y + 12 * 48) continue; map[newX][newY] = true; queueX.push(newX); queueY.push(newY); } } yourFuckinAnswer.push(newAnswer); } } console.log(yourFuckinAnswer); // }, updateEntranceTowers: function() { for (const validCellIndex in this.validCellsIndicators) { game.renderer.npcs.removeAttachment(this.validCellsIndicators[validCellIndex]); delete this.validCellsIndicators[validCellIndex]; }; this.currentEntTowers = {}; for (let cellIndex in this.currentMap) { cellIndex = parseInt(cellIndex); for (let CIx = cellIndex - this.HORIZONTAL_DELTA * this.currentWidth; CIx <= cellIndex + this.HORIZONTAL_DELTA * this.currentWidth; CIx += this.HORIZONTAL_DELTA) { if (CIx < 0 || CIx > this.HORIZONTAL_DELTA ** 2) continue; for (let CIy = CIx - this.VERTICAL_DELTA * this.currentWidth; CIy <= CIx + this.VERTICAL_DELTA * this.currentWidth; CIy += this.VERTICAL_DELTA) { if (CIy < 0 || CIy > this.HORIZONTAL_DELTA ** 2) continue; if (!(CIy in this.currentMap)) this.validCellsIndicators[CIy] = null; }; }; }; for (const validCellIndex in this.validCellsIndicators) { const entities = game.world.entityGrid.getEntitiesInCell(validCellIndex); if (this.validCellsIndicators[validCellIndex] !== null) continue; for (const uid in entities) { const building = game.ui.buildings[uid]; if (!building || building.dead === 1) continue; this.currentEntTowers[uid] = { type: towerCodes.indexOf(building.type), x: this.goldStash.x - building.x, y: this.goldStash.y - building.y, yaw: game.world.entities[building.uid]?.targetTick?.yaw }; const buildingSchema = game.ui.buildingSchema[building.type]; const buildingSize = buildingSchema.gridWidth * 48; const indicator = game.assetManager.models.rangeIndicatorModel({ width: buildingSize, height: buildingSize, }, {r: 111, g: 208, b: 247}, {r: 111, g: 208, b: 247}, 0); indicator.setPosition(building.x, building.y); this.validCellsIndicators[validCellIndex] = indicator; game.renderer.npcs.addAttachment(indicator); }; this.validCellsIndicators[validCellIndex] === null && delete this.validCellsIndicators[validCellIndex]; }; console.log(this.validCellsIndicators); }, startReconstruction: function() { if (!this.goldStash) return; game.ui.components.MenuParty.hide(); game.ui.components.MenuShop.hide(); game.ui.components.MenuSettings.hide(); this.isMapping = true; this.buildNote.style.display = "block"; this.currentBase = game.script.builder.recordBase(false); game.renderer.follow(game.world.entities[this.goldStash.uid]); }, onLocalBuilding: function(buildings) { for (const building of buildings) { if (building.type == "GoldStash") { this.goldStash = building.dead === 0 ? building : null; }; }; }, onMouseDown: function(e) { if (!this.isMapping || e.button !== 2) return; if (!e.repeat) { console.log('a'); this.currentMap = {}; this.isMappingNow = true; }; }, onMouseMove: function(e) { if (!this.isMappingNow) return; const absolutePos = game.renderer.screenToWorld(e.clientX, e.clientY); const cellBeneath = game.world.entityGrid.getCellIndexes(absolutePos.x, absolutePos.y, {width: 1, height: 1})[0]; const cellCoordByCells = game.world.entityGrid.getCellCoords(cellBeneath); const cellCoordsByAbsPos = {x: cellCoordByCells.x * 48, y: cellCoordByCells.y * 48}; this.currentMap[cellBeneath] = { x: this.goldStash.x - cellCoordsByAbsPos.x, y: this.goldStash.y - cellCoordsByAbsPos.y, }; if (!this.pathWayIndicators[cellBeneath]) { console.log(cellBeneath, cellCoordByCells, cellCoordsByAbsPos); const indicator = game.assetManager.models.rangeIndicatorModel({ width: 48, height: 48, }, {r: 255, g: 0, b: 0}, {r: 255, g: 0, b: 0}, 0); indicator.setPosition(cellCoordsByAbsPos.x + 24, cellCoordsByAbsPos.y + 24); game.renderer.npcs.addAttachment(indicator); this.pathWayIndicators[cellBeneath] = indicator; }; }, onMouseUp: function(e) { if (!this.isMappingNow) return; this.isMappingNow = false; this.entSettings.style.display = "block"; this.entSettings.style.left = e.clientX + "px"; this.entSettings.style.top = e.clientY + "px"; this.updateEntranceTowers(); }, }, autoHeal: { init: function() { Game.currentGame.ui._events.playerPetTickUpdate.push(pet => this.petTickUpdate.onTick(pet)); game.ui._events.playerTickUpdate.push(player => this.playerTickUpdate.onTick(player)); }, playerTickUpdate: { hasEquipedPotion: false, lastTickHealth: 100, onTick: function(player) { const options = game.options.options; if (options.autoHeal) { if (!game.ui.inventory.HealthPotion && player.gold >= 100) { Game.currentGame.network.sendRpc({name: "BuyItem", itemName: "HealthPotion", tier: 1}); }; const playerHealth = (player.health / player.maxHealth) * 100; this.hasEquipedPotion = (this.lastTickHealth <= playerHealth); const healThreshold = getId("auto-heal-threshold").valueAsNumber || 15; if (playerHealth <= healThreshold && !this.hasEquipedPotion) { Game.currentGame.network.sendRpc({name: "EquipItem", itemName: "HealthPotion", tier: 1}); }; this.lastTickHealth = playerHealth; }; }, }, petTickUpdate: { buyItem: (itemName, tier = 1) => Game.currentGame.network.sendRpc({name: "BuyItem", itemName, tier}), equipItem: (itemName, tier = 1) => Game.currentGame.network.sendRpc({name: "EquipItem", itemName, tier}), petLevelEnum: [8, 16, 24, 32, 48, 64, 96], petTokenEnum: [100, 100, 100, 100, 200, 200, 300, Infinity], onTick: function(pet) { const options = game.options.options; if (options.autoHeal) { if (pet.health <= 0) { this.buyItem("PetRevive"); this.equipItem("PetRevive"); } let petHealth = (pet.health / pet.maxHealth) * 100; const healThreshold = getId("auto-heal-threshold").valueAsNumber || 15; if (petHealth <= healThreshold) { this.buyItem('PetHealthPotion'); this.equipItem("PetHealthPotion"); } }; this.petLevelEnum.indexOf(game.ui.components.MenuShop.shopItems[pet.model].level) > -1 && ( game.ui.playerTick.token >= this.petTokenEnum[pet.tier - 1] && this.buyItem(pet.model, pet.tier + 1) ); }, }, }, spam: { randomSpamText: [ `?verify`, "hi", "ez", "Super Idol的笑容都没你的甜八月正午的阳光都没你耀眼热爱 105 °C的你滴滴清纯的蒸馏水", "Zǎoshang hǎo zhōngguó xiànzài wǒ yǒu BING CHILLING 🥶🍦", "Wǒ hěn xǐhuān BING CHILLING 🥶🍦 Dànshì sùdù yǔ jīqíng 9 bǐ BING CHILLING 🥶🍦", ], spamInterval: null, spamText: '', fetchUnicode: async function() { if (!localStorage.literallyEveryUnicodeEver) { localStorage.literallyEveryUnicodeEver = await fetch('https://raw.githubusercontent.com/bits/UTF-8-Unicode-Test-Documents/master/UTF-8_sequence_unseparated/utf8_sequence_0-0xffff_assigned_printable_unseparated.txt') .then(response => response.text()); this.randomSpamText.push(`${garbageGenerator()} BIG RAID ${garbageGenerator()}`); } }, onchange: function({target}) { this.spamText = target.value; }, start: function() { this.spamInterval = setInterval(() => { let text; if (this.spamText !== '') text = `${garbageGenerator()} ${this.spamText} ${garbageGenerator()}`; else text = getRandomItem(this.randomSpamText); game.network.sendRpc({ name: "SendChatMessage", channel: "Local", message: text }); }, 1050); }, stop: function() { clearInterval(this.spamInterval); }, init: function() { this.fetchUnicode(); }, }, autoBow: { handlers: [{type: "entityUpdate", names: "onEntityUpdate"}, {type: "keybind", names: "keyup"}], onEntityUpdate: function() { if (game.options.options.autoBow) { game.network.sendInput({space: 0}); game.network.sendInput({space: 1}); }; }, keyup: function(e) { if (e.key == "=" && game.world.inWorld) { if (game.ui.inventory.Bow) { game.options.options.autoBow = !game.options.options.autoBow; game.options.options.autoBow && game.network.sendRpc({name: "EquipItem", itemName: "Bow", tier: game.ui.inventory.Bow.tier}); } else if (game.ui.playerTick.gold > 100) { game.network.sendRpc({name: "BuyItem", itemName: "Bow", tier: 1}); }; } }, }, autoAim: { handlers: [{type: "entityUpdate", names: "onEntityUpdate"}], aimTarget: "player", targets: [], init: function() { getId("autoAimOptions").addEventListener("change", ({target}) => { this.aimTarget = target.value; }); }, onEntityUpdate: function() { const options = game.options.options; if (options.autoAim) { this.targets = []; const entities = game.renderer.npcs.attachments; for (let entity of entities) { switch(this.aimTarget) { case "player": if (entity.fromTick.model == "GamePlayer" && entity.targetTick.partyId !== game.ui.playerPartyId && entity.fromTick.dead == 0) { this.targets.push(entity.fromTick); }; break; case "zombie": if (entity.fromTick.model !== "NeutralTier1" && entity.fromTick.entityClass == "Npc") { this.targets.push(entity.fromTick); }; break; case "zom/dem": if (entity.fromTick.entityClass == "Npc") { this.targets.push(entity.fromTick); }; break; default: if (entity.fromTick.uid !== game.world.myUid) { this.targets.push(entity.fromTick); }; }; }; if (this.targets.length > 0) { const myPos = game.ui.playerTick.position; this.targets.sort((a, b) => { const distThisToA = measureDistance(myPos, a.position); const distThisToB = measureDistance(myPos, b.position); if (distThisToA < distThisToB) return -1; else if (distThisToA > distThisToB) return 1; return 0; }); const target = this.targets[0]; const clientPos = game.renderer.worldToScreen(target.position.x, target.position.y); game.inputManager.onMouseMoved({clientX: clientPos.x, clientY: clientPos.y}); } }; } }, autoRespawn: { handlers: [{type: "rpc", names: ["Dead"]}], Dead: function() { if (!game.options.options.autoRespawn) return; document.querySelector("#hud-respawn > div > div > div > button:nth-child(3)").click(); }, }, getRSS: { handlers: [{type: "entityUpdate", names: "onTick"}, {type: "keybind", names: ["keyup", "mousemove"]}, {type: "rpc", names: ["DayCycle"]}], dayTickData: null, scoreData: {}, lastHoveredPlayers: {}, allowedRSS: true, assignOldTick: function(player, playerTick) { const [wood_1, stone_1, gold_1, token_1, px_1, py_1, info] = playerTick; player.targetTick.oldWood = wood_1; player.targetTick.oldStone = stone_1; player.targetTick.oldGold = gold_1; player.targetTick.oldToken = token_1; player.targetTick.oldPX = px_1; player.targetTick.oldPY = py_1; player.targetTick.info = info; player.targetTick.name = player.targetTick.info; }, highlightColor: function(uid, rgb) { // const hr = hexToRgb(hex); game.world.entities[uid].currentModel.nameEntity.setColor(111, 208, 247); }, resetColor: function(uid) { game.world.entities[uid].currentModel.nameEntity.setColor(220, 220, 220); }, DayCycle: function(e) { this.dayTickData = e; if (!game.ui.playerTick) return; if (game.ui.playerTick.wave == 0) return; for (const player of game.ui.playerPartyMembers) { if (!e.isDay) { const { playerUid } = player; const playerTick = game.world.entities[playerUid].targetTick; this.scoreData[playerUid] ||= { spw: 0, lastWaveScore: playerTick.score, }; this.scoreData[playerUid].spw = playerTick.score - this.scoreData[playerUid].lastWaveScore; this.scoreData[playerUid].lastWaveScore = playerTick.score; }; }; for (const uid in this.scoreData) { game.ui.playerPartyMembers.findIndex(e => e.playerUid == uid) == -1 && delete this.scoreData[uid]; }; }, onTick: function() { const options = game.options.options; if (options.getRSS) !this.allowedRSS && (this.allowedRSS = true); if (options.getRSS || this.allowedRSS) { // game.debug.allEntities = []; for (let player of game.renderer.npcs.attachments) { if (!!player.fromTick.name) { let wood_1 = counter(player.targetTick.wood); let stone_1 = counter(player.targetTick.stone); let gold_1 = counter(player.targetTick.gold); let token_1 = counter(player.targetTick.token); let px_1 = counter(player.targetTick.position.x); let py_1 = counter(player.targetTick.position.y); let timeout_1 = player.targetTick.isPaused ? "On Timeout" : ""; if (options.getRSS && !player.targetTick.oldName) { player.targetTick.oldName = player.targetTick.name; let info = ` ${window.filterXSS(player.targetTick.oldName)}; score: ${player.targetTick.score.toLocaleString()} UID: ${player.targetTick.uid} W: ${wood_1}, S: ${stone_1}, G: ${gold_1}, T: ${token_1} partyId: ${Math.round(player.targetTick.partyId)} timeDead: ${msToTime(player.targetTick.timeDead)} ${(player.targetTick.uid in this.scoreData) ? `SPW: ${this.scoreData[player.targetTick.uid].spw.toLocaleString()}` : ""} ${timeout_1} `; this.assignOldTick(player, [wood_1, stone_1, gold_1, token_1, px_1, py_1, info]); } if (!options.getRSS && player.targetTick.oldName) { player.targetTick.info = player.targetTick.oldName; player.targetTick.name = player.targetTick.info; player.targetTick.oldName = null; } if (options.getRSS) { if (player.targetTick.oldGold !== gold_1 || player.targetTick.oldWood !== wood_1 || player.targetTick.oldStone !== stone_1 || player.targetTick.oldToken !== token_1 || player.targetTick.oldPX !== px_1 || player.targetTick.oldPY !== py_1) { let info = ` ${window.filterXSS(player.targetTick.oldName)}; score: ${player.targetTick.score.toLocaleString()} UID: ${player.targetTick.uid} W: ${wood_1}, S: ${stone_1}, G: ${gold_1}, T: ${token_1} partyId: ${Math.round(player.targetTick.partyId)} timeDead: ${msToTime(player.targetTick.timeDead)} ${(player.targetTick.uid in this.scoreData) ? `SPW: ${this.scoreData[player.targetTick.uid].spw.toLocaleString()}` : ""} ${timeout_1} `; this.assignOldTick(player, [wood_1, stone_1, gold_1, token_1, px_1, py_1, info]); } } } } } if (!options.getRSS) this.allowedRSS = false; }, mousemove: function(e) { if (!game.options.options.getRSS) return; const worldPos = game.renderer.screenToWorld(e.clientX, e.clientY); const entitiesUnderMouse = getEntityAtPos(worldPos.x, worldPos.y); for (const uid in this.lastHoveredPlayers) { if (!(uid in entitiesUnderMouse)) { uid in game.world.entities && this.resetColor(uid); delete this.lastHoveredPlayers[uid]; } } for (const uid in entitiesUnderMouse) { if (game.world.entities[uid].targetTick.name && !(uid in this.lastHoveredPlayers)) { this.lastHoveredPlayers[uid] = true; this.highlightColor(uid); } } }, keyup: function(e) { if (e.key == "-") game.options.options.getRSS = !game.options.options.getRSS; }, }, showAoe: { modelIndicatorsPool: {}, projectileEnums: { "CannonProjectile": { parent: 'CannonTower', innerRGB: {r: 60, g: 188, b: 92}, borderRGB: {r: 58, g: 170, b: 86}, aoe: 250, }, "BombProjectile": { parent: 'BombTower', innerRGB: {r: 0xd8, g: 0x0, b: 0x27}, borderRGB: {r: 0xd8, g: 0x4d, b: 0x5c}, aoe: 250, }, "FireballProjectile": { parent: "MagicTower", innerRGB: {r: 73, g: 178, b: 204}, borderRGB: {r: 48, g: 130, b: 150}, aoe: 100 } }, init: function() { const poolSize = 50; for (let modelName in this.projectileEnums) { this.modelIndicatorsPool[modelName] = []; for (let i = 0; i < poolSize; i++) { const {innerRGB, borderRGB, aoe, parent} = this.projectileEnums[modelName]; const aoeIndicator = game.assetManager.models.rangeIndicatorModel({ isCircular: true, radius: aoe, }, innerRGB, borderRGB); aoeIndicator.setVisible(true); this.modelIndicatorsPool[modelName].push(aoeIndicator); } } }, onEntityRemoved: function(t) { if (game.options.options.showAoe) { const entityTick = game.world.entities[t].targetTick; if (entityTick.model in this.projectileEnums) { const { msBetweenFiresTiers, lifetimeTiers } = game.ui.buildingSchema[this.projectileEnums[entityTick.model].parent]; let aoeIndicator; if (this.modelIndicatorsPool[entityTick.model].length > 0) { aoeIndicator = this.modelIndicatorsPool[entityTick.model].shift(); } else { const {innerRGB, borderRGB, aoe, parent} = this.projectileEnums[entityTick.model]; aoeIndicator = game.assetManager.models.rangeIndicatorModel({ isCircular: true, radius: aoe, }, innerRGB, borderRGB); aoeIndicator.setVisible(true); } aoeIndicator.setPosition(entityTick.position.x, entityTick.position.y); game.renderer.ground.addAttachment(aoeIndicator); setTimeout(() => { game.renderer.ground.removeAttachment(aoeIndicator); this.modelIndicatorsPool[entityTick.model].push(aoeIndicator); }, msBetweenFiresTiers[7]); } } }, }, stashIndicators: { BUILDING_DISTANCE: 864, MIN_STASH_DISTANCE: 2496, ZOMBIE_SPAWN_RANGE: 48 * Math.sqrt(2 * (18 ** 2)), currentIndicators: { uid: null, indicators: [], }, onEntityCreated: function(t) { if (t.model == "GoldStash") { if (this.currentIndicators.uid == t.uid) return; for (const indicator of this.currentIndicators.indicators) game.renderer.ground.removeAttachment(indicator); const buildLimitIndicator = game.assetManager.models.rangeIndicatorModel({ width: this.BUILDING_DISTANCE * 2, height: this.BUILDING_DISTANCE * 2, }, null, {r: 0xee, g: 0xee, b: 0xee}, 12); buildLimitIndicator.setVisible(game.options.options.stashIndicators); buildLimitIndicator.setPosition(t.position.x, t.position.y); game.renderer.ground.addAttachment(buildLimitIndicator); const stashLimitIndicator = game.assetManager.models.rangeIndicatorModel({ width: this.MIN_STASH_DISTANCE * 2, height: this.MIN_STASH_DISTANCE * 2, }, null, {r: 0xee, g: 0xee, b: 0xee}, 12); stashLimitIndicator.setVisible(game.options.options.stashIndicators); stashLimitIndicator.setPosition(t.position.x, t.position.y); game.renderer.ground.addAttachment(stashLimitIndicator); const zombieSpawnIndicator = game.assetManager.models.rangeIndicatorModel({ isCircular: true, radius: this.ZOMBIE_SPAWN_RANGE, }, {r: 0xff, g: 0xff, b: 0xff}, {r: 0xee, g: 0xee, b: 0xee}); zombieSpawnIndicator.setVisible(game.options.options.stashIndicators); zombieSpawnIndicator.setPosition(t.position.x, t.position.y); game.renderer.ground.addAttachment(zombieSpawnIndicator); this.currentIndicators = { uid: t.uid, indicators: [buildLimitIndicator, stashLimitIndicator, zombieSpawnIndicator], }; } }, }, grouping: { loadedGrid: false, displays: ["none", "blue", "purple"], currentlyDisplaying: "none", init: function() { game.network.addEnterWorldHandler(() => { if (this.loadedGrid) return; this.makeGrid(); }); document.addEventListener("keyup", function(e) { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { if (e.key == "v") getId("Grouping Grid").click(); }; }); }, makeGrid: function(width, height) { this.loadedGrid = true; const blueCell = game.assetManager.models.rangeIndicatorModel({ width: 196, height: 196, }, null, {r: 111, g: 208, b: 247}, 4); const purpleCell = game.assetManager.models.rangeIndicatorModel({ width: 196, height: 196, }, null, {r: 213, g: 118, b: 211}, 4); this.blueGrid = new game.renderer.spriteType(blueCell.goldRegion.getTexture(), true); this.blueGrid.setDimensions(0, 0, 24000, 24000); this.blueGrid.setAnchor(0, 0); this.blueGrid.setAlpha(1.5); this.blueGrid.setVisible(this.currentlyDisplaying == "blue"); this.purpleGrid = new game.renderer.spriteType(purpleCell.goldRegion.getTexture(), true); this.purpleGrid.setDimensions(48, 48, 23952, 23952); this.purpleGrid.setAnchor(0, 0); this.purpleGrid.setAlpha(1.75); this.purpleGrid.setVisible(this.currentlyDisplaying == "purple"); game.renderer.ground.addAttachment(this.blueGrid); game.renderer.ground.addAttachment(this.purpleGrid); }, refreshGrid: function() { this.blueGrid.setVisible(this.currentlyDisplaying == "blue"); this.purpleGrid.setVisible(this.currentlyDisplaying == "purple"); }, cycleGrid: function() { this.currentlyDisplaying = this.displays[(this.displays.indexOf(this.currentlyDisplaying) + 1) % 3]; this.refreshGrid(); }, }, buildingLife: { handlers: [{type: "rpc", names: ["LocalBuilding"]}], MAX_ALPHA: 5, buildingWeights: {}, indicators: [], sceneryAlpha: 0.5, startingIndex: 0, refreshTimeout: null, LocalBuilding: function(buildings) { for (let building of buildings) { if (building.dead === 1) { building.uid in this.buildingWeights && delete this.buildingWeights[building.uid]; } else { if (building.uid in this.buildingWeights) continue; this.buildingWeights[building.uid] = null; }; }; getId("heat-slider").max = Object.keys(this.buildingWeights).length - 1; getId("heat-max").value = Object.keys(this.buildingWeights).length; this.refreshMap(); }, refreshMap: function() { const sortedUids = Object.keys(this.buildingWeights).sort((a, b) => a - b); const gap = this.MAX_ALPHA / (sortedUids.length - this.startingIndex); let i = 0, _i = -1; for (let uid of sortedUids) { _i++; if (_i < this.startingIndex) { this.buildingWeights[uid] = null; continue; } else if (_i == this.startingIndex) i = 0; this.buildingWeights[uid] = gap * i++; }; }, setSceneryAlpha: function(alpha) { this.sceneryAlpha = alpha; game.options.options.buildingLife && game.renderer.scenery.setAlpha(this.sceneryAlpha); }, showMap: function() { for (let uid in this.buildingWeights) { if (this.buildingWeights[uid] === null) { const entity = game.renderer.scenery.attachments.find(e => e.uid == uid); entity?.setAlpha?.(0); continue; }; const building = game.ui.buildings[uid]; if (!building || building.dead === 1) continue; const buildingSchema = game.ui.buildingSchema[building.type]; const buildingSize = buildingSchema.gridWidth * 48; const indicator = game.assetManager.models.rangeIndicatorModel({ width: buildingSize, height: buildingSize, }, {r: 255, g: 0, b: 0}, {r: 255, g: 0, b: 0}, 0); indicator.setAlpha(this.MAX_ALPHA - this.buildingWeights[uid]); indicator.setPosition(building.x, building.y); this.indicators.push(indicator); game.renderer.npcs.addAttachment(indicator); }; game.renderer.scenery.setAlpha(this.sceneryAlpha); }, hideMap: function() { for (const indicator of this.indicators) game.renderer.npcs.removeAttachment(indicator); for (const entities of game.renderer.scenery.attachments) entities.setAlpha(1); game.renderer.scenery.setAlpha(1); }, }, bossAlert: { handlers: [{type: "keybind", names: ["DayCycle"]}], init: function() { this.bossAlert = document.createElement('p'); this.bossAlert.innerHTML = ` Boss wave incoming`; this.bossAlert.style.display = "none"; this.bossAlert.style.color = "white"; this.bossAlert.style.opacity = '0.5'; getClass('hud-top-center')[0].appendChild(this.bossAlert); }, DayCycle: function(e) { if (!game.ui.playerTick) return; if (e.isDay) this.bossAlert.style.display = (getIsNextWaveActive() && game.options.options.bossAlert) ? "block" : "none"; }, }, lockAim: { handlers: [{type: "keybind", names: "keyup"}], init: function() { game.inputManager._emit = game.inputManager.emit; game.inputManager.emit = function(...args) { if (args[0].indexOf("mouse") > -1 && game.options.options.lockAim) return; return this._emit(...args); }; }, keyup: function(e) { if (e.key == "u") game.options.options.lockAim = !game.options.options.lockAim; }, }, /* @Rebinds */ sell: { sellAllByType: function(type) { if (!game.ui.playerPartyCanSell) return; let lastSoldBuilding = null; let allBuildings = [Infinity, ...Object.values(game.ui.buildings).filter(e => e.type == type)]; let sellInterval = () => { if (!game.ui.buildings[lastSoldBuilding?.uid]) allBuildings.shift(); if (window.sellBreak || allBuildings.length == 0) return; const target = allBuildings[0]; if (target !== undefined && !game.ui.buildings[target]?.dead) { lastSoldBuilding = target; Game.currentGame.network.sendRpc({ name: "DeleteBuilding", uid: parseInt(target.uid) }); setTimeout(sellInterval, 50); }; }; sellInterval(); }, sellAllWithUids: function(uids) { if (!game.ui.playerPartyCanSell) return; let lastSoldBuilding = null; uids = uids.filter(e => game.ui.buildings[e]?.type !== "GoldStash"); let allBuildings = [Infinity, ...uids]; let sellInterval = () => { if (!game.ui.buildings[lastSoldBuilding]) allBuildings.shift(); if (window.sellBreak || allBuildings.length == 0) return; const target = allBuildings[0]; if (target !== undefined && !game.ui.buildings[target]?.dead) { lastSoldBuilding = target; Game.currentGame.network.sendRpc({ name: "DeleteBuilding", uid: parseInt(target) }); setTimeout(sellInterval, 50); }; }; sellInterval(); }, sellAll: function() { this.isSellingAll = true; const sellInterval = () => { if (window.sellBreak) return; if (Object.keys(game.ui.buildings).length > 1 && game.ui.playerPartyCanSell) { Game.currentGame.network.sendRpc({ name: "DeleteBuilding", uid: parseInt(Object.keys(game.ui.buildings)[1]) }); setTimeout(() => { sellInterval(); }, 100); Object.keys(game.ui.buildings).length == 2 && (this.isSellingAll = false); } } sellInterval(); }, sellBuilding: function () { if (this.buildingUid) { if ('GoldStash' == this.buildingId) { game.ui.components.PopupOverlay.showConfirmation(`Are you sure you want to delete all buildings?`, 5000, function() { game.script.sell.sellAll(); }); return this.stopWatching(); } if (this.shouldUpgradeAll) { const id = this.buildingId; game.ui.components.PopupOverlay.showConfirmation(`Are you sure you want to delete all buildings of this type?`, 5000, function() { game.script.sell.sellAllByType(id); }); } else Game.currentGame.network.sendRpc({name: 'DeleteBuilding', uid: this.buildingUid}); }; }, init: function() { game.ui.components.BuildingOverlay.sellBuilding = this.sellBuilding.bind(game.ui.components.BuildingOverlay); }, }, chat: { blockedUids: [], emojiList: { hmm: "https://cdn.discordapp.com/emojis/724365641963929611.png?size=48", pog: "https://cdn.discordapp.com/emojis/721070353337811026.png?size=48", pepehands: "https://cdn.discordapp.com/emojis/733406770139103293.png?size=48", pepeEyes: "https://cdn.discordapp.com/emojis/869573233794486323.gif?size=48", pepeHappy: "https://cdn.discordapp.com/emojis/801475958883614811.png?size=48", sadge: "https://cdn.discordapp.com/emojis/826530556974989344.png?size=48", ha: "https://cdn.discordapp.com/emojis/782756472886525953.png?size=48", kekw: "https://cdn.discordapp.com/emojis/748511358076846183.png?size=48", pogEyes: "https://cdn.discordapp.com/emojis/786979080406564885.png?size=48", appalled: "https://cdn.discordapp.com/emojis/830880294881853530.png?size=48", pogYou: "https://cdn.discordapp.com/emojis/790293794716516430.png?size=48", pogChag: "https://cdn.discordapp.com/emojis/831156303497134090.png?size=48", pogey: "https://cdn.discordapp.com/emojis/790293759861719050.png?size=48", weirdChamp: "https://cdn.discordapp.com/emojis/757553915389673502.png?size=48", monkaS: "https://cdn.discordapp.com/emojis/757179783573405766.png?size=48", yep: "https://cdn.discordapp.com/emojis/758356179477987339.png?size=48", weirdButOkay: "https://cdn.discordapp.com/emojis/831156194247966782.gif?size=48", pogpogpogpog: "https://cdn.discordapp.com/emojis/869580566096379974.gif?size=48", wooyeah: "https://cdn.discordapp.com/emojis/791008461420888084.gif?size=48", idk: "https://cdn.discordapp.com/emojis/882513306164805642.gif?size=48", }, blockPlayer: function(name, uid) { game.ui.components.PopupOverlay.showConfirmation(`Are you sure you want to block ${window.filterXSS(name)} (${uid})?`, 3500, () => { this.blockedUids.push(uid); for (let bl of Array.from(document.getElementsByClassName(`uid${uid}`))) { bl.innerHTML = "Unblock"; bl.style.color = "blue"; bl.onclick = () => this.unblockPlayer(name, uid); }; }, () => {}); }, unblockPlayer: function(name, uid) { this.blockedUids.splice(this.blockedUids.indexOf(uid), 1); for (let bl of Array.from(document.getElementsByClassName(`uid${uid}`))) { bl.innerHTML = "Block"; bl.style.color = "red"; bl.onclick = () => this.blockPlayer(name, uid); }; }, onMessageReceived: function(e) { if (this.blockedUids.includes(e.uid) || window.chatDisabled) return; let a = Game.currentGame.ui.getComponent("Chat"), b = window.filterXSS(e.displayName), c = window.filterXSS(e.message) .replace(/(?:f|F)uck/gi, ``) .replace(/s[3e]x+/gi, ``) .replace(/n+[i1]+gg+[a@]+/i, ``); let arr = c.split(':'); for (let i = 1; i < arr.length; i += 2) { if (!this.emojiList[arr[i]]) arr = [c]; else arr[i] = ``; } let d = a.ui.createElement(`
    Block ${b}: ${arr.join(" ")}${getClock()}
    `); d.children[0].onclick = () => game.script.chat.blockPlayer(b, e.uid); a.messagesElem.appendChild(d); a.messagesElem.scrollTop = a.messagesElem.scrollHeight; }, init: function() { Game.currentGame.network.emitter.removeListener("PACKET_RPC", Game.currentGame.network.emitter._events.PACKET_RPC[1]); Game.currentGame.network.addRpcHandler("ReceiveChatMessage", this.onMessageReceived.bind(this)); document.addEventListener('keyup', function(e) { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { if (e.key == "Enter") { (game.ui?.playerTick?.dead === 1) && game.ui.components.Chat.startTyping(); }; }; }); }, }, parties: { serverPopulation: 0, partyMenu: getClass("hud-menu-party")[0], partyTabs: getClass("hud-party-tabs")[0], partyMembers: getClass("hud-party-members")[0], partyGrid: getClass("hud-party-grid")[0], closedParties: null, shareKeyLog: null, init: function() { this.partyMembers.style.display = "block"; this.partyGrid.style.display = "none"; const privateTab2 = document.createElement("a"); privateTab2.className = "hud-party-tabs-link"; privateTab2.id = "privateTab2"; privateTab2.innerHTML = "Closed Parties"; this.closedParties = document.createElement("div"); this.closedParties.className = "hud-private hud-party-grid"; this.closedParties.id = "privateHud2"; this.closedParties.style.display = "none"; this.partyTabs.appendChild(privateTab2); this.partyMenu.insertBefore(this.closedParties, getClass("hud-party-actions")[0]); const privateTab = document.createElement("a"); privateTab.className = "hud-party-tabs-link"; privateTab.id = "privateTab"; privateTab.innerHTML = "Key Logs"; this.shareKeyLog = document.createElement("div"); this.shareKeyLog.className = "hud-private hud-party-grid"; this.shareKeyLog.id = "privateHud"; this.shareKeyLog.style.display = "none"; this.partyTabs.appendChild(privateTab); this.partyMenu.insertBefore(this.shareKeyLog, getClass("hud-party-actions")[0]); privateTab.addEventListener("click", e => { for (let menu of [this.partyMembers, this.partyGrid, this.closedParties, this.shareKeyLog]) { menu.style.display == "block" && (menu.style.display = "none"); } for (let i of getClass("hud-party-tabs-link")) i.className = "hud-party-tabs-link"; privateTab.className = "hud-party-tabs-link is-active"; this.shareKeyLog.style.display = "block"; }) privateTab2.addEventListener("click", e => { for (let menu of [this.partyMembers, this.partyGrid, this.closedParties, this.shareKeyLog]) { menu.style.display == "block" && (menu.style.display = "none"); } for (let i of getClass("hud-party-tabs-link")) i.className = "hud-party-tabs-link"; privateTab2.className = "hud-party-tabs-link is-active"; this.closedParties.style.display = "block"; }) getClass("hud-party-tabs-link")[0].addEventListener("click", e => { this.shareKeyLog.style.display = "none"; privateTab.className = "hud-party-tabs-link"; this.closedParties.style.display = "none"; privateTab2.className = "hud-party-tabs-link"; }) getClass("hud-party-tabs-link")[1].addEventListener("click", e => { this.shareKeyLog.style.display = "none"; privateTab.className = "hud-party-tabs-link"; this.closedParties.style.display = "none"; privateTab2.className = "hud-party-tabs-link"; }); game.network.addRpcHandler("PartyShareKey", (e) => game.script.parties.onPartyShareKey(e)); game.network.addRpcHandler("SetPartyList", (e) => game.script.parties.onSetPartyList(e)); }, onSetPartyList: function(parties) { this.serverPopulation = 0; for (let party of parties) { this.serverPopulation += party.memberCount; } document.getElementsByClassName("hud-party-server")[0].innerHTML = `${this.serverPopulation}/32`; }, onPartyShareKey: function(e) { const psk = e.partyShareKey, lnk = `https://zombs.io/#/${game.options.serverId}/${psk}/`; this.shareKeyLog.innerHTML += `
  • ${psk} - [Link]

  • `; }, }, /* @NonToggle */ builder: { buildBase: function(design) { const goldStash = Object.values(game.ui.buildings).find(building => building.type == "GoldStash"); if (typeof design !== "string") throw new Error("Argument must be given as a string."); if (goldStash === undefined) throw new Error("You must have a gold stash to be able to use this."); const towers = design.split(";"); for (let towerStr of towers) { const tower = towerStr.split(","); if (tower[0] === "") continue; if (tower.length < 4) throw new Error(`${JSON.stringify(tower)} contains an issue that must be fixed before this design can be replicated.`); Game.currentGame.network.sendRpc({ name: "MakeBuilding", type: towerCodes[parseInt(tower[0])], x: goldStash.x - parseInt(tower[1]), y: goldStash.y - parseInt(tower[2]), yaw: parseInt(tower[3]) }); }; }, recordBase: function(pasteValue = true) { const goldStash = Object.values(game.ui.buildings).find(building => building.type == "GoldStash"); let baseStr = ""; for (let i in game.ui.buildings) { const building = game.ui.buildings[i]; if (towerCodes.indexOf(building.type) < 0) continue; let yaw = 0; if (["Harvester", "MeleeTower"].includes(building.type)) { if (game.world.entities[building.uid] !== undefined) yaw = game.world.entities[building.uid].targetTick.yaw; } baseStr += `${towerCodes.indexOf(building.type)},${goldStash.x - building.x},${goldStash.y - building.y},${yaw};`; } if (pasteValue) getId('target-base-design').value = baseStr; else return baseStr; }, calculateResourceNeeded(design) { const rssNeeded = {wood: 0, stone: 0}; const schema = game.ui.buildingSchema; if (typeof design !== "string") throw new Error("Argument must be given as a string."); const towers = design.split(";"); for (let towerStr of towers) { const tower = towerStr.split(","); if (tower[0] === "") continue; if (tower.length < 4) throw new Error(`${JSON.stringify(tower)} contains an issue that must be fixed before this design can be calculated for resources needed.`); rssNeeded.wood += schema[towerCodes[parseInt(tower[0])]].woodCosts[0]; rssNeeded.stone += schema[towerCodes[parseInt(tower[0])]].stoneCosts[0]; }; return rssNeeded; }, calculateNeededPlayers(design) { if (typeof design !== "string") throw new Error("Argument must be given as a string."); const allTowers = {}; const schema = game.ui.buildingSchema; const towers = design.split(";"); for (let towerStr of towers) { const tower = towerStr.split(","); if (tower[0] === "") continue; if (tower.length < 4) throw new Error(`${JSON.stringify(tower)} contains an issue that must be fixed before this design can be calculated for resources needed.`); allTowers[tower[0]] ||= 0; allTowers[tower[0]]++; }; let highestNeededAmount = 1; for (const towerName in allTowers) { const amount = allTowers[towerName]; const neededAmount = Math.ceil(amount / (schema[towerCodes[parseInt(towerName)]].limit / game.ui.playerPartyMembers.length)); neededAmount > highestNeededAmount && (highestNeededAmount = neededAmount); }; return highestNeededAmount; }, showOverlay: function (design, timeout) { const goldStash = Object.values(game.ui.buildings).find(building => building.type == "GoldStash"); if (typeof design !== "string") throw new Error("Argument must be given as a string."); if (goldStash === null) throw new Error("You must have a gold stash to be able to use this."); this.overlayEntities && (this.overlayEntities.length > 0 && this.overlayEntities.map(e => game.renderer.ui.removeAttachment(e))); this.overlayEntities = []; this.overlayDesign = design; this.isShowingOverlay = true; game.renderer.follow(game.world.entities[goldStash.uid]); setTimeout(() => { const towers = design.split(";"), schema = game.ui.getBuildingSchema(); for (let towerStr of towers) { const towerData = towerStr.split(","); const [type, xWorld, yWorld, yaw] = towerData; const towerLength = towerData.length if (type === "") continue; if (towerLength.length < 4) throw new Error(`${JSON.stringify(towerLength)} contains an issue that must be fixed before this design can be replicated.`); const buildingType = schema[towerCodes[parseInt(type)]], placeholderEntity = Game.currentGame.assetManager.loadModel(buildingType.modelName, {}), { x, y } = game.renderer.worldToUi(goldStash.x - parseInt(xWorld), goldStash.y - parseInt(yWorld)); placeholderEntity.setAlpha(0.5); placeholderEntity.setRotation(parseInt(yaw)); placeholderEntity.setPosition(x, y); Game.currentGame.renderer.ui.addAttachment(placeholderEntity); this.overlayEntities.push(placeholderEntity); } timeout && setTimeout(this.hideOverlay.bind(this), timeout); }, 50); }, hideOverlay: function() { for (let entity of this.overlayEntities) game.renderer.ui.removeAttachment(entity); game.renderer.follow(game.world.entities[game.world.myUid]); this.isShowingOverlay = false; this.overlayDesign = null; }, onResize: function() { this.isShowingOverlay && this.showOverlay(this.overlayDesign); }, init: function() { window.addEventListener("resize", this.onResize.bind(this)); document.addEventListener("zoom", this.onResize.bind(this)); }, }, sessions: { SESSION_CREATE_TIMEOUT: 15000, SESSION_FETCH_TIMEOUT: 10000, staticJSONs: [{name:"BuildingShopPrices",response:{json:'[{"Name":"Wall","Class":"PlayerObject","GoldCosts":[0,5,30,60,80,100,250,800],"WoodCosts":[2,0,0,0,0,0,0,0],"StoneCosts":[0,2,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0,0],"Width":47.99,"Height":47.99,"Health":[150,200,300,400,600,800,1500,2500],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[5,7,12,17,25,40,80,250]},{"Name":"GoldStash","Class":"GoldStash","GoldCosts":[0,5000,10000,16000,20000,32000,100000,400000],"WoodCosts":[0,0,0,0,0,0,0,0],"StoneCosts":[0,0,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0,0],"Width":95.99,"Height":95.99,"Health":[1500,1800,2300,3000,5000,8000,12000,20000],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[50,60,70,90,110,150,400,700]},{"Name":"GoldMine","Class":"GoldMine","GoldCosts":[0,200,300,600,800,1200,8000,30000],"WoodCosts":[5,15,25,35,45,55,700,1600],"StoneCosts":[5,15,25,35,45,55,700,1600],"TokenCosts":[0,0,0,0,0,0,0,0],"Width":95.99,"Height":95.99,"Health":[150,250,350,500,800,1400,1800,2800],"GoldPerSecond":[4,6,7,10,12,15,25,35],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[5,7,12,17,25,40,70,120]},{"Name":"Door","Class":"Door","GoldCosts":[0,10,50,70,150,200,400,800],"WoodCosts":[5,5,0,0,0,0,0,0],"StoneCosts":[5,5,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0,0],"Width":47.99,"Height":47.99,"Health":[150,200,300,500,700,1000,1500,2000],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,1000],"HealthRegenPerSecond":[5,7,12,17,25,40,70,100]},{"Name":"CannonTower","Class":"Tower","GoldCosts":[0,100,200,600,1200,2000,8000,35000],"WoodCosts":[15,25,30,40,60,80,300,800],"StoneCosts":[15,25,40,50,80,120,300,800],"TokenCosts":[0,0,0,0,0,0,0,0],"TowerRadius":[500,500,500,500,600,600,600,600],"MsBetweenFires":[1000,769,625,500,400,350,250,250],"Height":95.99,"Width":95.99,"Health":[150,200,400,800,1200,1600,2200,3600],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,110,150],"DamageToZombies":[20,30,50,70,120,150,200,300],"DamageToPlayers":[5,5,6,6,7,7,8,8],"DamageToPets":[5,5,5,5,5,5,6,8],"DamageToNeutrals":[250,350,450,550,650,750,850,1000],"ProjectileLifetime":[1000,1000,1000,1000,1000,1000,1000,1000],"ProjectileVelocity":[60,65,70,70,75,80,100,140],"ProjectileName":"CannonProjectile","ProjectileAoe":[true,true,true,true,true,true,true,true],"ProjectileAoeRadius":[250,250,250,250,250,250,250,250],"ProjectileCollisionRadius":[10,10,10,10,10,10,10,10]},{"Name":"ArrowTower","Class":"ArrowTower","GoldCosts":[0,100,200,600,1200,2000,8000,35000],"WoodCosts":[5,25,30,40,50,70,300,800],"StoneCosts":[5,20,30,40,60,80,300,800],"TokenCosts":[0,0,0,0,0,0,0,0],"TowerRadius":[600,650,700,750,800,850,900,1000],"MsBetweenFires":[400,333,285,250,250,250,250,250],"Height":95.99,"Width":95.99,"Health":[150,200,400,800,1200,1600,2200,3600],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,110,150],"DamageToZombies":[20,40,70,120,180,250,400,500],"DamageToPlayers":[5,5,6,6,7,7,8,8],"DamageToPets":[5,5,5,5,5,5,6,6],"DamageToNeutrals":[250,350,450,550,650,750,850,1000],"ProjectileLifetime":[1300,1300,1300,1300,1300,1300,1300,1300],"ProjectileVelocity":[60,65,70,70,75,80,120,140],"ProjectileName":"ArrowProjectile","ProjectileAoe":[false,false,false,false,false,false,false,false],"ProjectileCollisionRadius":[10,10,10,10,10,10,10,10]},{"Name":"Harvester","Class":"Harvester","GoldCosts":[0,100,200,600,1200,2000,8000,10000],"WoodCosts":[5,25,30,40,50,70,300,600],"StoneCosts":[5,20,30,40,60,80,300,600],"TokenCosts":[0,0,0,0,0,0,0,0],"Height":95.99,"Width":95.99,"Health":[150,200,400,800,1200,1600,2200,2800],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,110,130],"HarvestAmount":[2.5,4.65,4.55,7.2,8.25,10,13.5,16],"HarvestCooldown":[1500,1400,1300,1200,1100,1000,900,800],"HarvestMax":[400,800,1200,1600,2000,2400,2800,3600],"HarvestRange":[300,300,300,300,300,300,300,300],"DepositCostPerMinute":[200,300,350,500,600,700,1200,1400],"DepositMax":[800,1200,1400,2000,2400,2800,4800,6000],"MaxYawDeviation":[70,70,70,70,70,70,70,70]},{"Name":"BombTower","Class":"Tower","GoldCosts":[0,100,200,600,1200,2000,8000,35000],"WoodCosts":[10,25,40,50,80,120,300,800],"StoneCosts":[10,25,40,50,80,120,300,800],"TokenCosts":[0,0,0,0,0,0,0,0],"TowerRadius":[1000,1000,1000,1000,1000,1000,1000,1000],"MsBetweenFires":[1000,1000,1000,1000,1000,1000,900,900],"Height":95.99,"Width":95.99,"Health":[150,200,400,800,1200,1600,2200,3600],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,110,150],"DamageToZombies":[30,60,100,140,200,600,1200,1600],"DamageToPlayers":[9,9,10,10,11,11,12,12],"DamageToPets":[10,10,10,10,10,10,10,10],"DamageToNeutrals":[250,350,450,550,650,750,850,1000],"ProjectileLifetime":[1000,1000,1000,1000,1000,1000,1000,1000],"ProjectileVelocity":[20,20,20,20,20,20,20,20],"ProjectileName":"BombProjectile","ProjectileAoe":[true,true,true,true,true,true,true,true],"ProjectileIgnoresCollisions":[true,true,true,true,true,true,true,true],"ProjectileAoeRadius":[250,250,250,250,250,250,250,250],"ProjectileCollisionRadius":[10,10,10,10,10,10,10,10],"ProjectileMaxRange":[1000,1000,1000,1000,1000,1000,1000,1000]},{"Name":"MagicTower","Class":"MagicTower","GoldCosts":[0,100,200,600,1200,2000,8000,35000],"WoodCosts":[15,25,40,50,70,100,300,800],"StoneCosts":[15,25,40,50,70,100,300,800],"TokenCosts":[0,0,0,0,0,0,0,0],"TowerRadius":[400,400,400,400,400,400,400,400],"MsBetweenFires":[800,800,700,600,500,400,300,300],"Height":95.99,"Width":95.99,"Health":[150,200,400,800,1200,1600,2200,3600],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,110,150],"DamageToZombies":[10,20,40,50,70,80,120,160],"DamageToPlayers":[5,5,5,6,6,6,7,7],"DamageToPets":[5,5,5,5,5,5,5,5],"DamageToNeutrals":[250,350,450,550,650,750,850,1000],"ProjectileLifetime":[500,500,500,500,500,500,500,500],"ProjectileVelocity":[45,45,45,45,45,45,45,45],"ProjectileName":"FireballProjectile","ProjectileAoe":[true,true,true,true,true,true,true,true],"ProjectileAoeRadius":[100,100,100,100,100,100,100,100],"ProjectileCollisionRadius":[10,10,10,10,10,10,10,10]},{"Name":"MeleeTower","Class":"MeleeTower","GoldCosts":[0,100,200,600,1200,2000,8000,35000],"WoodCosts":[10,25,30,40,50,70,300,800],"StoneCosts":[10,20,30,40,60,80,300,800],"TokenCosts":[0,0,0,0,0,0,0,0],"TowerRadius":[110,110,110,110,110,110,110,110],"MsBetweenFires":[400,333,285,250,250,250,250,250],"Height":95.99,"Width":95.99,"Health":[200,400,800,1200,1600,2200,4000,9000],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,220,350],"DamageToZombies":[80,120,200,280,500,1000,2000,3000],"DamageToPlayers":[5,6,7,8,9,10,11,12],"DamageToPets":[5,5,5,5,5,5,6,6],"DamageToNeutrals":[250,350,450,550,650,750,850,1000],"MaxYawDeviation":[30,30,30,30,30,30,30,30]},{"Name":"SlowTrap","Class":"Trap","GoldCosts":[0,100,200,400,600,800,1000,1500],"WoodCosts":[5,25,30,40,50,70,300,800],"StoneCosts":[5,20,30,40,60,80,300,800],"TokenCosts":[0,0,0,0,0,0,0,0],"Height":47.99,"Width":47.99,"Health":[150,200,400,800,1200,1600,2200,3000],"MsBeforeRegen":[10000,10000,10000,10000,10000,10000,10000,10000],"HealthRegenPerSecond":[2,5,10,20,40,80,110,150],"SlowDuration":[2500,2500,2500,3000,3000,3250,3500,4000],"SlowAmount":[0.4,0.45,0.5,0.55,0.6,0.65,0.7,0.7]}]'},opcode:9},{name:"ItemShopPrices",response:{json:'[{"Name":"Spear","Class":"MeleeWeapon","MsBetweenFires":[250,250,250,250,250,250,250],"DamageToZombies":[30,80,120,300,2000,8000,10000],"DamageToNeutrals":[50,80,100,200,250,400,600],"DamageToBuildings":[0.75,1.5,2.25,3,3.75,4.5,5.25],"DamageToPlayers":[15,16,17,18,20,22,22],"DamageToPets":[3,3.5,4,4.5,5,5.5,5.5],"GoldCosts":[1400,2800,5600,11200,22500,45000,90000],"StoneCosts":[0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0],"Range":[100,100,100,100,100,100,100],"MaxYawDeviation":[50,50,50,50,50,50,50]},{"Name":"Pickaxe","Class":"MeleeWeapon","MsBetweenFires":[300,300,285,250,200,200,200],"DamageToZombies":[20,20,20,20,20,20,20],"DamageToBuildings":[0,0,0,0,0,0,0],"DamageToPlayers":[0,0,0,0,0,0,0],"DamageToNeutrals":[10,10,10,10,10,10,10],"DamageToPets":[0,0,0,0,0,0,0],"GoldCosts":[0,1000,3000,6000,8000,24000,90000],"StoneCosts":[0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0],"Range":[100,100,100,100,100,100,100],"MaxYawDeviation":[70,70,70,70,70,70,70],"IsTool":true,"HarvestCount":[1,2,2,3,3,4,6]},{"Name":"Bow","Class":"RangedWeapon","DamageToZombies":[20,40,100,300,2400,10000,14000],"DamageToBuildings":[2,2.3,2.5,2.7,3,3,3],"DamageToPlayers":[22,24,26,28,30,32,32],"DamageToNeutrals":[50,100,150,200,250,400,700],"DamageToPets":[2,2.3,2.5,2.7,3,3,3],"GoldCosts":[100,400,2000,7000,24000,30000,90000],"StoneCosts":[0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0],"MsBetweenFires":[500,500,500,500,500,500,500],"ChargeTime":[150,150,150,150,150,150,150],"ProjectileVelocity":[100,100,100,100,100,100,100],"ProjectileName":"BowProjectile","ProjectileCollisionRadius":[10,10,10,10,10,10,10],"ProjectileLifetime":[550,550,550,550,550,550,550]},{"Name":"Bomb","Class":"RangedWeapon","GoldCosts":[100,400,3000,5000,24000,30000,90000],"DamageToNeutrals":[50,100,150,200,250,300,500],"StoneCosts":[0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0],"MsBetweenFires":[500,500,500,500,500,500,500],"DamageToZombies":[10,30,80,150,1200,6000,9000],"DamageToBuildings":[1,1,1,1,1,1,1],"DamageToPlayers":[20,22,24,26,28,30,30],"DamageToPets":[1,1,1,1,1,1,1],"ProjectileVelocity":[40,40,40,40,40,40,40],"ProjectileName":"BombProjectile","ProjectileCollisionRadius":[10,10,10,10,10,10,10],"ProjectileLifetime":[700,700,700,700,700,700,700],"ProjectileAoe":[true,true,true,true,true,true,true],"ProjectileAoeRadius":[50,50,50,50,50,50,50],"ProjectileIgnoresCollisions":[false,false,false,false,false,false,false],"ProjectileMaxRange":[700,700,700,700,700,700,700]},{"Name":"HealthPotion","Class":"HealthPotion","GoldCosts":[100],"StoneCosts":[0],"WoodCosts":[0],"TokenCosts":[0],"PurchaseCooldown":15000},{"Name":"ZombieShield","Class":"ZombieShield","GoldCosts":[1000,3000,7000,14000,18000,22000,24000,30000,45000,70000],"StoneCosts":[0,0,0,0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0,0,0,0],"TokenCosts":[0,0,0,0,0,0,0,0,0,0],"Health":[500,1000,1800,4000,10000,20000,35000,50000,65000,85000],"RechargePerSecond":[50,100,200,400,1000,2000,3500,5000,6500,8500],"MsBeforeRecharge":[10000,9000,8000,7000,6000,6000,6000,6000,6000,6000]},{"Name":"Pause","Class":"Pause","GoldCosts":[10000],"StoneCosts":[0],"WoodCosts":[0],"TokenCosts":[0],"PurchaseCooldown":240000},{"Name":"PetMiner","Class":"Pet","GoldCosts":[0,0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0,0],"StoneCosts":[0,0,0,0,0,0,0,0],"TokenCosts":[0,100,100,100,100,200,200,300],"CollisionRadius":25,"Health":[400,800,1500,3000,5000,8000,10000,16000],"MsBeforeRegen":[8000,8000,8000,8000,8000,8000,8000,8000],"HealthRegenPerSecond":[5,5,5,5,5,5,5,5],"Speed":[30,32,34,35,35,37,37,38],"DamageToNeutrals":[80,100,150,200,250,400,500,600],"HarvestCount":[1,1,2,2,3,3,4,4],"Ranged":[false,false,false,false,false,false,false,false],"CanAttackPlayers":[false,false,false,false,false,false,false,false],"CanMine":[true,true,true,true,true,true,true,true],"LeashRange":[500,500,500,500,500,500,500,500],"HarvestLeashRange":[0,0,0,0,0,0,0,0],"AttackRange":[80,80,80,80,80,80,80,80],"MsBetweenFires":[500,450,450,400,400,380,380,350],"EvolvesAtLevel":[0,8,16,24,32,48,64,96],"ExperienceFromMiningPerHalfSecond":[1,1,1,1,1,1,1,1]},{"Name":"PetCARL","Class":"Pet","GoldCosts":[0,0,0,0,0,0,0,0],"WoodCosts":[0,0,0,0,0,0,0,0],"StoneCosts":[0,0,0,0,0,0,0,0],"TokenCosts":[0,100,100,100,100,200,200,300],"CollisionRadius":25,"Health":[400,800,1500,3000,5000,8000,10000,16000],"MsBeforeRegen":[8000,8000,8000,8000,8000,8000,8000,8000],"HealthRegenPerSecond":[5,5,5,5,5,5,5,5],"Speed":[30,32,34,35,35,37,37,38],"DamageToNeutrals":[80,100,150,200,250,400,500,600],"Ranged":[false,false,false,false,false,false,false,false],"CanAttackPlayers":[true,true,true,true,true,true,true,true],"LeashRange":[500,500,500,500,500,500,500,500],"AttackRange":[80,80,80,80,80,80,80,80],"MsBetweenFires":[500,490,490,490,480,480,470,470],"ProjectileLifetime":[1000,1000,1000,1000,1000,1000,1000,1000],"ProjectileVelocity":[60,60,60,60,60,60,60,60],"ProjectileName":"PetCARLProjectile","ProjectileAoe":[true,true,true,true,true,true,true,true],"ProjectileAoeRadius":[250,250,250,250,250,250,250,250],"ProjectileCollisionRadius":[10,10,10,10,10,10,10,10],"DamageToZombies":[30,100,400,600,1000,3000,6000,8000],"DamageToPlayers":[30,31,32,33,34,35,36,37],"DamageToBuildings":[2,2,2,3,3,3,4,4],"EvolvesAtLevel":[0,8,16,24,32,48,64,96],"ExperienceFromZombies":[30,28,25,25,25,25,25,25],"ExperienceFromNeutrals":[30,28,25,25,25,25,25,25]},{"Name":"HatHorns","Class":"Hat","GoldCosts":[0],"WoodCosts":[0],"StoneCosts":[0],"TokenCosts":[0]},{"Name":"PetHealthPotion","Class":"PetHealthPotion","GoldCosts":[100],"StoneCosts":[0],"WoodCosts":[0],"TokenCosts":[0]},{"Name":"PetWhistle","Class":"PetWhistle","GoldCosts":[0],"StoneCosts":[0],"WoodCosts":[0],"TokenCosts":[0]},{"Name":"PetRevive","Class":"PetRevive","GoldCosts":[0],"StoneCosts":[0],"WoodCosts":[0],"TokenCosts":[0]}]'},opcode:9},{name:"Spells",response:{json:'[{"Name":"HealTowersSpell","VisualLifetime":10000,"VisualRadius":600,"Cooldown":[240000],"IsCooldownForParty":true,"Healing":[{"Type":"Tower","Amount":[50],"Over":[10000],"Radius":[600]}],"GoldCosts":[1000],"WoodCosts":[0],"StoneCosts":[0],"TokenCosts":[0]}]'},opcode:9}], codecJSON: '{"attributeMaps":{"667546015":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1},{"name":"lastPetDamage","type":3},{"name":"lastPetDamageTick","type":1},{"name":"lastPetDamageTarget","type":1},{"name":"firingTick","type":1},{"name":"experience","type":1},{"name":"stoneGain","type":3},{"name":"woodGain","type":3},{"name":"stoneGainTick","type":1},{"name":"woodGainTick","type":1}],"742594995":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1}],"1059671174":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"firingTick","type":1},{"name":"lastDamagedTick","type":1}],"1372600389":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"hits","type":8}],"1496910567":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"firingTick","type":1}],"1566069472":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1}],"1672634632":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1}],"1816895259":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1}],"2092990061":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1}],"2093252446":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"hits","type":8}],"2347737811":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"reconnectSecret","type":4},{"name":"name","type":4},{"name":"score","type":13},{"name":"baseSpeed","type":3},{"name":"speedAttribute","type":3},{"name":"availableSkillPoints","type":2},{"name":"experience","type":3},{"name":"level","type":1},{"name":"msBetweenFires","type":3},{"name":"aimingYaw","type":2},{"name":"energy","type":3},{"name":"maxEnergy","type":3},{"name":"energyRegenerationRate","type":3},{"name":"kills","type":2},{"name":"weaponName","type":4},{"name":"weaponTier","type":1},{"name":"firingTick","type":1},{"name":"startChargingTick","type":1},{"name":"stone","type":15},{"name":"wood","type":15},{"name":"gold","type":15},{"name":"token","type":15},{"name":"wave","type":1},{"name":"partyId","type":1},{"name":"zombieShieldHealth","type":3},{"name":"zombieShieldMaxHealth","type":3},{"name":"isPaused","type":1},{"name":"isInvulnerable","type":1},{"name":"lastPetDamage","type":3},{"name":"lastPetDamageTick","type":1},{"name":"lastPetDamageTarget","type":1},{"name":"lastDamage","type":3},{"name":"lastDamageTick","type":1},{"name":"lastDamageTarget","type":1},{"name":"hatName","type":4},{"name":"petUid","type":1},{"name":"isBuildingWalking","type":10}],"2402467733":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1}],"2462472648":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1}],"2464630638":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1}],"2899981078":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1},{"name":"harvestMax","type":1},{"name":"stone","type":1},{"name":"wood","type":1},{"name":"firingTick","type":1},{"name":"deposit","type":3},{"name":"depositMax","type":3},{"name":"lastHarvestedBy","type":4}],"2969697641":[{"name":"position","type":5},{"name":"yaw","type":2},{"name":"health","type":3},{"name":"maxHealth","type":3},{"name":"damage","type":3},{"name":"height","type":3},{"name":"width","type":3},{"name":"collisionRadius","type":1},{"name":"model","type":4},{"name":"entityClass","type":4},{"name":"dead","type":1},{"name":"timeDead","type":3},{"name":"slowed","type":1},{"name":"stunned","type":1},{"name":"tier","type":1},{"name":"partyId","type":1},{"name":"towerYaw","type":3},{"name":"firingTick","type":1},{"name":"healingTick","type":1}]},"entityTypeNames":{"667546015":"Pet","742594995":"GoldMine","1059671174":"Zombie","1372600389":"Stone","1496910567":"Neutral","1566069472":"PlayerObject","1672634632":"NeutralCamp","1816895259":"GameProjectile","2092990061":"Trap","2093252446":"Tree","2347737811":"GamePlayer","2402467733":"GoldStash","2462472648":"Spell","2464630638":"Door","2899981078":"Harvester","2969697641":"Tower"},"rpcMaps":[{"name":"Shutdown","parameters":[{"name":"reason","type":3},{"name":"shutdownUnix","type":0}],"isArray":false,"index":0},{"name":"ReceiveChatMessage","parameters":[{"name":"displayName","type":3},{"name":"channel","type":3},{"name":"message","type":3},{"name":"uid","type":0}],"isArray":false,"index":1},{"name":"SendChatMessage","parameters":[{"name":"channel","type":3},{"name":"message","type":3}],"isArray":false,"index":2},{"name":"Login","parameters":[{"name":"token","type":3}],"isArray":false,"index":3},{"name":"LoginResponse","parameters":[{"name":"json","type":3}],"isArray":false,"index":4},{"name":"AccountSession","parameters":[{"name":"json","type":3}],"isArray":false,"index":5},{"name":"Metrics","parameters":[{"name":"minFps","type":2},{"name":"maxFps","type":2},{"name":"currentFps","type":2},{"name":"averageFps","type":2},{"name":"framesRendered","type":2},{"name":"framesInterpolated","type":2},{"name":"framesExtrapolated","type":2},{"name":"allocatedNetworkEntities","type":2},{"name":"currentClientLag","type":2},{"name":"minClientLag","type":2},{"name":"maxClientLag","type":2},{"name":"currentPing","type":2},{"name":"minPing","type":2},{"name":"maxPing","type":2},{"name":"averagePing","type":2},{"name":"longFrames","type":2},{"name":"stutters","type":2},{"name":"group","type":0},{"name":"isMobile","type":0},{"name":"timeResets","type":2},{"name":"maxExtrapolationTime","type":2},{"name":"extrapolationIncidents","type":2},{"name":"totalExtrapolationTime","type":2},{"name":"differenceInClientTime","type":2}],"isArray":false,"index":6},{"name":"DayCycle","parameters":[{"name":"cycleStartTick","type":0},{"name":"nightEndTick","type":0},{"name":"dayEndTick","type":0},{"name":"isDay","type":0}],"isArray":false,"index":7},{"name":"MakeBuilding","parameters":[{"name":"x","type":1},{"name":"y","type":1},{"name":"type","type":3},{"name":"yaw","type":1}],"isArray":false,"index":8},{"name":"BuildingShopPrices","parameters":[{"name":"json","type":3}],"isArray":false,"index":9},{"name":"ItemShopPrices","parameters":[{"name":"json","type":3},{"name":"json","type":3}],"isArray":false,"index":10},{"name":"LocalBuilding","parameters":[{"name":"x","type":1},{"name":"y","type":1},{"name":"type","type":3},{"name":"dead","type":0},{"name":"uid","type":0},{"name":"tier","type":0}],"isArray":true,"index":11},{"name":"Dead","parameters":[{"name":"stashDied","type":0}],"isArray":false,"index":12},{"name":"Admin","parameters":[{"name":"password","type":3},{"name":"command","type":3}],"isArray":false,"index":13},{"name":"UpgradeBuilding","parameters":[{"name":"uid","type":0}],"isArray":false,"index":14},{"name":"DeleteBuilding","parameters":[{"name":"uid","type":0}],"isArray":false,"index":15},{"name":"BuyItem","parameters":[{"name":"itemName","type":3},{"name":"tier","type":0}],"isArray":false,"index":16},{"name":"SetItem","parameters":[{"name":"itemName","type":3},{"name":"tier","type":0},{"name":"stacks","type":0}],"isArray":false,"index":17},{"name":"EquipItem","parameters":[{"name":"itemName","type":3},{"name":"tier","type":0}],"isArray":false,"index":18},{"name":"SetOpenParty","parameters":[{"name":"isOpen","type":0}],"isArray":false,"index":19},{"name":"SetPartyName","parameters":[{"name":"partyName","type":3}],"isArray":false,"index":20},{"name":"SetPartyMemberCanSell","parameters":[{"name":"uid","type":0},{"name":"canSell","type":0}],"isArray":false,"index":21},{"name":"JoinParty","parameters":[{"name":"partyId","type":0}],"isArray":false,"index":22},{"name":"JoinPartyByShareKey","parameters":[{"name":"partyShareKey","type":3}],"isArray":false,"index":23},{"name":"PartyApplicant","parameters":[{"name":"displayName","type":3},{"name":"applicantUid","type":0}],"isArray":false,"index":24},{"name":"PartyApplicantDecide","parameters":[{"name":"applicantUid","type":0},{"name":"accepted","type":0}],"isArray":false,"index":25},{"name":"PartyApplicantDenied","parameters":[],"isArray":false,"index":26},{"name":"PartyApplicantExpired","parameters":[{"name":"applicantUid","type":0}],"isArray":false,"index":27},{"name":"PartyShareKey","parameters":[{"name":"partyShareKey","type":3}],"isArray":false,"index":28},{"name":"PartyInfo","parameters":[{"name":"playerUid","type":0},{"name":"displayName","type":3},{"name":"isLeader","type":0},{"name":"canSell","type":0}],"isArray":true,"index":29},{"name":"AddParty","parameters":[{"name":"partyId","type":0},{"name":"partyName","type":3},{"name":"isOpen","type":0},{"name":"memberCount","type":0}],"isArray":false,"index":30},{"name":"RemoveParty","parameters":[{"name":"partyId","type":0}],"isArray":false,"index":31},{"name":"Leaderboard","parameters":[{"name":"name","type":3},{"name":"uid","type":0},{"name":"rank","type":0},{"name":"score","type":4},{"name":"wave","type":0}],"isArray":true,"index":32},{"name":"Failure","parameters":[{"name":"category","type":3},{"name":"reason","type":3},{"name":"x","type":0},{"name":"y","type":0},{"name":"type","type":3}],"isArray":false,"index":33},{"name":"RecallPet","parameters":[],"isArray":false,"index":34},{"name":"LeaveParty","parameters":[],"isArray":false,"index":35},{"name":"KickParty","parameters":[{"name":"uid","type":0}],"isArray":false,"index":36},{"name":"AddDepositToHarvester","parameters":[{"name":"uid","type":0},{"name":"deposit","type":2}],"isArray":false,"index":37},{"name":"CollectHarvester","parameters":[{"name":"uid","type":0}],"isArray":false,"index":38},{"name":"CastSpell","parameters":[{"name":"spell","type":3},{"name":"x","type":1},{"name":"y","type":1},{"name":"tier","type":0}],"isArray":false,"index":39},{"name":"CastSpellResponse","parameters":[{"name":"spell","type":3},{"name":"cooldown","type":0},{"name":"cooldownStartTick","type":0}],"isArray":false,"index":40},{"name":"Spells","parameters":[{"name":"json","type":3}],"isArray":false,"index":41},{"name":"SetPartyList","parameters":[{"name":"partyId","type":0},{"name":"partyName","type":3},{"name":"isOpen","type":0},{"name":"memberCount","type":0}],"isArray":true,"index":42}],"rpcMapsByName":{"Shutdown":{"name":"Shutdown","parameters":[{"name":"reason","type":3},{"name":"shutdownUnix","type":0}],"isArray":false,"index":0},"ReceiveChatMessage":{"name":"ReceiveChatMessage","parameters":[{"name":"displayName","type":3},{"name":"channel","type":3},{"name":"message","type":3},{"name":"uid","type":0}],"isArray":false,"index":1},"SendChatMessage":{"name":"SendChatMessage","parameters":[{"name":"channel","type":3},{"name":"message","type":3}],"isArray":false,"index":2},"Login":{"name":"Login","parameters":[{"name":"token","type":3}],"isArray":false,"index":3},"LoginResponse":{"name":"LoginResponse","parameters":[{"name":"json","type":3}],"isArray":false,"index":4},"AccountSession":{"name":"AccountSession","parameters":[{"name":"json","type":3}],"isArray":false,"index":5},"Metrics":{"name":"Metrics","parameters":[{"name":"minFps","type":2},{"name":"maxFps","type":2},{"name":"currentFps","type":2},{"name":"averageFps","type":2},{"name":"framesRendered","type":2},{"name":"framesInterpolated","type":2},{"name":"framesExtrapolated","type":2},{"name":"allocatedNetworkEntities","type":2},{"name":"currentClientLag","type":2},{"name":"minClientLag","type":2},{"name":"maxClientLag","type":2},{"name":"currentPing","type":2},{"name":"minPing","type":2},{"name":"maxPing","type":2},{"name":"averagePing","type":2},{"name":"longFrames","type":2},{"name":"stutters","type":2},{"name":"group","type":0},{"name":"isMobile","type":0},{"name":"timeResets","type":2},{"name":"maxExtrapolationTime","type":2},{"name":"extrapolationIncidents","type":2},{"name":"totalExtrapolationTime","type":2},{"name":"differenceInClientTime","type":2}],"isArray":false,"index":6},"DayCycle":{"name":"DayCycle","parameters":[{"name":"cycleStartTick","type":0},{"name":"nightEndTick","type":0},{"name":"dayEndTick","type":0},{"name":"isDay","type":0}],"isArray":false,"index":7},"MakeBuilding":{"name":"MakeBuilding","parameters":[{"name":"x","type":1},{"name":"y","type":1},{"name":"type","type":3},{"name":"yaw","type":1}],"isArray":false,"index":8},"BuildingShopPrices":{"name":"BuildingShopPrices","parameters":[{"name":"json","type":3}],"isArray":false,"index":9},"ItemShopPrices":{"name":"ItemShopPrices","parameters":[{"name":"json","type":3},{"name":"json","type":3}],"isArray":false,"index":10},"LocalBuilding":{"name":"LocalBuilding","parameters":[{"name":"x","type":1},{"name":"y","type":1},{"name":"type","type":3},{"name":"dead","type":0},{"name":"uid","type":0},{"name":"tier","type":0}],"isArray":true,"index":11},"Dead":{"name":"Dead","parameters":[{"name":"stashDied","type":0}],"isArray":false,"index":12},"Admin":{"name":"Admin","parameters":[{"name":"password","type":3},{"name":"command","type":3}],"isArray":false,"index":13},"UpgradeBuilding":{"name":"UpgradeBuilding","parameters":[{"name":"uid","type":0}],"isArray":false,"index":14},"DeleteBuilding":{"name":"DeleteBuilding","parameters":[{"name":"uid","type":0}],"isArray":false,"index":15},"BuyItem":{"name":"BuyItem","parameters":[{"name":"itemName","type":3},{"name":"tier","type":0}],"isArray":false,"index":16},"SetItem":{"name":"SetItem","parameters":[{"name":"itemName","type":3},{"name":"tier","type":0},{"name":"stacks","type":0}],"isArray":false,"index":17},"EquipItem":{"name":"EquipItem","parameters":[{"name":"itemName","type":3},{"name":"tier","type":0}],"isArray":false,"index":18},"SetOpenParty":{"name":"SetOpenParty","parameters":[{"name":"isOpen","type":0}],"isArray":false,"index":19},"SetPartyName":{"name":"SetPartyName","parameters":[{"name":"partyName","type":3}],"isArray":false,"index":20},"SetPartyMemberCanSell":{"name":"SetPartyMemberCanSell","parameters":[{"name":"uid","type":0},{"name":"canSell","type":0}],"isArray":false,"index":21},"JoinParty":{"name":"JoinParty","parameters":[{"name":"partyId","type":0}],"isArray":false,"index":22},"JoinPartyByShareKey":{"name":"JoinPartyByShareKey","parameters":[{"name":"partyShareKey","type":3}],"isArray":false,"index":23},"PartyApplicant":{"name":"PartyApplicant","parameters":[{"name":"displayName","type":3},{"name":"applicantUid","type":0}],"isArray":false,"index":24},"PartyApplicantDecide":{"name":"PartyApplicantDecide","parameters":[{"name":"applicantUid","type":0},{"name":"accepted","type":0}],"isArray":false,"index":25},"PartyApplicantDenied":{"name":"PartyApplicantDenied","parameters":[],"isArray":false,"index":26},"PartyApplicantExpired":{"name":"PartyApplicantExpired","parameters":[{"name":"applicantUid","type":0}],"isArray":false,"index":27},"PartyShareKey":{"name":"PartyShareKey","parameters":[{"name":"partyShareKey","type":3}],"isArray":false,"index":28},"PartyInfo":{"name":"PartyInfo","parameters":[{"name":"playerUid","type":0},{"name":"displayName","type":3},{"name":"isLeader","type":0},{"name":"canSell","type":0}],"isArray":true,"index":29},"AddParty":{"name":"AddParty","parameters":[{"name":"partyId","type":0},{"name":"partyName","type":3},{"name":"isOpen","type":0},{"name":"memberCount","type":0}],"isArray":false,"index":30},"RemoveParty":{"name":"RemoveParty","parameters":[{"name":"partyId","type":0}],"isArray":false,"index":31},"Leaderboard":{"name":"Leaderboard","parameters":[{"name":"name","type":3},{"name":"uid","type":0},{"name":"rank","type":0},{"name":"score","type":4},{"name":"wave","type":0}],"isArray":true,"index":32},"Failure":{"name":"Failure","parameters":[{"name":"category","type":3},{"name":"reason","type":3},{"name":"x","type":0},{"name":"y","type":0},{"name":"type","type":3}],"isArray":false,"index":33},"RecallPet":{"name":"RecallPet","parameters":[],"isArray":false,"index":34},"LeaveParty":{"name":"LeaveParty","parameters":[],"isArray":false,"index":35},"KickParty":{"name":"KickParty","parameters":[{"name":"uid","type":0}],"isArray":false,"index":36},"AddDepositToHarvester":{"name":"AddDepositToHarvester","parameters":[{"name":"uid","type":0},{"name":"deposit","type":2}],"isArray":false,"index":37},"CollectHarvester":{"name":"CollectHarvester","parameters":[{"name":"uid","type":0}],"isArray":false,"index":38},"CastSpell":{"name":"CastSpell","parameters":[{"name":"spell","type":3},{"name":"x","type":1},{"name":"y","type":1},{"name":"tier","type":0}],"isArray":false,"index":39},"CastSpellResponse":{"name":"CastSpellResponse","parameters":[{"name":"spell","type":3},{"name":"cooldown","type":0},{"name":"cooldownStartTick","type":0}],"isArray":false,"index":40},"Spells":{"name":"Spells","parameters":[{"name":"json","type":3}],"isArray":false,"index":41},"SetPartyList":{"name":"SetPartyList","parameters":[{"name":"partyId","type":0},{"name":"partyName","type":3},{"name":"isOpen","type":0},{"name":"memberCount","type":0}],"isArray":true,"index":42}}}', useSes: false, currentSesId: null, allSessions: null, endpoints: { localhost: { secret: 'f07cbf563d19619ba4afe3ae1e2ec95710a72b3e', https: false, wss: "localhost:727", api: "localhost:728", }, }, nameInput: getClass("hud-intro-name")[0], serverSelect: getClass("hud-intro-server")[0], overlayElem: getId("hud-intro-overlay"), sesSelect: document.createElement("select"), delBtn: document.createElement("button"), addBtn: document.createElement("button"), init: async function() { this.addBtn.id = "addBtn"; this.addBtn.innerText = "Add..."; document.querySelector("#hud-intro > div.hud-intro-wrapper > div > div").insertAdjacentElement("afterbegin", this.addBtn); this.delBtn.id = "delBtn"; this.delBtn.innerText = "Delete"; document.querySelector("#hud-intro > div.hud-intro-wrapper > div > div").insertAdjacentElement("afterbegin", this.delBtn); this.sesSelect.disabled = true; this.sesSelect.id = "sesSelect"; document.querySelector("#hud-intro > div.hud-intro-wrapper > div > div").insertAdjacentElement("afterbegin", this.sesSelect); getId("hud-intro-overlay").insertAdjacentHTML("beforeend", `

    Which one do wish to add?

    Endpoint info

    Nickname

    Server URL

    API URL

    Secret key

    Protocol

    Session creation info

    You can leave the Party share key field empty.

    Endpoint

    Username

    Server

    Party share key

    `); getId("useSes").onchange = () => { this.useSes = getId("useSes").checked; if (this.useSes) { this.sesSelect.style.display = "block"; this.delBtn.style.display = "inline-block"; this.addBtn.style.display = "inline-block"; this.nameInput.style.display = "none"; this.serverSelect.style.display = "none"; document.documentElement.style.setProperty('--normal-btn', 'rgb(158 74 208)'); document.documentElement.style.setProperty('--light-hover-btn', 'rgb(213 118 211)'); document.querySelector("#intro-animation > img").src = "https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/luna.png?v=1720346619482"; document.querySelector("#intro-animation > img").style.left = "calc(15vw - 600px)"; document.querySelector("#hud-intro").setAttribute("style", "--bg-image: url('https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/Eden%20Conflict.webp?v=1717298976083');"); } else { this.sesSelect.style.display = "none"; this.delBtn.style.display = "none"; this.addBtn.style.display = "none"; this.nameInput.style.display = "block"; this.serverSelect.style.display = "block"; document.documentElement.style.setProperty('--normal-btn', 'rgb(40 152 231)'); document.documentElement.style.setProperty('--light-hover-btn', 'rgb(111 208 247)'); document.querySelector("#intro-animation > img").src = "https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/eto_large.png?v=1717285769725"; document.querySelector("#intro-animation > img").style.left = "calc(15vw - 300px)"; document.querySelector("#hud-intro").setAttribute("style", "--bg-image: url('https://cdn.glitch.global/ba7f4151-2a49-416a-985b-56301606ae3d/BG_eden_append_light.webp?v=1717285760533');"); }; }; this.delBtn.onclick = () => { const [endpoint, sessionId] = this.sesSelect.value.split("/"); this.deleteSes(endpoint, sessionId); }; this.addBtn.onclick = () => { getId("hud-intro").style.display = "block"; game.ui.components.Intro.hideLoadingScreen(); this.overlayElem.style.display = "flex"; this.overlayElem.style.opacity = 1; getId("select-session-add").style.display = "flex"; getId("endpoint-add-menu").style.display = "none"; getId("session-add-menu").style.display = "none"; }; getId("add-endpoint").onclick = () => { getId("select-session-add").style.display = "none"; getId("endpoint-add-menu").style.display = "flex"; getId("session-add-menu").style.display = "none"; }; getId("add-session").onclick = () => { getId("select-session-add").style.display = "none"; getId("endpoint-add-menu").style.display = "none"; getId("session-add-menu").style.display = "flex"; }; for (const backElem of getClass("session-back")) backElem.onclick = () => { this.addBtn.click(); }; getId("create-endpoint").onclick = () => this.addNewEndpoint( getId("endpoint-name").value, getId("endpoint-server").value, getId("endpoint-api").value, getId("endpoint-protocol").value, getId("endpoint-secret").value, ); getId("create-session").onclick = () => this.createNewSession( getId("session-nickname").value, getId("session-server").value, getId("session-psk").value, getId("session-endpoint").value, ); game.network.establishSessionConnection = function() { const [endpoint, sessionId] = game.script.sessions.sesSelect.value.split("/"); this.connectionOptions = game.script.sessions.allSessions[endpoint][sessionId].connectionOptions; this.connected = false; this.connecting = true; this.codec.rpcMaps = [{ "name": "SyncData", "parameters": [{ "name": "json", "type": 3 }], "isArray": false, "index": 0 }, { "name": "VerifyUser", "parameters": [{ "name": "secretKey", "type": 3, }], "isArray": false, "index": 1 }, { "name": "ConnectSession", "parameters": [{ "name": "id", "type": 3 }], "isArray": false, "index": 2 }]; this.codec.rpcMapsByName = { "SyncData": { "name": "SyncData", "parameters": [{ "name": "json", "type": 3 }], "isArray": false, "index": 0 }, "VerifyUser": { "name": "VerifyUser", "parameters": [{ "name": "secretKey", "type": 3 }], "isArray": false, "index": 1 }, "ConnectSession": { "name": "ConnectSession", "parameters": [{ "name": "id", "type": 3 }], "isArray": false, "index": 2 }, }; this.socket = new WebSocket(`ws${game.script.sessions.endpoints[endpoint].https ? "s" : ""}://${game.script.sessions.endpoints[endpoint].wss}`); this.socket.binaryType = `arraybuffer`; this.socket.addEventListener("open", () => { this.socket.send(this.codec.encode(9, { name: "VerifyUser", secretKey: game.script.sessions.endpoints[endpoint].secret })); this.socket.send(this.codec.encode(9, { name: "ConnectSession", id: sessionId, })); }); this.bindEventListeners(); this.addRpcHandler("SyncData", (response) => { try { const data = JSON.parse(response.json); this.connectionOptions = data.connectionOptions; game.options.serverId = data.connectionOptions.id; game.options.nickname = data.syncNeeds[0].effectiveDisplayName; const staticCodecData = JSON.parse(game.script.sessions.codecJSON); for (let i in staticCodecData) { this.codec[i] = staticCodecData[i]; }; this.codec.sortedUidsByType = data.sortedUidsByType; this.codec.removedEntities = data.removedEntities; this.codec.absentEntitiesFlags = data.absentEntitiesFlags; this.codec.updatedEntityFlags = data.updatedEntityFlags; for (let i = 0; i < game.script.sessions.staticJSONs.length; i++) { this.emitter.emit(PacketIds_1.default[game.script.sessions.staticJSONs[i].opcode], game.script.sessions.staticJSONs[i]); }; for (let i = 0; i < data.syncNeeds.length; i++) { this.emitter.emit(PacketIds_1.default[data.syncNeeds[i].opcode], data.syncNeeds[i]); }; for (let i = 0; i < data.messages.length; i++) { this.emitter.emit(PacketIds_1.default[9], { name: "ReceiveChatMessage", response: data.messages[i], opcode: 9 }); }; if (data.castSpellResponse && data.castSpellResponse.cooldownStartTick && (data.tick - data.castSpellResponse.cooldownStartTick) * 50 < 240000) { this.emitter.emit(PacketIds_1.default[9], { name: 'CastSpellResponse', response: data.castSpellResponse, opcode: 9 }); }; for (let i in data.inventory) { this.emitter.emit(PacketIds_1.default[9], { name: "SetItem", response: { itemName: data.inventory[i].itemName, tier: data.inventory[i].tier, stacks: data.inventory[i].stacks }, opcode: 9 }); }; this.emitter.emit(PacketIds_1.default[9], { name: "LocalBuilding", response: data.localBuildings, opcode: 9 }); this.emitter.emit(PacketIds_1.default[0], { tick: data.tick, entities: data.entities, byteSize: data.byteSize, opcode: 0 }); this.emitter.once(PacketIds_1.default[0], () => { const myPlayer = data.entities[data.syncNeeds[0].uid]; myPlayer?.dead && this.emitter.emit(PacketIds_1.default[9], { name: "Dead", response: {stashDied: 0}, opcode: 9 }); myPlayer?.isPaused && ( game.ui.onLocalItemUpdate({ itemName: 'Pause', tier: 1, stacks: 1 }), game.ui.emit('wavePaused') ); }); } catch(e) { console.log(e); }; }); }; game.network.connect = async function (options) { if (!this.connecting) { if (game.script.sessions.useSes) return this.establishSessionConnection(); this.connectionOptions = options; this.connected = false; this.connecting = true; this.socket = new WebSocket('wss://' + options.hostname + ':' + options.port); this.socket.binaryType = `arraybuffer`; this.bindEventListeners(); }; }; game.network.reconnect = function () { return this.connect(this.connectionOptions); }; await this.fetchSessions(); }, fetchSessions: async function() { for (const endpointName in this.endpoints) { const endpointData = this.endpoints[endpointName]; try { let data = await fetch( `http${endpointData.https ? "s" : ""}://${endpointData.api}/sessions`, { signal: AbortSignal.timeout(this.SESSION_FETCH_TIMEOUT), method: "get", headers: new Headers({ "ngrok-skip-browser-warning": "69420", }), } ); data = await data.json(); this.allSessions ||= {}; this.sesSelect.disabled = false; this.allSessions[endpointName] = data; } catch(e) { console.warn(`Failed to fetch available sessions for ${endpointName}: ` + e); }; }; this.mapSessions(); }, mapSessions() { if (this.allSessions === null) return; let sessionHTML = ''; for (const endpointName in this.allSessions) { sessionHTML += ` ${Object.entries(this.allSessions[endpointName]).map(([key, data]) => { const { connectionOptions: { id }, name } = data; return ``; }).join("\n")} `; }; this.sesSelect.innerHTML = sessionHTML; getId("session-endpoint").innerHTML = Object.keys(this.endpoints).map((name) => { return ``; }).join("\n"); }, addNewEndpoint: async function(name, server, api, protocol, secret) { game.ui.components.Intro.showLoadingScreen(); if (name && secret && server && api && protocol) { this.endpoints[name] = {https: protocol == "https", secret, wss: server, api}; await this.fetchSessions(); } else game.ui.components.Intro.onConnectionError("Please fill in ALL necessary details to add a new endpoint."); game.ui.components.Intro.hideLoadingScreen(); game.world.isInitialized && (getId("hud-intro").style.display = "none"); }, createNewSession: async function(nickname, server, psk = "", endpoint) { game.ui.components.Intro.showLoadingScreen(); try { const {https, api} = this.endpoints[endpoint]; console.log(nickname, server, psk, endpoint); let data = await fetch( `http${https ? "s" : ""}://${api}/create?name=${nickname}&serverId=${server}&psk=${psk}`, { signal: AbortSignal.timeout(this.SESSION_CREATE_TIMEOUT), method: "get", headers: new Headers({ "ngrok-skip-browser-warning": "69420", }), } ); data = await data.json(); this.allSessions ||= {}; this.sesSelect.disabled = false; this.allSessions[endpoint] = data.data; this.mapSessions(); this.sesSelect.value = `${endpoint}/${data.createdSession}`; } catch(e) { console.log(e); game.ui.components.Intro.onConnectionError(e); }; game.ui.components.Intro.hideLoadingScreen(); game.world.isInitialized && (getId("hud-intro").style.display = "none"); }, deleteSes: async function(endpoint, sesId) { const { api, https } = this.endpoints[endpoint]; await fetch( `http${https ? "s" : ""}://${api}/delete?sessionId=${sesId}`, { signal: AbortSignal.timeout(this.SESSION_CREATE_TIMEOUT) } ); setTimeout(this.fetchSessions.bind(this), 500); }, }, sockets: { statusEnum: { "Population Full": "red", "Failed": "red", "Closed": "red", "Connecting": "yellow", "Open": "green" }, shared: { all: {}, uiHooks: {}, availableNames: shuffleArray([ "Hikari", "Tairitsu", "Kou", "Sapphire", "Lethe", "Tairitsu (Axium)", "Tairitsu (Grievous Lady)", "Stella", "Hikari & Fisica", "Ilith", "Eto", "Luna", "Shirabe", "Hikari (Zero)", "Hikari (Fracture)", "Hikari (Summer)", "Tairitsu (Summer)", "Tairitsu & Trin", "Ayu", "Eto & Luna (Winter)", "Hikari & Seine", "Yume", "Saya", "Tairitsu (Grievous Lady) & Chuni Penguin", "Nono Shibusawa", "Haruna Mishima", "Regulus (MDA-21)", "Pandora Nemesis (MTA-XXX)", "Chuni Penguin", "Kanae", "Hikari (Fantasia)", "Tairitsu (Sonata)", "Sia", "DORO*C", "Tairitsu (Tempest)", "Brillante (E/S Primera)", "Ilith (Summer)", "Saya (Etude)", "Alice & Tenniel", "Luna & Mia", "Areus", "Seele Haze", "Isabelle Yagrush", "Mir", "Lagrange", "Shirahime", "Linka", "Nami", "Saya & Elizabeth", "Lily", "Kanae (Midsummer)", "Alice & Tenniel (Minuet)", "Tairitsu (Elegy)", "Marija", "Vita", "Hikari (Fatalis)", "Hikari & Tairitsu (Reunion)", "Saki", "Setsuna", "Amane", "Kou (Winter)", "Lethe (Apophenia)", "Lagrange (Aria)", "Milk (UNiVERSE)", "Shama (UNiVERSE)", "Mika Yurisaki", "Shikoku", "Toa Kozukata", "Mithra Tercera", "Nami (Twilight)", "Ilith & Ivy", "Hikari & Vanessa", "Maya", "Luin", "Vita (Cadenza)", "Ai-chan", "Luna & Ilot", "Eto & Hoppe", "Nell", "Lacrymira", "Tsumugi", "Chinatsu", ]), options: { control: { enabled: true, }, autoFill: { enabled: false, onCallback: () => { game.network.emitter.emit("PACKET_RPC", { name: "SetPartyList", response: Object.values(game.ui.parties), opcode: 9 }); }, }, randomizeName: { enabled: true, }, }, }, init: function() { game.network.addRpcHandler("SetPartyList", (parties) => { if (this.shared.options.autoFill.enabled) { let serverPopulation = 0; for (let party of parties) { serverPopulation += party.memberCount; }; const emptyAmount = 32 - serverPopulation; if (emptyAmount > 0) { let connectedSockets = 0; for (const socket of Object.values(this.shared.all)) { if (socket.hasEnteredWorld && socket.ws.readyState === 1) connectedSockets++; }; const shouldWait = ((connectedSockets + 1) % 7) === 0; shouldWait && game.ui.components.PopupOverlay.showHint("Please change your IP Address for the auto-filling to continue."); this.createSocket("Filler"); }; }; }); }, updateActiveSocketsAmount: function() { let openSockets = 0; const allSockets = Object.values(this.shared.all); for (let i = 0; i < allSockets.length; i++) { allSockets[i].ws.readyState == 1 && openSockets++; }; getId("clone-amount").innerText = openSockets + " active"; }, addUiHook: function(UUID, elemSelector, event, callback) { this.shared.uiHooks[elemSelector] ||= {}; if (!this.shared.uiHooks[elemSelector][event]) { this.shared.uiHooks[elemSelector][event] = {}; document.querySelector(elemSelector)["on" + event] = (e) => { for (let uuid in this.shared.uiHooks[elemSelector][event]) { this.shared.uiHooks[elemSelector][event][uuid](); }; }; }; this.shared.uiHooks[elemSelector][event][UUID] = callback; }, removeUiHook: function(UUID, elemSelector, event) { delete this.shared.uiHooks[elemSelector][event][UUID]; }, Socket: class Socket { constructor(url, identifiers) { this.ws = new WebSocket(url); this.ws.binaryType = "arraybuffer"; this.identifiers = identifiers; this.player = { uid: null, partyShareKey: null, targetTick: {}, petTick: {}, // entities: {}, inventory: {}, buildings: {}, parties: {}, hasHealed: false, lastTickHealth: 100, }; this.hasEnteredWorld = false; this.parent = game.script.sockets; this.pingStart = null; this.pingCompletion = null; this.ping = 0; this.keybinds = { keyup: {}, keydown: {}, mousedown: {}, mouseup: {}, mousemove: () => {}, }; /* this.msElapsedSinceInputSent = 0; this.currentInput = {}; this.shouldInput = false; */ this.statusDisplay = getId(`alt${this.identifiers.id}`) || document.createElement("p"); this.statusDisplay.style.position = "relative"; this.statusDisplay.style.width = "100%"; this.minimapDisplay = document.createElement('div'); this.timeCreation = Date.now(); this.init(this.identifiers.type); }; init(type) { this.ws.addEventListener("open", () => { this.updateStatus("Connecting"); this.codec = new BinCodec(); this.resetListeners(type); this.bindKeys(); }); this.ws.addEventListener("message", this.onMessage.bind(this)); this.ws.addEventListener("close", this.onClose.bind(this)); this.ws.addEventListener("error", this.onError.bind(this)); // game.renderer.addTickCallback(this.onRendererTick.bind(this)); }; decodeData(data) { this.data = {}; const m = new Uint8Array(data); this.data.opcode = m[0]; switch(m[0]) { case 5: wasmModule(e => { this.sendPacket(4, { displayName: this.identifiers.name, extra: e[5].extra }); this.wasmModule = e; }, m, game.network.connectionOptions.ipAddress); break; case 10: this.sendPacket(10, {extra: codec.decode(data, this.wasmModule[10]).extra}); break; default: this.data = this.codec.decode(data); }; if (m[0] == 4 && this.data.allowed) { this.ws.send(this.wasmModule[6]); this.hasEnteredWorld = true; }; }; onError() { game.ui.components.PopupOverlay.showHint("Socket failed to connect. Maybe switch your IP?"); }; onClose() { this.parent.updateActiveSocketsAmount(); this.updateStatus("Closed"); this.minimapDisplay.remove(); this.wasmModule = null; this.identifiers.name != game.ui.playerTick.name && this.parent.shared.availableNames.push(this.identifiers.name); if (this.parent.shared.options.autoFill.enabled) { setTimeout(() => { game.network.emitter.emit("PACKET_RPC", { name: "SetPartyList", response: Object.values(game.ui.parties), opcode: 9 }); }, 1000); }; }; onMessage({data}) { this.sendPingIfNecessary(); this.decodeData(data); this.emitter.emit(PacketIds_1.default[this.data.opcode], this.data); }; /* onRendererTick(delta) { this.msElapsedSinceInputSent += delta; this.sendInputKeys(); }; */ addOnceHandler(opcode, callback) { this.emitter.once(PacketIds_1.default[opcode], callback.bind(this)); }; addPacketHandler(opcode, callback) { this.emitter.on(PacketIds_1.default[opcode], callback.bind(this)); }; sendPacket(e, t) { const enc = this.codec.encode(e, t); this.ws.readyState == 1 && this.ws.send(enc); }; sendInput(t) { this.sendPacket(3, t); }; sendRpc(t) { this.sendPacket(9, t); }; sendPingIfNecessary() { var pingInProgress = (this.pingStart != null); if (pingInProgress) { return; } if (this.pingCompletion != null) { var msSinceLastPing = (new Date().getTime() - this.pingCompletion.getTime()); if (msSinceLastPing <= 5000) { return; } } this.pingStart = new Date(); this.sendPacket(7, { nonce: 0 }); }; /* scheduleInput(data) { this.currentInput = data; this.shouldInput = true; this.sendInputKeys(); }; sendInputKeys() { const msPerTick = game.world.msPerTick; if (!(this.msElapsedSinceInputSent < msPerTick)) { if (this.shouldInput) { this.sendInput(this.currentInput); this.currentInput = {}; this.shouldInput = false; }; }; }; */ resetListeners(type) { this.keybinds = { keyup: {}, keydown: {}, mousedown: {}, mouseup: {}, mousemove: () => {}, }; this.emitter = new EventEmitter(); this.bindDefaultListeners(); this.bindTypeHandlers(type); }; bindDefaultListeners() { this.addPacketHandler(0, (data) => { try { if (data.entities[this.player?.uid].name) { this.player.targetTick = data.entities[this.player.uid]; }; for (let g in this.player.targetTick) { if (this.player.targetTick[g] !== data.entities[this.player.uid][g] && data.entities[this.player.uid][g] !== undefined) { this.player.targetTick[g] = data.entities[this.player.uid][g]; }; }; if (this.player.targetTick.petUid) { if (data.entities?.[this.player.targetTick.petUid]?.model) { this.player.petTick = data.entities[this.player.targetTick.petUid]; } for (let g in this.player.petTick) { if (this.player.petTick[g] !== data.entities?.[this.player.targetTick.petUid][g] && data.entities?.[this.player.targetTick.petUid][g] !== undefined) { this.player.petTick[g] = data.entities[this.player.targetTick.petUid][g]; }; }; }; for (let i in data.entities) { if (["Tree", "Stone", "NeutralCamp"].indexOf(data.entities[i].model) > -1) { game.world.createEntity(data.entities[i]); }; }; } catch {}; }); this.addPacketHandler(4, (e) => { if (e.allowed) { this.player.uid = e.uid; this.parent.updateActiveSocketsAmount(); this.updateStatus("Open"); console.log(`%c [SOCKET ${this.identifiers.id}]`, 'color: #54ebd9', '\n', `${e.players + 1}/32 players`); this.sendRpc({name: "BuyItem", itemName: "PetCARL", tier: 1}); this.sendRpc({name: "BuyItem", itemName: "PetMiner", tier: 1}); this.sendInput({left: 1, up: 1}); this.sendInput({space: 1}); } else { console.log(`%c [SOCKET ${this.identifiers.id}]`, 'color: #54ebd9', '\n', `32/32 players`); this.updateStatus("Population Full"); }; }); this.addPacketHandler(5, () => { setTimeout(() => { if (this.data.opcode === 5) this.ws.close(); }, 5000); }); this.addPacketHandler(7, () => { const now = new Date(); this.ping = (now.getTime() - this.pingStart.getTime()) / 2; this.pingStart = null; this.pingCompletion = now; }); this.addPacketHandler(9, (e) => { this.updateStatus("Open"); switch(e.name) { case "PartyShareKey": if (!this.player.partyShareKey) { this.sendRpc({ name: "JoinPartyByShareKey", partyShareKey: game.ui.getPlayerPartyShareKey() }); this.minimapDisplay.classList.add('hud-map-player'); this.minimapDisplay.style.display = "block"; getClass('hud-map')[0].appendChild(this.minimapDisplay); }; this.player.partyShareKey = e.response.partyShareKey; this.minimapDisplay.style.display = (e.response.partyShareKey == game.ui.getPlayerPartyShareKey()) ? "none" : "block"; break; case "SetItem": this.player.inventory[e.response.itemName] = e.response; if (!this.player.inventory[e.response.itemName].stacks) { delete this.player.inventory[e.response.itemName]; }; break; case "Leaderboard": this.sendRpc(game.metrics.metrics); this.minimapDisplay.style.left = (Math.round(this.player?.targetTick?.position?.x) / 24000 * 100) + '%'; this.minimapDisplay.style.top = (Math.round(this.player?.targetTick?.position?.y) / 24000 * 100) + '%'; break; }; }); }; bindKeys() { for (let event in this.keybinds) { switch(event) { case "keyup": case "keydown": document.addEventListener(event, (e) => { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { for (let keyCode in this.keybinds[event]) { if (e.keyCode == keyCode && this.ws.readyState == 1) this.keybinds[event][keyCode].call(this, e); }; } }); break; case "mouseup": case "mousedown": document.addEventListener(event, (e) => { for (let which in this.keybinds[event]) { if (e.which == which && this.ws.readyState == 1) this.keybinds[event][which].call(this, e); }; }); break; case "mousemove": document.addEventListener(event, (e) => { this.ws.readyState == 1 && this.keybinds[event].call(this, e); }); break; }; }; }; bindTypeHandlers(type) { if (this.parent[type]) { this.typeHandlers = []; this.typeSpecificVariables = {}; typeof this.parent[type].init == "function" && this.parent[type].init.call(this); const handlersForType = this.parent[type]; for (const handler in handlersForType) { if (typeof handlersForType[handler] !== "function" || ["init", "deinit"].indexOf(handler) > -1) continue; this.typeHandlers.push(handler); }; for (const handler of this.typeHandlers) this.addPacketHandler(PacketIds_1.default[handler], (data) => { handlersForType[handler].call(this, data, handlersForType); }); if (this.parent[type].keybinds) { Object.assign(this.keybinds, this.parent[type].keybinds); }; }; }; switchType(type) { typeof this.parent[this.identifiers.type].deinit == "function" && this.parent[this.identifiers.type].deinit.call(this); this.identifiers.type = type; this.resetListeners(type); }; updateStatus(status) { this.statusDisplay.innerHTML = ` ${this.identifiers.name}
    State: [${status}]${["Population Full", "Failed", "Closed"].indexOf(status) == -1 ? `, ping: ${this.ping}ms` : `, created: ${getClock(new Date(this.timeCreation))}`}
    ${status === "Open" ? ` UID: ${this.player.uid}, partyId: ${this.player.targetTick.partyId || 0}
    W: ${counter(this.player?.targetTick?.wood || 0)}, S: ${counter(this.player?.targetTick?.stone || 0)}, G: ${counter(this.player?.targetTick?.gold || 0)}, T: ${counter(this.player?.targetTick?.token || 0)}
    Delete ${this.identifiers.type == "Multibox" ? ` ${this.player.targetTick.partyId != game.ui.playerPartyId && this.player.targetTick.partyId ? ` Join Party ` : ""} ` : ""} ` : ""} `; if (status === "Connecting" && !getId(`alt${this.identifiers.id}`)) { this.statusDisplay.id = `alt${this.identifiers.id}`; getId("clone-status").appendChild(this.statusDisplay); }; getId(`switch${this.identifiers.id}`).onchange = ({target}) => { this.switchType(target.value); }; }; }, createSocket: function(type) { const socketId = genUUID(), socketName = this.shared.options.randomizeName.enabled ? this.shared.availableNames.shift() : game.ui.playerTick.name; this.shared.all[socketId] = new this.Socket(`wss://${game.network.connectionOptions.hostname}:443/`, { id: socketId, name: socketName, type, }); }, Builder: { }, Multibox: { init: function() { this.typeSpecificVariables.mouseData = { yaw: 1, worldX: 1, worldY: 1, distance: 1, mouseUp: 1, mouseDown: 0, mousePos: {x: 0, y: 0}, }; this.typeSpecificVariables.overrides = { autoMove: false, }; this.typeSpecificVariables.FOLLOW_ACCURACY = 50; this.typeSpecificVariables.FOLLOW_TYPE = { NONE: 0, MOUSE: 1, LOCK: 2, PLAYER: 3, }; this.typeSpecificVariables.autoMove = parseInt(getId("movementTypeOptions").value); // this.parent.shared.overridables.autoMove; this.typeSpecificVariables.followPoint = {x: 0, y: 0}; const shopItems = ['Pickaxe', 'Spear', 'Bow', 'Bomb', 'ZombieShield']; for (let i in shopItems) { this.parent.addUiHook(this.identifiers.id, `#hud-menu-shop > div.hud-shop-grid > a:nth-child(${parseInt(i) + 1})`, "click", () => { const item = shopItems[i]; if (this.parent.shared.options.control.enabled && this.ws.readyState == 1) { this.sendRpc({ name: "BuyItem", itemName: item, tier: this.player.inventory[item] ? this.player.inventory[item].tier + 1 : 1 }); }; }); }; const itemArray = ['Pickaxe', 'Spear', 'Bow', 'Bomb', 'HealthPotion', 'PetHealthPotion', 'PetWhistle']; for (let i in itemArray) { this.parent.addUiHook(this.identifiers.id, `#hud-toolbar > div.hud-toolbar-inventory > a:nth-child(${parseInt(i) + 1})`, "click", () => { if (this.parent.shared.options.control.enabled && this.ws.readyState == 1) { this.player.inventory[itemArray[i]] && this.sendRpc({ name: "EquipItem", itemName: itemArray[i], tier: this.player.inventory[itemArray[i]].tier }); }; }); }; }, deinit: function() { const shopItems = ['Pickaxe', 'Spear', 'Bow', 'Bomb', 'ZombieShield']; for (let i in shopItems) { this.parent.removeUiHook(this.identifiers.id, `#hud-menu-shop > div.hud-shop-grid > a:nth-child(${parseInt(i) + 1})`, "click"); }; const itemArray = ['Pickaxe', 'Spear', 'Bow', 'Bomb', 'HealthPotion', 'PetHealthPotion', 'PetWhistle']; for (let i in itemArray) { this.parent.removeUiHook(this.identifiers.id, `#hud-toolbar > div.hud-toolbar-inventory > a:nth-child(${parseInt(i) + 1})`, "click"); }; }, keybinds: { keyup: { 87: function() { if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({up: 0}); }; }, 83: function() { if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({down: 0}); }; }, 68: function() { if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({right: 0}); }; }, 65: function() { if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({left: 0}); }; }, 81: function(data) { if (this.parent.shared.options.control.enabled) { let nextWeapon = 'Pickaxe', weaponOrder = ['Pickaxe', 'Spear', 'Bow', 'Bomb'], foundCurrent = false; for (let i in weaponOrder) { if (foundCurrent) { if (this.player.inventory[weaponOrder[i]]) { nextWeapon = weaponOrder[i]; break; }; } else if (weaponOrder[i] == this.player.targetTick.weaponName) { foundCurrent = true; }; }; this.sendRpc({name: 'EquipItem', itemName: nextWeapon, tier: this.player.inventory[nextWeapon].tier}); }; }, 72: function() { this.parent.shared.options.control.enabled && this.sendRpc({name: 'LeaveParty'}); }, 74: function() { this.parent.shared.options.control.enabled && this.sendRpc({name: 'JoinPartyByShareKey', partyShareKey: game.ui.playerPartyShareKey}); }, 32: function() { if (this.parent.shared.options.control.enabled) { this.sendInput({space: 0}); }; }, 82: function() { if (this.parent.shared.options.control.enabled) { const buildingOverlay = game.ui.components.BuildingOverlay; const buildings = game.ui.buildings; if (buildingOverlay.shouldUpgradeAll) { for (let i in buildings) { if (buildings[i].type == buildingOverlay.buildingId && buildings[i].tier == buildingOverlay.buildingTier) { this.sendRpc({name: "UpgradeBuilding", uid: buildings[i].uid}); }; }; } else if (buildingOverlay.buildingUid) { this.sendRpc({name: "UpgradeBuilding", uid: buildingOverlay.buildingUid}); }; }; }, 89: function() { if (this.parent.shared.options.control.enabled) { const buildingOverlay = game.ui.components.BuildingOverlay; if (buildingOverlay.buildingUid) { this.sendRpc({name: "DeleteBuilding", uid: buildingOverlay.buildingUid}); }; }; }, 76: function() { if (this.parent.shared.options.control.enabled) { this.sendRpc({ name: "BuyItem", itemName: "PetRevive", tier: 1 }); this.sendRpc({ name: "EquipItem", itemName: "PetRevive", tier: 1 }); }; }, 186: function() { if (this.parent.shared.options.control.enabled && this.player.petTick) { this.sendRpc({name: "DeleteBuilding", uid: this.player.petTick.uid}); } } /* 221: function() { this.parent.shared.options.control.enabled && game.network.sendRpc({name: "JoinPartyByShareKey", partyShareKey: this.player.partyShareKey}); } */ }, keydown: { 87: function(e) { if (e.repeat) return; if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({up: 1, down: 0}); }; }, 83: function(e) { if (e.repeat) return; if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({up: 0, down: 1}); }; }, 68: function(e) { if (e.repeat) return; if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({right: 1, left: 0}); }; }, 65: function(e) { if (e.repeat) return; if (this.parent.shared.options.control.enabled && !this.typeSpecificVariables.autoMove) { this.sendInput({right: 0, left: 1}); }; }, 32: function() { if (this.parent.shared.options.control.enabled) { this.sendInput({space: 1}); }; }, 82: function() { if (this.parent.shared.options.control.enabled) { this.sendRpc({name: "BuyItem", itemName: "HealthPotion", tier: 1}); this.sendRpc({name: "EquipItem", itemName: "HealthPotion", tier: 1}); }; }, 222: function() { if (this.parent.shared.options.control.enabled) { this.sendRpc({ name: "EquipItem", itemName: "PetCARL", tier: this.player.inventory.PetCARL.tier }); this.sendRpc({ name: "BuyItem", itemName: "PetCARL", tier: this.player.inventory.PetCARL.tier + 1 }); }; }, }, mouseup: { 1: function() { if (this.parent.shared.options.control.enabled) { this.typeSpecificVariables.mouseData.mouseDown = 0; this.typeSpecificVariables.mouseData.mouseUp = 1; const {worldX, worldY, distance} = this.typeSpecificVariables.mouseData; this.sendInput({mouseUp: 1, worldX, worldY, distance}); }; }, 3: function(e) { if (this.parent.shared.options.control.enabled) { if (this.typeSpecificVariables.autoMove == this.typeSpecificVariables.FOLLOW_TYPE.LOCK) { this.typeSpecificVariables.followPoint = game.renderer.screenToWorld(e.clientX, e.clientY); }; }; }, }, mousedown: { 3: function() { /* const mouseToWorld = game.renderer.screenToWorld(e.clientX, e.clientY); const options = game.script.options; if (!ws.isclosed && ws.local.isOnControl) { if (e.which === 3) { ws.local.moveaway = true; const packet = {}; if (ws.entities.myPlayer.position.y - mouseToWorld.y > 1) { packet.down = 0; } else { packet.down = 1; } if (-ws.entities.myPlayer.position.y + mouseToWorld.y > 1) { packet.up = 0; } else { packet.up = 1; } if (-ws.entities.myPlayer.position.x + mouseToWorld.x > 1) { packet.left = 0; } else { packet.left = 1; } if (ws.entities.myPlayer.position.x - mouseToWorld.x > 1) { packet.right = 0; } else { packet.right = 1; } ws.move(packet); } } */ }, 1: function() { if (this.parent.shared.options.control.enabled) { /* if (options.raid) { if (_this.nearestToCursor !== ws.player.uid) { ws.network.sendInput({mouseUp: 0}); } if (_this.nearestToCursor === ws.player.uid) { ws.network.sendInput({space: 1}); ws.network.sendInput({space: 0}); ws.network.sendRpc({name: "JoinPartyByShareKey", partyShareKey: Game.currentGame.ui.getPlayerPartyShareKey() + ""}); } if (game.ui.playerPartyShareKey === ws.player.psk.partyShareKey && ws.entities.myPlayer.dead) { ws.network.sendRpc({name: "LeaveParty"}); ws.network.sendInput({mouseUp: 0}); } else { ws.network.sendInput({mouseUp: 0}); } } else { */ this.typeSpecificVariables.mouseData.mouseDown = 1; this.typeSpecificVariables.mouseData.mouseUp = 0; const {yaw, worldX, worldY, distance} = this.typeSpecificVariables.mouseData; this.sendInput({mouseDown: yaw, worldX, worldY, distance}); // } }; } }, mousemove: function(e) { if (this.parent.shared.options.control.enabled) { this.typeSpecificVariables.mouseData.mousePos = {x: e.clientX, y: e.clientY}; const worldMouse = game.renderer.screenToWorld(e.clientX, e.clientY); if (this.typeSpecificVariables.autoMove == this.typeSpecificVariables.FOLLOW_TYPE.MOUSE) this.typeSpecificVariables.followPoint = worldMouse; if (this.player?.targetTick?.position && !this.player?.targetTick?.dead) { const xPos = (-this.player.targetTick.position.x + worldMouse.x) * 100, yPos = (-this.player.targetTick.position.y + worldMouse.y) * 100, yaw = game.inputPacketCreator.screenToYaw(xPos, yPos); if (this.typeSpecificVariables.mouseData.yaw != yaw) { const distance = game.inputPacketCreator.distanceToCenter(xPos, yPos) / 100; this.typeSpecificVariables.mouseData.yaw = yaw; this.typeSpecificVariables.mouseData.worldX = xPos; this.typeSpecificVariables.mouseData.worldY = yPos; this.typeSpecificVariables.mouseData.distance = distance; if (this.typeSpecificVariables.mouseData.mouseDown) { this.sendInput({mouseMovedWhileDown: yaw, worldX: xPos, worldY: yPos, distance}); } if (this.typeSpecificVariables.mouseData.mouseUp) { this.sendInput({mouseMoved: yaw, worldX: xPos, worldY: yPos, distance}); }; // game.world.entities[this.player.uid].targetTick.aimingYaw = game.world.entities[this.player.uid].fromTick.aimingYaw = yaw; }; }; }; } }, PACKET_ENTITY_UPDATE: function(data, parent) { if (this.player.targetTick.dead) { this.typeSpecificVariables.mouseData.yaw = (this.typeSpecificVariables.mouseData.yaw + 1) % 360; this.sendInput({mouseMoved: this.typeSpecificVariables.mouseData.yaw}); } if (game.world.localPlayer.entity.fromTick.position.x != game.world.localPlayer.entity.targetTick.position.x || game.world.localPlayer.entity.fromTick.position.y != game.world.localPlayer.entity.targetTick.position.y) { if (this.typeSpecificVariables.autoMove == this.typeSpecificVariables.FOLLOW_TYPE.MOUSE) { this.typeSpecificVariables.followPoint = game.renderer.screenToWorld( this.typeSpecificVariables.mouseData.mousePos.x, this.typeSpecificVariables.mouseData.mousePos.y ); }; if (this.typeSpecificVariables.autoMove == this.typeSpecificVariables.FOLLOW_TYPE.PLAYER) { this.typeSpecificVariables.followPoint = game.world.localPlayer.entity.targetTick.position; }; }; // if (this.parent.shared.options.control.enabled) { if (this.typeSpecificVariables.autoMove) { const acc = this.typeSpecificVariables.FOLLOW_ACCURACY, point = this.typeSpecificVariables.followPoint, position = this.player.targetTick.position, packet = {}; if (position.y - point.y > acc) { packet.down = 0; } else { packet.down = 1; } if (-position.y + point.y > acc) { packet.up = 0; } else { packet.up = 1; } if (-position.x + point.x > acc) { packet.left = 0; } else { packet.left = 1; } if (position.x - point.x > acc) { packet.right = 0; } else { packet.right = 1; } this.sendInput(packet); }; // }; }, }, Filler: { PACKET_RPC: function(data, parent) { this.sendInput({left: 1, up: 1, right: 0, down: 0}); // this.sendInput({space: 1}); if (this.data.name == "Dead") this.sendInput({respawn: 1}); }, }, PlayerTrick: { init: function() { this.typeSpecificVariables.playerTrickTimeouts = { inull: true, i1: true, i2: true, i3: true }; this.typeSpecificVariables.dayTicker = 0; }, getIsZombiesActive: function () { let isZombiesActive = false; for (let i of game.renderer.npcs.attachments) { if (i.fromTick.model !== "NeutralTier1") { if (i.fromTick.entityClass == "Npc") { isZombiesActive = true; return isZombiesActive; } } } return isZombiesActive; }, getIsNextWaveActive: function () { let isNextWaveActive = false; const allComingBossWaves = [9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 121].map(wave => wave - 1); for (let wave of allComingBossWaves) { game.ui.playerTick.wave == wave && (isNextWaveActive = true); } return isNextWaveActive; }, getIsWaveActive: function () { let isWaveActive = false; const allBossWaves = [9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 121]; for (let wave of allBossWaves) { game.ui.playerTick.wave == wave && (isWaveActive = true); } return isWaveActive; }, PACKET_RPC: function(data, parent) { if (this.data.name == "DayCycle") { this.typeSpecificVariables.tickData = this.data.response; } }, PACKET_ENTITY_UPDATE: function(data, parent) { this.typeSpecificVariables.currentTick = data.tick; let dayRatio = 0, nightRatio = 0, barWidth = 130; if (this.typeSpecificVariables.tickData) { if (this.typeSpecificVariables.tickData.dayEndTick) { if (this.typeSpecificVariables.tickData.dayEndTick > 0) { var dayLength = this.typeSpecificVariables.tickData.dayEndTick - this.typeSpecificVariables.tickData.cycleStartTick; var dayTicksRemaining = this.typeSpecificVariables.tickData.dayEndTick - this.typeSpecificVariables.currentTick; dayRatio = 1 - dayTicksRemaining / dayLength; } } else if (this.typeSpecificVariables.tickData.nightEndTick > 0) { var nightLength = this.typeSpecificVariables.tickData.nightEndTick - this.typeSpecificVariables.tickData.cycleStartTick; var nightTicksRemaining = this.typeSpecificVariables.tickData.nightEndTick - this.typeSpecificVariables.currentTick; dayRatio = 1; nightRatio = 1 - nightTicksRemaining / nightLength; } var currentPosition = (dayRatio * 1 / 2 + nightRatio * 1 / 2) * - barWidth; var offsetPosition = currentPosition + barWidth / 2; if (offsetPosition) this.typeSpecificVariables.dayTicker = Math.round(offsetPosition); } // if (game.options.options.playerTrick) { if (parent.getIsWaveActive() && parent.getIsZombiesActive() && game.ui.playerPartyMembers.length !== 1) { if (this.typeSpecificVariables.playerTrickTimeouts.inull) { this.typeSpecificVariables.playerTrickTimeouts.inull = false; this.sendRpc({name: "LeaveParty"}); setTimeout(() => { this.typeSpecificVariables.playerTrickTimeouts && (this.typeSpecificVariables.playerTrickTimeouts.inull = true); }, 250); } } if (this.typeSpecificVariables.dayTicker < -18 && this.typeSpecificVariables.dayTicker >= -23 && !this.typeSpecificVariables.tickData.isDay && parent.getIsZombiesActive() && game.ui.playerPartyMembers.length !== 1) { if (this.typeSpecificVariables.playerTrickTimeouts.i1) { this.typeSpecificVariables.playerTrickTimeouts.i1 = false; this.sendRpc({name: "LeaveParty"}); setTimeout(() => { this.typeSpecificVariables.playerTrickTimeouts && (this.typeSpecificVariables.playerTrickTimeouts.i1 = true); }, 250); } } if (!parent.getIsZombiesActive() && game.ui.playerPartyMembers.length !== 4 && !parent.getIsNextWaveActive()) { if (this.typeSpecificVariables.playerTrickTimeouts.i2) { this.typeSpecificVariables.playerTrickTimeouts.i2 = false; this.sendRpc({name: "JoinPartyByShareKey", partyShareKey: Game.currentGame.ui.getPlayerPartyShareKey() + ""}); setTimeout(() => { this.typeSpecificVariables.playerTrickTimeouts && (this.typeSpecificVariables.playerTrickTimeouts.i2 = true); }, 250); } } if (this.typeSpecificVariables.dayTicker > 18 && this.typeSpecificVariables.dayTicker <= 23 && parent.getIsZombiesActive() && this.typeSpecificVariables.tickData.isDay && game.ui.playerPartyMembers.length !== 4) { if (this.typeSpecificVariables.playerTrickTimeouts.i3) { this.typeSpecificVariables.playerTrickTimeouts.i3 = false; this.sendRpc({name: "LeaveParty"}); setTimeout(() => { this.typeSpecificVariables.playerTrickTimeouts && (this.typeSpecificVariables.playerTrickTimeouts.i3 = true); }, 250) } } // } }, }, AITO: { lastSocket: {}, PACKET_ENTITY_UPDATE: function(data, parent) { if (this.player.partyShareKey) { if (this.player.partyShareKey != game.ui.getPlayerPartyShareKey()) { this.sendRpc({ name: "JoinPartyByShareKey", partyShareKey: game.ui.getPlayerPartyShareKey() }); } else if (this.player.targetTick.gold >= 10000) { this.sendRpc({ name: "BuyItem", itemName: "Pause", tier: 1 }); }; }; }, PACKET_PRE_ENTER_WORLD: function(data, parent) { parent.lastSocket[this.identifiers.id]?.ws?.readyState === 1 && parent.lastSocket[this.identifiers.id].ws.close(); }, PACKET_RPC: function(data, parent) { this.sendInput({left: 1, up: 1, right: 0, down: 0}); // this.sendInput({space: 1}); if (this.data.name == "DayCycle") { this.typeSpecificVariables.isDay = this.data.response.isDay; if (!this.typeSpecificVariables.isDay) { if (this.ws.readyState === 1 && this.typeSpecificVariables.verified === true) { parent.lastSocket[this.identifiers.id] = this; this.parent.shared.all[this.identifiers.id] = new this.parent.Socket( `wss://${game.network.connectionOptions.hostname}:443/`, this.identifiers ); } } else this.typeSpecificVariables.verified = true; } if (this.data.name == "Dead") this.sendInput({respawn: 1}); }, }, }, movementCopy: { handlers: [{type: "entityUpdate", names: "onTick"}, {type: "keybind", names: "keyup"}], targetLastPos: null, target: null, onTick: function({entities}) { if (game.options.options.movementCopy) { if (!this.target) { const targets = []; for (let entity of game.renderer.npcs.attachments) { if (entity.uid === game.world.myUid || entity.targetTick.dead == 1) continue; const { position, uid } = entity.targetTick; entity.entityClass === "PlayerEntity" && targets.push({position, uid}); }; if (targets.length > 0) { const myPos = game.ui.playerTick.position; targets.sort((a, b) => { const distThisToA = measureDistance(myPos, a.position); const distThisToB = measureDistance(myPos, b.position); if (distThisToA < distThisToB) return -1; else if (distThisToA > distThisToB) return 1; return 0; }); this.target = targets[0].uid; }; }; const targetInCurrentTick = entities[this.target]; const targetInWorldTick = game.world.entities[this.target]; const currentTargetPosition = targetInCurrentTick?.position; const lastTargetPosition = this.targetLastPos || targetInWorldTick.targetTick.position; if (currentTargetPosition) { const nextMove = predictDirection(lastTargetPosition, currentTargetPosition); nextMove && game.network.sendInput(nextMove); this.targetLastPos = currentTargetPosition; }; const {position: playerPos} = game.world.entities[game.world.myUid].targetTick; const distFromPlayerToTarget = measureDistance(playerPos, targetInWorldTick.targetTick.position); if (distFromPlayerToTarget > 48) { const nextMove = predictDirection(playerPos, targetInWorldTick.targetTick.position, 48); nextMove && game.network.sendInput(nextMove); }; }; }, keyup: function(e) { if (e.key == "[") getId("toggle-movementCopy").click(); }, }, spectate: { handlers: [{type: "rpc", names: ["Dead"]}, {type: "keybind", names: ["keydown", "keyup"]}], cameraPos: {x: 0, y: 0}, navigationKeys: { // numpad 104: "up", 98: "down", 102: "right", 100: "left", // wasd 87: "up", 83: "down", 68: "right", 65: "left", // keypad 38: "up", 40: "down", 39: "right", 37: "left" }, direction: { up: { axis: "y", value: -96 }, down: { axis: "y", value: 96 }, right: { axis: "x", value: 96 }, left: { axis: "x", value: -96 } }, moveCameraTo: function({x, y}) { game.renderer.follow({ getPositionX: () => x, getPositionY: () => y }); }, init: function() { this.spectatorNote = document.createElement('p'); this.spectatorNote.innerHTML = `Press Esc to stop spectating...
    Use arrow keys or WASD to navigate.`; this.spectatorNote.style.display = "none"; this.spectatorNote.style.color = "white"; this.spectatorNote.style.opacity = '0.5'; this.spectatorNote.style.textAlign = "center"; getClass('hud-top-center')[0].appendChild(this.spectatorNote); getId('hud-spectate-btn').onclick = () => { game.options.options.spectate = true; this.spectatorNote.style.display = "block"; getId("hud-respawn").style.display = "none"; }; }, Dead: function() { this.cameraPos = game.ui.playerTick.position; }, keyup: function(e) { if (e.key == "Escape") { if (game.options.options.spectate) { game.options.options.spectate = false; this.spectatorNote.style.display = "none"; getId("hud-respawn").style.display = "block"; game.renderer.follow(game.world.entities[game.world.myUid]); } } }, keydown: function(e) { if (!game.options.options.spectate) return; if (e.keyCode in this.navigationKeys) { const {axis, value} = this.direction[this.navigationKeys[e.keyCode]]; this.cameraPos[axis] += value; this.moveCameraTo(this.cameraPos); }; }, }, dragBox: { handlers: [{type: "keybind", names: "keydown"}], buildingModels: ["Wall", "Door", "SlowTrap", "ArrowTower", "CannonTower", "MeleeTower", "BombTower", "MagicTower", "GoldMine", "Harvester"], firstDragMouse: { pos: {x: 0, y: 0}, hasDragged: false }, mouseDragPos: {x: 0, y: 0}, staticWorldPos: undefined, dragBoxElem: undefined, dragBoxMenuElem: undefined, resetData: function() { this.firstDragMouse = { pos: {x: 0, y: 0}, hasDragged: false }; this.mouseDragPos = {x: 0, y: 0}; }, isWithinSelection: function({x, y}, {pos1, pos2}) { return (x > Math.min(pos1.x, pos2.x) && x < Math.max(pos1.x, pos2.x) && y > Math.min(pos1.y, pos2.y) && y < Math.max(pos1.y, pos2.y)); }, findStash: function({pos1, pos2}) { return game.renderer.scenery.attachments.find((entity) => { if (entity.fromTick.model == "GoldStash") { const building = entity.targetTick.position; if (this.isWithinSelection(building, {pos1, pos2})) return entity; } }); }, cancelDragBox: function() { this.dragBoxMenuElem.remove(); this.dragBoxElem.remove(); this.staticWorldPos = undefined; window.disableZoom = false; }, init: function() { getId("hud").addEventListener("mousedown", this.onMouseDown.bind(this)); getId("hud").addEventListener("mousemove", this.onMouseMove.bind(this)); getId("hud").addEventListener("mouseup", this.onMouseUp.bind(this)); game.renderer.on("cameraUpdate", this.onCameraUpdate.bind(this)); }, onMouseDown: function(e) { if (this.firstDragMouse.hasDragged || !game.options.options.dragBox || !game.world.inWorld) return; window.disableZoom = true; this.firstDragMouse = { pos: {x: e.clientX, y: e.clientY}, hasDragged: true }; this.dragBoxElem = document.createElement('div'); this.dragBoxElem.classList.add('dragBox'); this.dragBoxElem.style.top = `${e.clientY}px`; this.dragBoxElem.style.left = `${e.clientX}px`; getId("hud").appendChild(this.dragBoxElem); }, onMouseMove: function(e) { if (!this.firstDragMouse.hasDragged || !game.world.inWorld) return; // clearTimeout(this.dragTimeout); this.mouseDragPos = {x: e.clientX, y: e.clientY}; this.dragBoxElem.style.top = `${Math.min(this.mouseDragPos.y, this.firstDragMouse.pos.y)}px`; this.dragBoxElem.style.left = `${Math.min(this.mouseDragPos.x, this.firstDragMouse.pos.x)}px`; this.dragBoxElem.style.width = `${Math.abs(this.mouseDragPos.x - this.firstDragMouse.pos.x)}px`; this.dragBoxElem.style.height = `${Math.abs(this.mouseDragPos.y - this.firstDragMouse.pos.y)}px`; // this.dragTimeout = setTimeout(function() { this.onMouseUp(e); }.bind(this), 2000); }, onMouseUp: function(e) { if (!this.firstDragMouse.hasDragged || !game.world.inWorld) return; // clearTimeout(this.dragTimeout); const lastDragPos = { x: e.clientX, y: e.clientY }; const oldPos = { x: this.firstDragMouse.pos.x, y: this.firstDragMouse.pos.y }; this.staticWorldPos = game.renderer.screenToWorld(oldPos.x, oldPos.y); if (Math.hypot(lastDragPos.x - oldPos.x, lastDragPos.y - oldPos.y) < 50) { this.dragBoxElem.remove(); this.resetData(); this.staticWorldPos = undefined; return; } this.dragBoxMenuElem = document.createElement('div'); this.dragBoxMenuElem.classList.add('dragBoxMenu'); this.dragBoxMenuElem.style.top = `${lastDragPos.y}px`; this.dragBoxMenuElem.style.left = `${lastDragPos.x}px`; this.dragBoxMenuElem.innerHTML = ` `; getId("hud").appendChild(this.dragBoxMenuElem); game.options.options.dragBox = false; this.resetData(); const pos1 = this.staticWorldPos; const pos2 = game.renderer.screenToWorld(lastDragPos.x, lastDragPos.y); getId("cancelDragBox").onclick = this.cancelDragBox.bind(this); getId("delWithin").onclick = function(e) { const roundedX = Math.round(pos1.x / 48) * 48; const roundedY = Math.round(pos1.y / 48) * 48; const deltaX = Math.round(Math.abs(pos1.x - pos2.x) / 48); const deltaY = Math.round(Math.abs(pos1.y - pos2.y) / 48); const entitiesWithin = {}; for (let x = roundedX; x < roundedX + (deltaX * 48); x += 48) { for (let y = roundedY; y < roundedY + (deltaX * 48); y += 48) { Object.assign(entitiesWithin, getEntityAtPos(x, y)); }; }; game.script.sell.sellAllWithUids(Object.keys(entitiesWithin)); this.cancelDragBox(); }.bind(this); getId("saveSelection").onclick = function(e) { const goldStash = this.findStash({pos1, pos2})?.fromTick?.position; let baseStr = ''; if (!goldStash) { getId("selectionErrorMessage").style.display = "block"; return; }; for (let uid in game.world.entities) { const entity = game.world.entities[uid]; if (this.buildingModels.includes(entity.fromTick.model)) { const buildingPos = entity.targetTick.position; if (this.isWithinSelection(buildingPos, {pos1, pos2})) { baseStr += `${this.buildingModels.indexOf(entity.fromTick.model)},${goldStash.x - buildingPos.x},${goldStash.y - buildingPos.y},${entity.targetTick.yaw};`; }; } }; if (baseStr.length == 0) { getId("selectionErrorMessage").style.display = "block"; return; }; this.cancelDragBox(); game.ui.components.MenuSettings.isVisible() || game.ui.components.MenuSettings.show(); window.isSlotEmpty(localStorage.totalSlots) && window.createBaseSlot(); setPage('0'); getId("more-baseSaver").click(); getId("base-item-" + localStorage.totalSlots).click(); getId('target-base-design').value = baseStr; }.bind(this); }, onCameraUpdate: function() { if (!this.staticWorldPos) return; const {x: realScreenX, y: realScreenY } = game.renderer.worldToScreen(this.staticWorldPos.x, this.staticWorldPos.y); const rect = this.dragBoxElem.getBoundingClientRect(); this.dragBoxElem.style.top = `${realScreenY}px`; this.dragBoxElem.style.left = `${realScreenX}px`; this.dragBoxMenuElem.style.top = `${rect.y + rect.height}px`; this.dragBoxMenuElem.style.left = `${rect.x + rect.width}px`; }, onResize: function() { }, keydown: function(e) { if (!e.repeat && e.shiftKey && e.key == "Z") { game.options.options.dragBox = true; }; }, }, navigator: { handlers: [{type: "keybind", names: "keyup"}], targetDisplay: null, reachedTargetX: false, reachedTargetY: false, lastX: 0, lastY: 0, current_action: "", stuckPrediction: 0, timeInMs: 0, interval: 0, lastStuckPosition: {x: 0, y: 0}, init() { getId("hud-map").onclick = this.onMapClick.bind(this); }, onMapClick: function(e) { if (!e.shiftKey) return; const rect = getId("hud-map").getBoundingClientRect(), x = e.clientX - rect.left, y = e.clientY - rect.top; const worldX = (x / rect.width) * game.world.getWidth(), worldY = (y / rect.height) * game.world.getHeight(); if (game.options.options.navigator) { this.stop(); game.ui.components.PopupOverlay.showHint("Stopped current navigation."); } else { if (typeof worldX !== "number" || typeof worldY !== "number") return game.ui.components.PopupOverlay.showHint("Invalid value!"); if (worldX > 23900 || worldX < 30 || worldY > 23900 || worldY < 30) return game.ui.components.PopupOverlay.showHint("Coordinates too small / big"); this.targetDisplay = document.createElement("div"); this.targetDisplay.classList.add('hud-map-player'); this.targetDisplay.style.display = "block"; this.targetDisplay.style.left = x + "px"; this.targetDisplay.style.top = y + "px"; this.targetDisplay.style.background = 'var(--light-hover-btn)'; getId("hud-map").appendChild(this.targetDisplay); this.navigate(worldX, worldY); }; }, reset: function() { this.targetDisplay?.remove?.(); this.reachedTargetX = false; this.reachedTargetY = false; this.lastX = 0; this.lastY = 0; this.current_action = ''; this.stuckPrediction = 0; this.timeInMs = 0; this.lastStuckPosition = {x: 0, y: 0}; }, stop: function() { Game.currentGame.network.sendPacket(3, { up: 0, down: 0, left: 0, right: 0 }); console.log(`time took: ${this.timeInMs}ms`, {x: this.lastX, y: this.lastY}); game.options.options.navigator = false; clearInterval(this.interval); this.reset(); }, navigate: function(x, y) { const options = game.options.options, targetX = parseInt(x), targetY = parseInt(y); options.navigator = true; // this.reset(); this.interval = setInterval(() => { this.timeInMs += 100; if (!options.navigator) return clearInterval(this.interval); if (this.reachedTargetX && this.reachedTargetY) return this.stop(); const { x, y } = game.world.localPlayer.entity.targetTick.position; if (inRange(this.lastX, x, 5) && inRange(this.lastY, y, 5)) { this.stuckPrediction++; this.lastStuckPosition = {x, y}; }; if (this.stuckPrediction > 200) { console.log('looks like you got yourself in a confluffle!'); if (inRange(this.lastStuckPosition.x, x, 5) && inRange(this.lastStuckPosition.y, y, 5)) { game.ui.components.PopupOverlay.showConfirmation(`It seems like you've been stuck at this position twice, do you want to cancel navigating?`, 5000, () => { game.script.navigator.stop(); }, () => {}); } switch(this.current_action) { case 'ne': sw(); break; case 'se': nw(); break; case 'nw': se(); break; case 'sw': ne(); break; case 'n': s(); break; case 's': n(); break; case 'e': w(); break; case 'w': e(); break; } if (!inRange(x, this.lastX, 350) && !inRange(y, this.lastY, 350)) this.stuckPrediction = 0; return; } this.lastX = x; this.lastY = y; if (!this.reachedTargetX && inRange(x, targetX, 150)) { this.reachedTargetX = true; return stopX(); } if (!this.reachedTargetY && inRange(y, targetY, 150)) { this.reachedTargetY = true; return stopY(); } if (targetX > x && targetY < y) { this.current_action = 'ne'; return ne(); } else if (targetX > x && targetY > y) { this.current_action = 'se'; return se(); } else if (targetX < x && targetY < y) { this.current_action = 'nw'; return nw(); } else if (targetX < x && targetY > y) { this.current_action = 'sw'; return sw(); } else if (!this.reachedTargetY && targetY < y) { this.current_action = 'n'; return n(); } else if (!this.reachedTargetX && targetX > x) { this.current_action = 'e'; return e(); } else if (!this.reachedTargetY && targetY > y) { this.current_action = 's'; return s(); } else if (!this.reachedTargetX && targetX < x) { this.current_action = 'w'; return w(); } }, 100); function n() { Game.currentGame.network.sendPacket(3, { up: 1, down: 0, left: 0, right: 0 }); } function ne() { Game.currentGame.network.sendPacket(3, { up: 1, down: 0, left: 0, right: 1 }); } function e() { Game.currentGame.network.sendPacket(3, { up: 0, down: 0, left: 0, right: 1 }); } function se() { Game.currentGame.network.sendPacket(3, { up: 0, down: 1, left: 0, right: 1 }); } function s() { Game.currentGame.network.sendPacket(3, { up: 0, down: 1, left: 0, right: 0 }); } function sw() { Game.currentGame.network.sendPacket(3, { up: 0, down: 1, left: 1, right: 0 }); } function w() { Game.currentGame.network.sendPacket(3, { up: 0, down: 0, left: 1, right: 0 }); } function nw() { Game.currentGame.network.sendPacket(3, { up: 1, down: 0, left: 1, right: 0 }); } function stopX() { Game.currentGame.network.sendPacket(3, { left: 0, right: 0 }); } function stopY() { Game.currentGame.network.sendPacket(3, { up: 0, down: 0 }); } }, keyup: function(e) { if (e.key == "Escape") { if (game.options.options.navigator) { this.stop(); game.ui.components.PopupOverlay.showHint("Stopped current navigation."); } }; }, }, zoom: { dimension: 1, zoomonscroll: false, upd: function() { if (window.disableZoom) return; getClass('zoom-prop')[0].innerText = `${this.dimension.toFixed(1)}x`; const renderer = Game.currentGame.renderer; let canvasWidth = window.innerWidth * window.devicePixelRatio; let canvasHeight = window.innerHeight * window.devicePixelRatio; let ratio = canvasHeight / (1080 * this.dimension); renderer.scale = ratio; renderer.entities.setScale(ratio); renderer.ui.setScale(ratio); renderer.renderer.resize(canvasWidth, canvasHeight); renderer.viewport.width = renderer.renderer.width / renderer.scale + 2 * renderer.viewportPadding; renderer.viewport.height = renderer.renderer.height / renderer.scale + 2 * renderer.viewportPadding; const event = new Event("zoom"); document.dispatchEvent(event); }, onWindowResize: function(e) { if (this.zoomonscroll && e.deltaY) { if (e.deltaY > 0) this.dimension *= 1.02; else if (e.deltaY < 0) this.dimension /= 1.02; } this.upd(); }, zoom: function(val) { this.dimension = val; this.upd(); }, toggleZoomOnScroll: function() { this.dimension /= 1.02; this.zoomonscroll = !this.zoomonscroll; const zoomMode = document.querySelector("#zoom-mode"); const zoomBtns = [...document.getElementsByClassName('hud-zoom-item')]; if (!this.zoomonscroll) { this.resetZoom(); for (let i of zoomBtns) i.classList.remove('tag-is-disabled'); zoomMode.innerText = "Button"; } else { zoomMode.innerText = "Scroll"; for (let i of zoomBtns) i.classList.add('tag-is-disabled'); } }, zoomOut: function() { this.dimension *= 1.25; this.zoom(this.dimension); }, zoomIn: function() { this.dimension /= 1.25; this.zoom(this.dimension); }, resetZoom: function() { this.dimension = 1; this.zoom(this.dimension); }, init: function() { getId('hud').insertAdjacentHTML('beforeend', `
    1.0x Button
    `); document.addEventListener('keyup', function(e) { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { if (e.code == 'KeyG') { const wheelDisplay = getClass('interaction-wheel')[0]; wheelDisplay.style.display = (wheelDisplay.style.display === 'block') ? 'none' : 'block'; } if (e.code == "KeyN") { game.script.zoom.zoomIn(); } if (e.code == "KeyM") { game.script.zoom.zoomOut(); } if (e.key == "Escape") { getClass('interaction-wheel')[0].style.display = 'none'; }; }; }); window.onresize = this.onWindowResize.bind(this); window.onwheel = this.onWindowResize.bind(this); window.onresize(); }, }, markers: { currentIndex: 0, allMarkers: [], removeMarker: function(id) { getId(id)?.remove?.(); delete this.allMarkers[id]; }, placeMarker: function({x, y, id, timeout, shouldIndicateIndex}) { id ||= genUUID(); shouldIndicateIndex && (this.currentIndex++); const markerLeft = parseInt(Math.round(x / game.world.getWidth() * 100)) - 4; const markerTop = parseInt(Math.round(y / game.world.getHeight() * 100)) - 14; const markerElem = document.createElement("div"); markerElem.style.left = `${markerLeft}%`; markerElem.style.top = `${markerTop}%`; markerElem.style.display = "block"; markerElem.style.color = "white"; markerElem.style.position = "absolute"; markerElem.classList.add("map-display"); markerElem.id = id; markerElem.innerHTML = `${shouldIndicateIndex ? this.currentIndex : ""}`; markerElem.onclick = (e) => { e.stopPropagation(); if (e.shiftKey) return game.script.navigator.onMapClick(e); game.ui.components.PopupOverlay.showConfirmation("Do you want to remove this marker?", 5000, (() => { this.removeMarker(id); }).bind(this)); }; getId("hud-map").insertAdjacentElement("beforeend", markerElem); this.allMarkers[id] = {x, y}; timeout && setTimeout(() => { getId(`${id}`).remove(); }, 240000); }, init: function() { getId("hud-map").addEventListener("click", (e) => { if (e.shiftKey) return; const rect = getId("hud-map").getBoundingClientRect(), x = e.clientX - rect.left, y = e.clientY - rect.top; const worldX = (x / rect.width) * game.world.getWidth(), worldY = (y / rect.height) * game.world.getHeight(); this.placeMarker({x: worldX, y: worldY}); }); }, }, optimize: { zombieSprite: true, towerSprite: true, projectileSprite: true, updateAnimation: true, background: true, init: function() { if (window.location.hostname !== "localhost") { getId("shouldUseOptimization").addEventListener("change", ({target}) => { game.script.optimize.background = !target.checked; }); game.world.networkEntityPool = null; game.world._networkEntityPool = []; const bsTick = { uid: 0, entityClass: null }; const poolSize = Game.currentGame.getNetworkEntityPooling(); for (let i = 0; i < poolSize; i++) { const entity = new game.renderer.entityType(bsTick); entity.reset(); game.world._networkEntityPool.push(entity); }; game.world.getPooledNetworkEntityCount = function() { return this._networkEntityPool.length; }; } else { getId("shouldUseOptimization").disabled = true; getId("optimize-div").classList.add("disabled"); }; }, }, }; /* @Add-ons */ game.ui.components.PlacementOverlay.update = function () { if (this.buildingId) { var buildingSchema = this.ui.getBuildingSchema(), building = buildingSchema[this.buildingId], mousePos = this.ui.getMousePosition(), world = Game.currentGame.world, worldMousePos = Game.currentGame.renderer.screenToWorld(mousePos.x, mousePos.y), cells = world.entityGrid.getCellIndexes(worldMousePos.x, worldMousePos.y, { 'width': building.gridWidth, 'height': building.gridHeight }), cellSize = world.entityGrid.getCellSize(), coord = { 'x': 0, 'y': 0 }; for (var cell in cells) { if (cells[cell]) { var cellCoord = world.entityGrid.getCellCoords(cells[cell]), _cellCoord = { 'x': cellCoord.x * cellSize + cellSize / 2, 'y': cellCoord.y * cellSize + cellSize / 2 }, worldToUi = Game.currentGame.renderer.worldToUi(_cellCoord.x, _cellCoord.y), isOccupied = this.checkIsOccupied(cells[cell], cellCoord); this.placeholderTints[cell].setPosition(worldToUi.x, worldToUi.y); this.placeholderTints[cell].setIsOccupied(isOccupied); this.placeholderTints[cell].setVisible(true); coord.x += cellCoord.x; coord.y += cellCoord.y; } else this.placeholderTints[cell].setVisible(false); }; coord.x = coord.x / cells.length; coord.y = coord.y / cells.length; var worldPos = { 'x': coord.x * cellSize + cellSize / 2, 'y': coord.y * cellSize + cellSize / 2 }, uiPos = Game.currentGame.renderer.worldToUi(worldPos.x, worldPos.y); if (!this.rangeIndicator) { var { maxStashDistance } = game.ui.components.BuildingOverlay; if ('GoldStash' == this.buildingId) { this.rangeIndicator = game.assetManager.models.rangeIndicatorModel({ 'width': maxStashDistance * cellSize * 2, 'height': maxStashDistance * cellSize * 2 }, {r: 0xff, g: 0xff, b: 0xff}, {r: 0xdd, g: 0xdd, b: 0xdd}); } else if (building.rangeTiers) { this.rangeIndicator = game.assetManager.models.rangeIndicatorModel({ 'isCircular': true, 'radius': building.rangeTiers[0] * 0.57071 }, {r: 0xff, g: 0xff, b: 0xff}, {r: 0xdd, g: 0xdd, b: 0xdd}); }; this.rangeIndicator && Game.currentGame.renderer.ground.addAttachment(this.rangeIndicator); }; this.placeholderEntity.setPosition(uiPos.x, uiPos.y); this.placeholderText.setPosition(uiPos.x, uiPos.y - 110); this.rangeIndicator && this.rangeIndicator.setPosition(worldPos.x, worldPos.y); }; }; game.ui.components.PlacementOverlay.cancelPlacing = function () { if (this.buildingId) { Game.currentGame.renderer.ui.removeAttachment(this.placeholderEntity); Game.currentGame.renderer.ground.removeAttachment(this.rangeIndicator); for (let cell in this.placeholderTints) Game.currentGame.renderer.ui.removeAttachment(this.placeholderTints[cell]); for (let cell in this.borderTints) Game.currentGame.renderer.ground.removeAttachment(this.borderTints[cell]); this.placeholderText.setAlpha(0x0); this.placeholderText.setPosition(-0x3e8, -0x3e8); this.placeholderEntity = null; this.rangeIndicator = null; this.placeholderTints = []; this.borderTints = []; this.buildingId = null; }; }; /* @Rebinds */ getId('hud-debug').style.color = 'var(--light-hover-btn)'; game.debug.onRendererTick = function () { if (this.ticks++, this.visible && this.ticks % 0x14 === 0x0) { let debugHTML = `Server: ` + Game.currentGame.network.connectionOptions.id + ` (` + Game.currentGame.network.connectionOptions.name + `)
    `, serverTime = Game.currentGame.world.replicator.getServerTime(), clientTime = Game.currentGame.world.replicator.getClientTime(), realClientTime = Game.currentGame.world.replicator.getRealClientTime(), frameStutters = Game.currentGame.world.replicator.getFrameStutters(), interpolating = Game.currentGame.world.replicator.getInterpolating(), tickByteSize = Game.currentGame.world.replicator.getTickByteSize(), tickEntities = Game.currentGame.world.replicator.getTickEntities(), pooledNetworkEntityCount = (Game.currentGame.world.getLocalPlayer(), Game.currentGame.world.getPooledNetworkEntityCount()), frameExtrapolated = Game.currentGame.metrics.getFramesExtrapolated(), clientTimeResets = Game.currentGame.world.replicator.getClientTimeResets(), maxExtrapolationTime = Game.currentGame.world.replicator.getMaxExtrapolationTime(), playerPos = game.world.localPlayer.entity.getPosition(), kilobytePerSec = tickByteSize / 1024 * 20, isTickLarge = kilobytePerSec > 150; debugHTML = debugHTML + `Ping: ` + Game.currentGame.network.getPing() + ` ms
    `; debugHTML = debugHTML + `Server time: ` + serverTime + '\x20ms
    '; debugHTML = debugHTML + `Client time: ` + clientTime + ` ms
    `; debugHTML = debugHTML + `Real client time: ` + realClientTime + ` ms
    `; debugHTML = debugHTML + `Client lag: ` + (serverTime - clientTime) + '\x20ms
    '; debugHTML = debugHTML + `Real client lag: ` + (serverTime - realClientTime) + ` ms
    `; debugHTML = debugHTML + 'Stutters:\x20' + frameStutters + `
    `; debugHTML = debugHTML + `Frames extrapolated: ` + frameExtrapolated + `
    `; debugHTML = debugHTML + `Max extrapolation time: ` + maxExtrapolationTime + '
    '; debugHTML = debugHTML + `Client time resets: ` + clientTimeResets + `
    `; debugHTML = debugHTML + `Interpolating: ` + interpolating + `
    `; debugHTML = debugHTML + `Tick byte size: ` + (isTickLarge ? "" : "") + tickByteSize + '\x20(' + kilobytePerSec.toFixed(2) + 'KB/s)' + (isTickLarge ? "" : "") + '
    '; debugHTML = debugHTML + `Tick entities: ` + tickEntities + '
    '; debugHTML = debugHTML + 'Pooled\x20network\x20entities:\x20' + pooledNetworkEntityCount + '
    '; debugHTML += `Pooled model entities:
    `; for (const modelName in Game.currentGame.getModelEntityPooling()) { debugHTML = debugHTML + '- ' + modelName + ': ' + Game.currentGame.world.getPooledModelEntityCount(modelName) + '
    '; } debugHTML = debugHTML + 'Player\x20position:\x20
    '; debugHTML = debugHTML + '-\x20X:\x20' + playerPos.x.toFixed(2) + '
    '; debugHTML = debugHTML + '-\x20Y:\x20' + playerPos.y.toFixed(2) + '
    '; this.debugElem.innerHTML = debugHTML; } } game.renderer.tickCallbacks[8] = game.debug.onRendererTick.bind(game.debug); game.ui.components.MenuParty.update = function () { var parties = this.ui.getParties(); var playerIsLeader = this.ui.getPlayerPartyLeader(); var playerPartyData = parties[this.ui.getPlayerPartyId()]; var playerPartyMembers = this.ui.getPlayerPartyMembers(); var serverId = this.ui.getOption('serverId'); var staleElems = {}; var availableParties = 0; this.clonedPartyElems ||= {}; this.closedGridElem ||= getId("privateHud2"); for (var partyId in this.partyElems) { staleElems[partyId] = true; } for (var partyId in parties) { var partyData = parties[partyId]; var partyElem = this.partyElems[partyId]; var partyNameSanitized = window.filterXSS(partyData.partyName); delete staleElems[partyId]; if (!this.partyElems[partyId]) { partyElem = this.ui.createElement("
    "); this.clonedPartyElems[partyId] = this.ui.createElement("
    "); this.partyElems[partyId] = partyElem; this.gridElem.appendChild(partyElem); this.closedGridElem.appendChild(this.clonedPartyElems[partyId]); partyElem.addEventListener('click', this.onPartyJoinRequestHandler(partyData.partyId).bind(this)); } if (partyData.isOpen) { partyElem.style.display = 'block'; this.clonedPartyElems[partyId].style.display = "none"; availableParties++; } else { partyElem.style.display = 'none'; this.clonedPartyElems[partyId].style.display = "block"; } if (this.ui.getPlayerPartyId() === partyData.partyId) { partyElem.classList.add('is-active'); partyElem.classList.remove('is-disabled'); this.clonedPartyElems[partyId].classList.add('is-active'); this.clonedPartyElems[partyId].classList.remove('is-disabled'); } else if (partyData.memberCount === this.maxPartySize) { partyElem.classList.remove('is-active'); partyElem.classList.add('is-disabled'); this.clonedPartyElems[partyId].classList.remove('is-active'); this.clonedPartyElems[partyId].classList.add('is-disabled'); } else { partyElem.classList.remove('is-active'); partyElem.classList.remove('is-disabled'); this.clonedPartyElems[partyId].classList.remove('is-active'); this.clonedPartyElems[partyId].classList.remove('is-disabled'); } partyElem.innerHTML = "" + partyNameSanitized + "ID: " + partyData.partyId + " " + partyData.memberCount + "/" + this.maxPartySize + "-"; this.clonedPartyElems[partyId].innerHTML = "" + partyNameSanitized + "ID: " + partyData.partyId + " " + partyData.memberCount + "/" + this.maxPartySize + ""; } for (var partyId in staleElems) { if (!this.partyElems[partyId]) { continue; } this.partyElems[partyId].remove(); this.clonedPartyElems[partyId].remove(); delete this.partyElems[partyId]; delete this.clonedPartyElems[partyId]; } for (var i in this.memberElems) { this.memberElems[i].remove(); delete this.memberElems[i]; } for (var i in playerPartyMembers) { var playerName = window.filterXSS(playerPartyMembers[i].displayName); var memberElem = this.ui.createElement( "
    \n " + playerName + "\n " + (playerPartyMembers[i].isLeader === 1 ? 'Leader' : 'Member') + "\n \n
    " ); this.membersElem.appendChild(memberElem); this.memberElems[i] = memberElem; if (playerIsLeader && playerPartyMembers[i].isLeader === 0) { var kickElem = memberElem.querySelector('.hud-member-kick'); var canSellElem = memberElem.querySelector('.hud-member-can-sell'); kickElem.addEventListener('click', this.onPartyMemberKick(i).bind(this)); canSellElem.addEventListener('click', this.onPartyMemberCanSellToggle(i).bind(this)); } } if (availableParties > 0) { this.gridEmptyElem.style.display = 'none'; } else { this.gridEmptyElem.style.display = 'block'; } if (!playerPartyData) { this.tagInputElem.setAttribute('disabled', 'true'); this.tagInputElem.value = ''; this.shareInputElem.setAttribute('disabled', 'true'); this.shareInputElem.value = ''; this.visibilityElem.classList.add('is-disabled'); return; } if (document.activeElement !== this.tagInputElem) { this.tagInputElem.value = playerPartyData.partyName; } if (playerIsLeader) { this.tagInputElem.removeAttribute('disabled'); } else { this.tagInputElem.setAttribute('disabled', 'true'); } this.shareInputElem.removeAttribute('disabled'); this.shareInputElem.value = 'https://zombs.io/#/' + serverId + '/' + this.ui.getPlayerPartyShareKey(); if (playerIsLeader) { this.visibilityElem.classList.remove('is-disabled'); } else { this.visibilityElem.classList.add('is-disabled'); } if (playerPartyData.isOpen) { this.visibilityElem.classList.remove('is-private'); this.visibilityElem.innerText = 'Public'; } else { this.visibilityElem.classList.add('is-private'); this.visibilityElem.innerText = 'Private'; } }; getClass("hud-party-actions")[0].insertAdjacentHTML("afterend", `
    `); game.ui.components.Leaderboard.update = function () { const currentGame = Game.currentGame; // this.playerUidElems ||= {}; for (let leaderboardElem = 0; leaderboardElem < this.leaderboardData.length; leaderboardElem++) { let playerData = this.leaderboardData[leaderboardElem], playerName = this.playerNames[playerData.uid]; this.playerNames[playerData.uid] || ( playerName = window.filterXSS(playerData.name), this.playerNames[playerData.uid] = playerName ); leaderboardElem in this.playerElems || ( this.playerElems[leaderboardElem] = this.ui.createElement(''), // this.playerUidElems[leaderboardElem] = this.ui.createElement('-'), this.playerRankElems[leaderboardElem] = this.ui.createElement(`-`), this.playerNameElems[leaderboardElem] = this.ui.createElement(`-`), this.playerScoreElems[leaderboardElem] = this.ui.createElement(`-`), this.playerWaveElems[leaderboardElem] = this.ui.createElement('-'), this.playerElems[leaderboardElem].appendChild(this.playerRankElems[leaderboardElem]), // this.playerElems[leaderboardElem].appendChild(this.playerUidElems[leaderboardElem]), this.playerElems[leaderboardElem].appendChild(this.playerNameElems[leaderboardElem]), this.playerElems[leaderboardElem].appendChild(this.playerScoreElems[leaderboardElem]), this.playerElems[leaderboardElem].appendChild(this.playerWaveElems[leaderboardElem]), this.playersElem.appendChild(this.playerElems[leaderboardElem]) ); game.world.myUid === playerData.uid ? this.playerElems[leaderboardElem].classList.add('is-active') : this.playerElems[leaderboardElem].classList.remove(`is-active`); this.playerElems[leaderboardElem].style.display = `block`; // this.playerUidElems[leaderboardElem].innerText = playerData.uid; this.playerRankElems[leaderboardElem].innerText = '#' + (playerData.rank + 1); this.playerNameElems[leaderboardElem].innerText = playerName; this.playerScoreElems[leaderboardElem].innerText = playerData.score.toLocaleString(); this.playerWaveElems[leaderboardElem].innerHTML = (0 === playerData.wave) ? `-` : playerData.wave.toLocaleString(); } if (this.leaderboardData.length < this.playerElems.length) { for (let invalidEntries = this.leaderboardData.length; invalidEntries < this.playerElems.length; invalidEntries++) { this.playerElems[invalidEntries].style.display = `none`; } } } game.ui.components.BuildingOverlay.createResourceCostString = function (schema, schemaTier, amountOfBuildings) { void 0 === schemaTier && (schemaTier = 0x1); void 0 === amountOfBuildings && (amountOfBuildings = 0x1); var totalCost = [], resources = { 'wood': `wood`, 'stone': `stone`, 'gold': 'gold', 'token': 'tokens' }, playerTick = Game.currentGame.ui.getPlayerTick(); for (var resource in resources) { var resourceCost = resource + `Costs`; if (schema[resourceCost] && schema[resourceCost][schemaTier - 1]) { var currentTotalCost = schema[resourceCost][schemaTier - 1] * amountOfBuildings, canAfford = playerTick && playerTick[resource] >= currentTotalCost; canAfford ? totalCost.push('' + currentTotalCost.toLocaleString() + '\x20' + resources[resource] + ``) : totalCost.push(`` + currentTotalCost.toLocaleString() + '\x20' + resources[resource] + ``); } } return totalCost.length > 0 ? totalCost.join(',\x20') : `Free`; } game.ui.components.BuildingOverlay.createResourceRefundString = function (buildingType, buildingSchema, tier) { void 0 === tier && (tier = 1); var totalCost = [], buildingsByTier = {}, buildings = Object.values(game.ui.buildings).filter(e => e.type == buildingType), resources = { 'wood': `wood`, 'stone': `stone`, 'gold': `gold`, 'token': 'tokens' }; for (let building of buildings) { buildingsByTier[building.tier] ||= 0; buildingsByTier[building.tier]++; } for (var resource in resources) { var resourceCost = resource + `Costs`; if (buildingSchema[resourceCost]) { var totalTierCost = 0; if (this.shouldUpgradeAll) { for (let i = 1; i <= tier; i++) { totalTierCost = Math.floor( buildingSchema[resourceCost].slice(0, i) .reduce((prev, curr) => prev + curr, 0) / 2 ) * buildingsByTier[i]; } } else totalTierCost = Math.floor(buildingSchema[resourceCost].slice(0, tier).reduce((prev, curr) => prev + curr, 0) / 2); totalTierCost && totalCost.push('' + totalTierCost.toLocaleString() + '\x20' + resources[resource] + ``); } } return totalCost.length > 0 ? totalCost.join(',\x20') : `None`; } game.ui.components.BuildingOverlay.excludeBuilding = function() { game.script.AHRC.excludedHarvesters.has(this.buildingUid) ? ( this.excludeElem.classList.add("btn-theme"), this.excludeElem.classList.remove("btn-red"), this.excludeElem.innerHTML = `Exclude Harvester`, game.script.AHRC.excludedHarvesters.delete(this.buildingUid) ) : ( this.excludeElem.classList.add("btn-red"), this.excludeElem.classList.remove("btn-theme"), this.excludeElem.innerHTML = `Include Harvester`, game.script.AHRC.excludedHarvesters.add(this.buildingUid) ); } game.ui.components.BuildingOverlay.viewFromBuilding = function() { const buildings = game.ui.getBuildings(), building = game.world.entities[this.buildingUid]; if (!building) return; this.isViewingFromBuilding = true; this.componentElem.style.opacity = "0.1"; game.renderer.follow(building); }; game.ui.components.BuildingOverlay.stopWatching = function () { this.buildingUid && ( this.isViewingFromBuilding && game.renderer.follow(game.world.entities[game.world.myUid]), this.rangeIndicator && ( Game.currentGame.renderer.ground.removeAttachment(this.rangeIndicator), delete this.rangeIndicator ), this.componentElem.innerHTML = '', this.componentElem.style.opacity = "1", this.componentElem.style.left = `-1000px`, this.componentElem.style.top = '-1000px', this.buildingUid = null, this.buildingId = null, this.buildingTier = null, this.hide() ); } game.ui.components.BuildingOverlay.startWatching = function(buildingId) { this.buildingUid && this.stopWatching(); let buildings = this.ui.getBuildings(), building = buildings[buildingId]; if (!building) return; this.buildingUid = buildingId; this.buildingId = building.type; this.buildingTier = building.tier; const schema = this.ui.getBuildingSchema(), buildingSchema = schema[this.buildingId]; if ('GoldStash' == this.buildingId) { var world = Game.currentGame.world, cellSize = world.entityGrid.getCellSize(); this.rangeIndicator = game.assetManager.loadModel('RangeIndicatorModel', { 'width': this.maxStashDistance * cellSize * 2, 'height': this.maxStashDistance * cellSize * 2 }); Game.currentGame.renderer.ground.addAttachment(this.rangeIndicator); } else if (buildingSchema.rangeTiers) { this.rangeIndicator = game.assetManager.loadModel('RangeIndicatorModel', { 'isCircular': true, 'radius': buildingSchema.rangeTiers[this.buildingTier - 1] * 0.57071 }); Game.currentGame.renderer.ground.addAttachment(this.rangeIndicator) }; this.componentElem.innerHTML = `

    ` + buildingSchema.name + `

    Tier ` + this.buildingTier + ` Building

    `; this.tierElem = this.componentElem.querySelector(`.hud-building-tier`); this.healthBarElem = this.componentElem.querySelector(`.hud-tooltip-health-bar`); this.statsElem = this.componentElem.querySelector(`.hud-building-stats`); this.actionsElem = this.componentElem.querySelector(`.hud-building-actions`); this.depositElem = this.componentElem.querySelector(`.hud-building-deposit`); this.dualBtnElem = this.componentElem.querySelector(`.hud-building-dual-btn`); this.excludeElem = this.componentElem.querySelector(`.hud-building-ahrc`); this.collectElem = this.componentElem.querySelector(`.hud-building-collect`); this.viewElem = this.componentElem.querySelector('.hud-building-view'); this.upgradeElem = this.componentElem.querySelector('.hud-building-upgrade'); this.sellElem = this.componentElem.querySelector('.hud-building-sell'); `Harvester` !== this.buildingId && ( this.dualBtnElem.style.display = `none`, this.excludeElem.style.display = `none`, this.viewElem.style.display = `block` ); game.script.AHRC.excludedHarvesters.has(this.buildingUid) ? ( this.excludeElem.classList.add("btn-red"), this.excludeElem.classList.remove("btn-theme"), this.excludeElem.innerHTML = `Include Harvester` ) : ( this.excludeElem.classList.add("btn-theme"), this.excludeElem.classList.remove("btn-red"), this.excludeElem.innerHTML = `Exclude Harvester` ); this.depositElem.addEventListener(`click`, this.depositIntoBuilding.bind(this)); this.collectElem.addEventListener(`click`, this.collectFromBuilding.bind(this)); this.excludeElem.addEventListener(`click`, this.excludeBuilding.bind(this)); this.viewElem.addEventListener(`click`, this.viewFromBuilding.bind(this)); this.upgradeElem.addEventListener(`click`, this.upgradeBuilding.bind(this)); this.sellElem.addEventListener(`click`, this.sellBuilding.bind(this)); this.show(); this.update(); } game.ui.components.BuildingOverlay.update = function () { if (this.buildingUid) { const buildingEntity = Game.currentGame.world.getEntityByUid(this.buildingUid); if (!buildingEntity) return void this.stopWatching(); let renderer = Game.currentGame.renderer, buildingUiPosition = renderer.worldToScreen(buildingEntity.getPositionX(), buildingEntity.getPositionY()), buildingTick = buildingEntity.getTargetTick(), buildingsSchema = this.ui.getBuildingSchema(), buildings = this.ui.getBuildings(), buildingSchema = buildingsSchema[this.buildingId], building = buildings[this.buildingUid]; if (!building) return void this.stopWatching(); let buildingHeight = buildingSchema.gridHeight, buildingScale = (buildingSchema.gridWidth, buildingHeight / 2 * 48 * (renderer.getScale() / window.devicePixelRatio)), buildingTier = building.tier, buildingSchemaTier = 1, isBuildingMaxed = false, isBuildingAtMaxTier = false, currentStats = {}, nextTierStats = {}, sameBuildings = 1, buildingStats = { 'health': `Health`, 'damage': `Damage`, 'range': 'Range', 'gps': 'Gold/Sec', 'harvest': `Harvest/Sec`, 'harvestCapacity': `Capacity`, 'msBetweenFires': `Firerate` }; if (buildingSchema.tiers) { const stashTier = this.getGoldStashTier(); building.tier < buildingSchema.tiers ? (buildingSchemaTier = building.tier + 1, isBuildingMaxed = false) : (buildingSchemaTier = building.tier, isBuildingMaxed = true); isBuildingAtMaxTier = !isBuildingMaxed && (building.tier < stashTier || `GoldStash` === this.buildingId); } for (let buildingStat in buildingStats) { let currentStat = ``, nextTierStat = ''; buildingSchema[buildingStat + `Tiers`] && ( currentStat = buildingSchema[buildingStat + `Tiers`][buildingTier - 1], buildingStat == "msBetweenFires" && (currentStat = (1000 / currentStat).toFixed(2).toLocaleString()), isBuildingMaxed || ( nextTierStat = buildingSchema[buildingStat + 'Tiers'][buildingSchemaTier - 1], buildingStat == "msBetweenFires" && (nextTierStat = (1000 / nextTierStat).toFixed(2).toLocaleString()) ), currentStats[buildingStat] = '

    ' + buildingStats[buildingStat] + `: ` + currentStat + '

    ', nextTierStats[buildingStat] = `

    ` + buildingStats[buildingStat] + ':\x20' + nextTierStat + `

    ` ); } if (this.shouldUpgradeAll) { sameBuildings = 0; for (let buildingUid in buildings) { // parseInt(buildingUid); tf buildings[buildingUid].type === this.buildingId && buildings[buildingUid].tier === building.tier && sameBuildings++; } } let costString = this.createResourceCostString(buildingSchema, buildingSchemaTier, sameBuildings), refundString = this.createResourceRefundString(this.buildingId, buildingSchema, building.tier), buildingHealth = Math.round(buildingTick.health / buildingTick.maxHealth * 100); buildingTick.partyId !== this.ui.getPlayerPartyId() ? this.actionsElem.style.display = `none` : this.actionsElem.style.display = 'block'; this.tierElem.innerHTML = building.tier.toString(); this.buildingTier = building.tier; this.healthBarElem.style.width = buildingHealth + '%'; if (Object.keys(currentStats).length > 0) { let currentStatValues = '', nextTierStatValues = ''; for (let stat in currentStats) currentStatValues += currentStats[stat]; for (let stat in nextTierStats) nextTierStatValues += nextTierStats[stat]; this.statsElem.innerHTML = `
    ` + currentStatValues + `
    ` + nextTierStatValues + `
    `; } else this.statsElem.innerHTML = ''; if (`Harvester` === this.buildingId) { let depositAmount = Math.floor(buildingTick.depositMax / 10), isDepositPossible = buildingTick.depositMax - buildingTick.deposit < depositAmount; isDepositPossible ? this.depositElem.classList.add('is-disabled') : this.depositElem.classList.remove(`is-disabled`); this.shouldUpgradeAll ? this.depositElem.innerHTML = 'Refuel\x20All\x20(' + (depositAmount * sameBuildings).toLocaleString() + ` gold)` : this.depositElem.innerHTML = 'Refuel\x20(' + depositAmount.toLocaleString() + ` gold)`; } isBuildingAtMaxTier ? this.upgradeElem.classList.remove(`is-disabled`) : this.upgradeElem.classList.add('is-disabled'); this.shouldUpgradeAll ? ( this.upgradeElem.innerHTML = `Upgrade All (` + costString + ')', this.sellElem.innerHTML = `Sell All (` + refundString + ')' ) : ( this.upgradeElem.innerHTML = `Upgrade (` + costString + `)`, this.sellElem.innerHTML = `Sell (` + refundString + `)` ); `GoldStash` == this.buildingId ? ( this.sellElem.innerHTML = `Sell All Buildings`, this.sellElem.classList.remove(`is-disabled`), this.isSellingAll && this.sellElem.classList.add(`is-disabled`) ) : this.ui.getPlayerPartyCanSell() ? ( this.sellElem.classList.remove(`is-disabled`) ) : ( this.sellElem.classList.add(`is-disabled`), this.sellElem.innerHTML = `Need Permission to Sell` ); this.componentElem.style.left = buildingUiPosition.x - this.componentElem.offsetWidth / 0x2 + 'px'; this.componentElem.style.top = buildingUiPosition.y - buildingScale - this.componentElem.offsetHeight - 0x14 + 'px'; this.rangeIndicator && this.rangeIndicator.setPosition(buildingEntity.getPositionX(), buildingEntity.getPositionY()); } } game.ui.onBuildingSchemaUpdate = function (rawSchema) { const schemas = JSON.parse(rawSchema.json); for (const buildingId in schemas) { let buildingSchema = schemas[buildingId]; for (var schema in this.buildingSchema) { if (schema == buildingSchema.Name) { buildingSchema.MsBetweenFires && (this.buildingSchema[schema].msBetweenFiresTiers = buildingSchema.MsBetweenFires); buildingSchema.ProjectileAoe && (this.buildingSchema[schema].aoeTiers = buildingSchema.ProjectileAoe); buildingSchema.ProjectileAoeRadius && (this.buildingSchema[schema].aoeRadiusTiers = buildingSchema.ProjectileAoeRadius); buildingSchema.ProjectileLifetime && (this.buildingSchema[schema].lifetimeTiers = buildingSchema.ProjectileLifetime); break; } } } this.emit(`buildingSchemaUpdate`, this.buildingSchema); } game.ui.onItemSchemaUpdate = function (rawSchema) { var schemas = JSON.parse(rawSchema.json); for (var item in schemas) { var itemSchema = schemas[item]; for (var schema in this.itemSchema) { schema == "Bow" && (this.itemSchema[schema].rangeTiers = Array(7).fill(1000)); if (schema == itemSchema.Name) { itemSchema.MaxYawDeviation && (this.itemSchema[schema].maxYawDeviationTiers = itemSchema.MaxYawDeviation); itemSchema.MsBetweenFires && (this.itemSchema[schema].msBetweenFiresTiers = itemSchema.MsBetweenFires); itemSchema.ProjectileAoe && (this.itemSchema[schema].aoeTiers = itemSchema.ProjectileAoe); itemSchema.ProjectileAoeRadius && (this.itemSchema[schema].aoeRadiusTiers = itemSchema.ProjectileAoeRadius); itemSchema.ProjectileLifetime && (this.itemSchema[schema].lifetimeTiers = itemSchema.ProjectileLifetime); break; } } } this.emit(`itemSchemaUpdate`, this.itemSchema); } game.network.addRpcHandler("BuildingShopPrices", game.ui.onBuildingSchemaUpdate.bind(game.ui)); game.network.addRpcHandler("ItemShopPrices", game.ui.onItemSchemaUpdate.bind(game.ui)); /* @CustomRenderer */ game.renderer.nodeType = class i { constructor(e = null) { this.attachments = []; this.parent = null; this.isVisible = true; this.setNode(e || new window.PIXI.Container); } getNode() { return this.node; } setNode(t) { this.node = t; } getParent() { return this.parent; } setParent(t) { this.parent = t; } getAttachments() { return this.attachments; } addAttachment(t, e = 0) { t.getNode().zHack = e; t.setParent(this); this.node.addChild(t.getNode()); this.attachments.push(t); this.node.children.sort(function (t, e) { return t.zHack == e.zHack ? 0 : t.zHack < e.zHack ? -1 : 1; }); } removeAttachment(t) { if (t) { this.node.removeChild(t.getNode()); t.setParent(null); var e = this.attachments.indexOf(t); e > -1 && this.attachments.splice(e, 1); } } getRotation() { return 180 * this.node.rotation / Math.PI; } setRotation(t) { this.node.rotation = t * Math.PI / 180; } getAlpha() { return this.node.alpha; } setAlpha(t) { this.node.alpha = t; } getScale() { return this.node.scale; } setScale(t) { this.node.scale.x = t; this.node.scale.y = t; } getScaleX() { return this.node.scale.x; } setScaleX(t) { this.node.scale.x = t; } getScaleY() { return this.node.scale.y; } setScaleY(t) { this.node.scale.y = t; } getFilters() { return this.node.filters; } setFilters(t) { this.node.filters = t; } getPosition() { return this.node.position; } setPosition(t, e) { this.node.position.x = t; this.node.position.y = e; } getPositionX() { return this.getPosition().x; } setPositionX(t) { this.node.position.x = t; } getPositionY() { return this.getPosition().y; } setPositionY(t) { this.node.position.y = t; } getPivotPoint() { return this.node.pivot; } setPivotPoint(t, e) { this.node.pivot.x = t; this.node.pivot.y = e; } getVisible() { return this.isVisible; } setVisible(t) { this.isVisible = t; this.node.visible = t; } update(t, e) { for (let r = 0; r < this.attachments.length; r++) this.attachments[r].update(t, e); } }; game.renderer.spriteType = class d extends game.renderer.nodeType { constructor(texture, tiled) { super(); this.sprite = null; if (typeof texture === 'string') { texture = window.PIXI.Texture.fromImage(texture); } if (tiled) { this.sprite = new window.PIXI.extras.TilingSprite(texture); this.sprite.texture.baseTexture.scaleMode = window.PIXI.SCALE_MODES.NEAREST; } else { this.sprite = new window.PIXI.Sprite(texture); } this.sprite.anchor.x = 0.5; this.sprite.anchor.y = 0.5; this.setNode(this.sprite); } getAnchor() { return this.sprite.anchor; }; setAnchor(x, y) { this.sprite.anchor.x = x; this.sprite.anchor.y = y; }; getTint() { return this.node.tint; }; setTint(tint) { this.node.tint = tint; }; getBlendMode() { return this.node.tint; }; setBlendMode(blendMode) { this.node.blendMode = blendMode; }; getMask() { return this.node.mask; }; setMask(entity) { this.node.mask = entity.getNode(); }; setDimensions(x, y, width, height) { this.sprite.x = x; this.sprite.y = y; this.sprite.width = width; this.sprite.height = height; }; }; game.renderer.graphicsType = class u extends game.renderer.nodeType { constructor() { super(); this.draw = new window.PIXI.Graphics(); this.draw.clear(); this.setNode(this.draw); } drawLine(thickness, xStart, yStart, xEnd, yEnd, color = 0xffffff, alpha = 1) { this.draw.alpha = alpha; this.draw.lineStyle(thickness, color) .moveTo(xStart, yStart) .lineTo(xEnd, yEnd); } drawTriangle(_a, _b, _c, _d, _e, _f) { void 0x0 === _d && (_d = null); void 0x0 === _e && (_e = null); void 0x0 === _f && (_f = null); _f && _f > 0x0 && this.draw.lineStyle(_f, _e.r << 0x10 | _e.g << 0x8 | _e.b, _e.a); _d && this.draw.beginFill(_d.r << 0x10 | _d.g << 0x8 | _d.b, _d.a); this.draw.drawPolygon([_a.x, _a.y, _b.x, _b.y, _c.x, _c.y]); _d && this.draw.endFill(); } drawArc(_a, _b, _c, _d, _e, _f, _g, _h, _i) { void 0x0 === _g && (_g = null), void 0x0 === _h && (_h = null), void 0x0 === _i && (_i = null), _i && _i > 0x0 && this.draw.lineStyle(_i, _h.r << 0x10 | _h.g << 0x8 | _h.b, _h.a), _d = _d * Math.PI / 0xb4, _e = _e * Math.PI / 0xb4, _g && this.draw.beginFill(_g.r << 0x10 | _g.g << 0x8 | _g.b, _g.a), this.draw.arc(_a, _b, _c, _d, _e, _f), _g && this.draw.endFill(); }; drawCircle(_a, _b, _c, _d, _e, _f) { void 0x0 === _d && (_d = null), void 0x0 === _e && (_e = null), void 0x0 === _f && (_f = null), _f && _f > 0x0 && this.draw.lineStyle(_f, _e.r << 0x10 | _e.g << 0x8 | _e.b, _e.a), _d && this.draw.beginFill(_d.r << 0x10 | _d.g << 0x8 | _d.b, _d.a), this.draw.drawCircle(_a, _b, _c), _d && this.draw.endFill(); }; drawRect(_a, _b, _c, _d, _e, _f, _g) { void 0x0 === _e && (_e = null), void 0x0 === _f && (_f = null), void 0x0 === _g && (_g = null), _g && _g > 0x0 && this.draw.lineStyle(_g, _f.r << 0x10 | _f.g << 0x8 | _f.b, _f.a), _e && this.draw.beginFill(_e.r << 0x10 | _e.g << 0x8 | _e.b, _e.a), this.draw.drawRect(_a, _b, _c - _a, _d - _b), _e && this.draw.endFill(); }; drawRoundedRect(_a, _b, _c, _d, _e, _f, _g, _h) { void 0x0 === _f && (_f = null), void 0x0 === _g && (_g = null), void 0x0 === _h && (_h = null), _h && _h > 0x0 && this.draw.lineStyle(_h, _g.r << 0x10 | _g.g << 0x8 | _g.b, _g.a), _f && this.draw.beginFill(_f.r << 0x10 | _f.g << 0x8 | _f.b, _f.a), this.draw.drawRoundedRect(_a, _b, _c - _a, _d - _b, _e), _f && this.draw.endFill(); }; drawEllipse(_a, _b, _c, _d, _e, _f, _g) { void 0x0 === _e && (_e = null), void 0x0 === _f && (_f = null), void 0x0 === _g && (_g = null), _g && _g > 0x0 && this.draw.lineStyle(_g, _f.r << 0x10 | _f.g << 0x8 | _f.b, _f.a), _e && this.draw.beginFill(_e.r << 0x10 | _e.g << 0x8 | _e.b, _e.a), this.draw.drawEllipse(_a, _b, _c, _d), _e && this.draw.endFill(); }; getTexture() { const renderer = Game.currentGame.renderer.getInternalRenderer(); return renderer.generateTexture(this.draw); }; clear() { this.draw.clear(); }; }; if (window.location.hostname !== "localhost") { game.renderer.models = {GamePlayer:{model:"PlayerModel"},Stone:{model:"RecoilModel",gridSize:{width:3,height:3},args:{name:"/asset/image/map/map-stone.svg"}},Tree:{model:"RecoilModel",gridSize:{width:4,height:4},args:{name:"/asset/image/map/map-tree.svg"}},Wall:{model:"WallModel"},Door:{model:"DoorModel"},SlowTrap:{model:"SlowTrapModel"},ArrowTower:{model:"ArrowTowerModel",gridSize:{width:2,height:2}},CannonTower:{model:"CannonTowerModel",gridSize:{width:2,height:2}},MeleeTower:{model:"MeleeTowerModel",gridSize:{width:2,height:2}},BombTower:{model:"BombTowerModel",gridSize:{width:2,height:2}},MagicTower:{model:"MageTowerModel",gridSize:{width:2,height:2}},GoldMine:{model:"GoldMineModel",gridSize:{width:2,height:2}},Harvester:{model:"HarvesterModel",gridSize:{width:2,height:2}},GoldStash:{model:"GoldStashModel",gridSize:{width:2,height:2}},ArrowProjectile:{model:"ProjectileArrowModel"},CannonProjectile:{model:"ProjectileCannonModel"},BowProjectile:{model:"ProjectileArrowModel"},BombProjectile:{model:"ProjectileBombModel"},FireballProjectile:{model:"ProjectileMageModel"},HealTowersSpell:{model:"HealTowersSpellModel"},PetCARL:{model:"PetModel"},PetMiner:{model:"PetModel"},ZombieGreenTier1:{model:"ZombieModel"},ZombieGreenTier2:{model:"ZombieModel"},ZombieGreenTier3:{model:"ZombieModel"},ZombieGreenTier4:{model:"ZombieModel"},ZombieGreenTier5:{model:"ZombieModel"},ZombieGreenTier6:{model:"ZombieModel"},ZombieGreenTier7:{model:"ZombieModel"},ZombieGreenTier8:{model:"ZombieModel"},ZombieGreenTier9:{model:"ZombieModel"},ZombieGreenTier10:{model:"ZombieModel"},ZombieRangedGreenTier1:{model:"ZombieRangedModel"},ZombieBlueTier1:{model:"ZombieModel"},ZombieBlueTier2:{model:"ZombieModel"},ZombieBlueTier3:{model:"ZombieModel"},ZombieBlueTier4:{model:"ZombieModel"},ZombieBlueTier5:{model:"ZombieModel"},ZombieBlueTier6:{model:"ZombieModel"},ZombieBlueTier7:{model:"ZombieModel"},ZombieBlueTier8:{model:"ZombieModel"},ZombieBlueTier9:{model:"ZombieModel"},ZombieBlueTier10:{model:"ZombieModel"},ZombieRedTier1:{model:"ZombieModel"},ZombieRedTier2:{model:"ZombieModel"},ZombieRedTier3:{model:"ZombieModel"},ZombieRedTier4:{model:"ZombieModel"},ZombieRedTier5:{model:"ZombieModel"},ZombieRedTier6:{model:"ZombieModel"},ZombieRedTier7:{model:"ZombieModel"},ZombieRedTier8:{model:"ZombieModel"},ZombieRedTier9:{model:"ZombieModel"},ZombieRedTier10:{model:"ZombieModel"},ZombieYellowTier1:{model:"ZombieModel"},ZombieYellowTier2:{model:"ZombieModel"},ZombieYellowTier3:{model:"ZombieModel"},ZombieYellowTier4:{model:"ZombieModel"},ZombieYellowTier5:{model:"ZombieModel"},ZombieYellowTier6:{model:"ZombieModel"},ZombieYellowTier7:{model:"ZombieModel"},ZombieYellowTier8:{model:"ZombieModel"},ZombieYellowTier9:{model:"ZombieModel"},ZombieYellowTier10:{model:"ZombieModel"},ZombiePurpleTier1:{model:"ZombieModel"},ZombiePurpleTier2:{model:"ZombieModel"},ZombiePurpleTier3:{model:"ZombieModel"},ZombiePurpleTier4:{model:"ZombieModel"},ZombiePurpleTier5:{model:"ZombieModel"},ZombiePurpleTier6:{model:"ZombieModel"},ZombiePurpleTier7:{model:"ZombieModel"},ZombiePurpleTier8:{model:"ZombieModel"},ZombiePurpleTier9:{model:"ZombieModel"},ZombiePurpleTier10:{model:"ZombieModel"},ZombieOrangeTier1:{model:"ZombieModel"},ZombieOrangeTier2:{model:"ZombieModel"},ZombieOrangeTier3:{model:"ZombieModel"},ZombieOrangeTier4:{model:"ZombieModel"},ZombieOrangeTier5:{model:"ZombieModel"},ZombieOrangeTier6:{model:"ZombieModel"},ZombieOrangeTier7:{model:"ZombieModel"},ZombieOrangeTier8:{model:"ZombieModel"},ZombieOrangeTier9:{model:"ZombieModel"},ZombieOrangeTier10:{model:"ZombieModel"},ZombieBossTier1:{model:"ZombieBossModel"},ZombieBossTier2:{model:"ZombieBossModel"},ZombieBossTier3:{model:"ZombieBossModel"},ZombieBossTier4:{model:"ZombieBossModel"},ZombieBossTier5:{model:"ZombieBossModel"},ZombieBossTier6:{model:"ZombieBossModel"},ZombieBossTier7:{model:"ZombieBossModel"},ZombieBossTier8:{model:"ZombieBossModel"},ZombieBossTier9:{model:"ZombieBossModel"},ZombieBossTier10:{model:"ZombieBossModel"},ZombieBossTier11:{model:"ZombieBossModel"},ZombieBossTier12:{model:"ZombieBossModel"},ZombieBossTier13:{model:"ZombieBossModel"},ZombieBossTier14:{model:"ZombieBossModel"},ZombieBossTier15:{model:"ZombieBossModel"},ZombieBossTier16:{model:"ZombieBossModel"},ZombieBossTier17:{model:"ZombieBossModel"},ZombieBossTier18:{model:"ZombieBossModel"},ZombieBossTier19:{model:"ZombieBossModel"},ZombieBossTier20:{model:"ZombieBossModel"},ZombieBossTier21:{model:"ZombieBossModel"},ZombieBossTier22:{model:"ZombieBossModel"},ZombieBossTier23:{model:"ZombieBossModel"},ZombieBossTier24:{model:"ZombieBossModel"},ZombieBossTier25:{model:"ZombieBossModel"},ZombieBossTier26:{model:"ZombieBossModel"},ZombieBossTier27:{model:"ZombieBossModel"},ZombieBossTier28:{model:"ZombieBossModel"},ZombieBossTier29:{model:"ZombieBossModel"},ZombieBossTier30:{model:"ZombieBossModel"},ZombieBossTier31:{model:"ZombieBossModel"},ZombieBossTier32:{model:"ZombieBossModel"},ZombieBossTier33:{model:"ZombieBossModel"},ZombieBossTier34:{model:"ZombieBossModel"},ZombieBossTier35:{model:"ZombieBossModel"},ZombieBossTier36:{model:"ZombieBossModel"},ZombieBossTier37:{model:"ZombieBossModel"},ZombieBossTier38:{model:"ZombieBossModel"},ZombieBossTier39:{model:"ZombieBossModel"},ZombieBossTier40:{model:"ZombieBossModel"},NeutralCamp:{model:"NeutralCampModel"},NeutralTier1:{model:"NeutralModel"},PathNode:{model:"PathNodeModel"}}; game.lerp = function(t, e, r) { return (r > 1.2 && (r = 1), t + (e - t) * r); } game.mod = function (_a, _b) { return (_a % _b + _b) % _b; } game.interpolateYaw = function (__a, __b) { var _game = Game.currentGame.world.getReplicator().getMsInThisTick() / Game.currentGame.world.getMsPerTick(), delta = __a - __b; delta = game.mod(delta + 0xb4, 0x168) - 0xb4; delta = game.lerp(0x0, delta, _game); var addDelta = __b + delta; addDelta < 0x0 && (addDelta += 0x168); addDelta >= 0x168 && (addDelta -= 0x168); return addDelta; } game.renderer.entityType = class o extends game.renderer.nodeType { constructor(t) { super(); this.uid = t.uid; this.setVisible(true); this.setTargetTick(t); } calculateVector(t, e, r) { return (r > 1.2 && (r = 1), t + (e - t) * r); } reset() { this.uid = 0; this.currentModel = null; this.entityClass = null; this.fromTick = null; this.targetTick = null; this.setVisible(true); } isLocal() { var t = Game.currentGame.world.getLocalPlayer(); return !(!t || !t.getEntity()) && this.uid == t.getEntity().uid; } getTargetTick() { return this.targetTick; } getFromTick() { return this.fromTick; } setTargetTick(t) { this.targetTick || (this.entityClass = t.entityClass, this.targetTick = t); this.addMissingTickFields(t, this.targetTick); this.fromTick = this.targetTick; this.targetTick = t; void 0 !== t.scale && this.setScale(t.scale); this.fromTick.model !== this.targetTick.model && this.refreshModel(this.targetTick.model); this.entityClass = this.targetTick.entityClass; } overrideFromTick(t) { this.fromTick = t; } overrideTargetTick(t) { this.targetTick = t; } tick(t, e) { if (this.fromTick) { var r = t / e; this.isVisible || this.setVisible(true); this.setPositionX(this.calculateVector(this.fromTick.position.x, this.targetTick.position.x, r)); this.setPositionY(this.calculateVector(this.fromTick.position.y, this.targetTick.position.y, r)); this.setRotation(((t, e) => { let r = t - e; r = ((r + 180) % 360 + 360) % 360 - 180; r = this.calculateVector(0, r, Game.currentGame.world.getReplicator().getMsInThisTick() / Game.currentGame.world.getMsPerTick()); let i = e + r; i < 0 && (i += 360); i >= 360 && (i -= 360); return i; })(this.targetTick.yaw, this.fromTick.yaw)); } } update(t, e) { this.fromTick && (this.fromTick.interpolatedYaw = this.getRotation()); if (this.currentModel && game.script.optimize.updateAnimation) this.currentModel.update(t, this.fromTick); this.node.visible = this.isVisible; } refreshModel(t) { const e = game.renderer.models; if (!(t in e)) throw new Error("Attempted to create unknown model: " + t); var r = e[t].model; Game.currentGame.getModelEntityPooling(r) && (this.currentModel = Game.currentGame.world.getModelFromPool(r)); if (!this.currentModel) { var i = {}; "args" in e[t] && (i = e[t].args); i.modelName = t; this.currentModel = Game.currentGame.assetManager.loadModel(r, i); this.currentModel.modelName = r; } if (this.currentModel.modelName == 'PlayerModel') { this.currentModel.updateSwingingWeapon = function (useThisIfUrGay, radius) { var currentModel = this; void 0 === radius && (radius = 100); return function (tick, currentTick) { var interpolatedYaw = game.interpolateYaw(currentTick.getTargetTick().aimingYaw, currentTick.getFromTick().aimingYaw); currentModel.weapon.setRotation(interpolatedYaw - tick.interpolatedYaw); if (tick.firingTick && (tick.firingTick !== currentModel.lastFiringTick || !currentModel.lastFiringAnimationDone)) { currentModel.lastFiringTick = tick.firingTick; currentModel.lastFiringAnimationDone = false; var msSinceTick = Game.currentGame.world.replicator.getMsSinceTick(tick.firingTick), itemSchema = game.ui.itemSchema, _a = Math.min(msSinceTick / itemSchema[currentModel.lastWeaponName].msBetweenFiresTiers[currentModel.lastWeaponTier - 1], 1), _b = Math.sin(_a * Math.PI) * radius // itemSchema[currentModel.lastWeaponName].maxYawDeviationTiers[currentModel.lastWeaponTier - 1]; 1 === _a && (currentModel.lastFiringAnimationDone = true); currentModel.weapon.setRotation(interpolatedYaw - tick.interpolatedYaw - _b); currentModel.hat && currentModel.hat.setRotation(interpolatedYaw - tick.interpolatedYaw - 0.6 * _b); } } } /* this.currentModel.updateRotationWithLocalData = function(entity, yaw) { if (entity.isLocal() || yaw) { entity.getTargetTick().aimingYaw = entity.getFromTick().aimingYaw = yaw || game.inputPacketCreator.getLastAnyYaw(); } } */ }; this.currentModel.setParent(this); this.setNode(this.currentModel.getNode()); } addMissingTickFields(t, e) { for (var r in e) { var i = e[r]; r in t || (t[r] = i); } } }; game.renderer.textSpriteType = class s extends window.PIXI.Text { constructor() { super(); } renderWebGL(t) { this.updateText(true); this.calculateVertices(); t.setObjectRenderer(t.plugins[this.pluginName]); t.plugins[this.pluginName].render(this); } _renderCanvas(t) { this.updateText(true); t.plugins[this.pluginName].render(this); } }; game.renderer.textType = class a extends game.renderer.nodeType { constructor(t, e, r) { super(); this.text = new game.renderer.textSpriteType(t, {fontFamily: e, fontSize: r, lineJoin: "round", padding: 10}); this.text.resolution = 2 * window.devicePixelRatio; this.setNode(this.text); } setColor(t, e, r) { this.text.style.fill = t << 16 | e << 8 | r; } setStroke(t, e, r, i) { this.text.style.stroke = t << 16 | e << 8 | r; this.text.style.strokeThickness = i; } setFontWeight(t) { this.text.style.fontWeight = t; } setLetterSpacing(t) { this.text.style.letterSpacing = t; } setAnchor(t, e) { this.text.anchor.set(t, e); } setString(t) { this.text.text = t; } } Game.currentGame.world.createEntity = function(t) { if (this.entities[t.uid]) return; let e; if (Game.currentGame.getNetworkEntityPooling() && this._networkEntityPool.length > 0) { e = this._networkEntityPool.shift(); e.setTargetTick(t); e.uid = t.uid; } else { e = new game.renderer.entityType(t); } let shouldLoadModel = true; if (t.model.indexOf("Zombie") > -1 && !game.script.optimize.zombieSprite) shouldLoadModel = false; if (t.model in game.ui.buildingSchema && !game.script.optimize.towerSprite) shouldLoadModel = false; if (t.entityClass == "Projectile" && !game.script.optimize.projectileSprite) shouldLoadModel = false; shouldLoadModel && e.refreshModel(t.model); t.uid === this.myUid && (this.localPlayer.setEntity(e), this.renderer.follow(e)); this.entities[t.uid] = e; this.renderer.add(e, t.entityClass); this.entityGrid.updateEntity(this.entities[t.uid]); game.script.stashIndicators.onEntityCreated(t); }; Game.currentGame.world.removeEntity = function(t) { if (["Tree", "Stone", "NeutralCamp"].indexOf(this.entities[t].fromTick.model) > -1) return; game.script.showAoe.onEntityRemoved(t); this.renderer.remove(this.entities[t]); this.entityGrid.removeEntity(parseInt(t)); if (this.entities[t].currentModel) { const model = this.entities[t].currentModel; if (Game.currentGame.getModelEntityPooling(model.modelName)) { model.reset(); this.modelEntityPool[model.modelName].push(model); }; }; if (Game.currentGame.getNetworkEntityPooling()) { this.entities[t].reset(); this._networkEntityPool.push(this.entities[t]); } delete this.entities[t]; }; Game.currentGame.renderer.add = function(t, e) { if (t instanceof game.renderer.entityType) { switch (e) { case "Prop": this.scenery.addAttachment(t); break; case "Projectile": this.projectiles.addAttachment(t); break; case "Player": this.players.addAttachment(t); break; default: this.npcs.addAttachment(t); } // } else t instanceof game.renderer.textType ? this.ui.addAttachment(t) : this.ground.addAttachment(t); } else if (!game.script.optimize.background) { if (t instanceof game.renderer.nodeType) Game.currentGame.renderer.ground.addAttachment(t); else { if (!(t instanceof game.renderer.textType)) return; // throw new Error("Unhandled object: " + JSON.stringify(t)); Game.currentGame.renderer.ui.addAttachment(t); } } else t instanceof game.renderer.textType ? this.ui.addAttachment(t) : this.ground.addAttachment(t); }; Game.currentGame.renderer.remove = function(t) { if (t instanceof game.renderer.entityType) { switch (t.entityClass) { case "Prop": this.scenery.removeAttachment(t); break; case "Projectile": this.projectiles.removeAttachment(t); break; case "Player": this.players.removeAttachment(t); break; default: this.npcs.removeAttachment(t); } // } else t instanceof game.renderer.textType ? this.ui.removeAttachment(t) : this.ground.removeAttachment(t); } else if (!game.script.optimize.background) { t instanceof game.renderer.nodeType ? Game.currentGame.renderer.ground.removeAttachment(t) : t instanceof game.renderer.textType && Game.currentGame.renderer.ui.removeAttachment(t); } else t instanceof game.renderer.textType ? this.ui.removeAttachment(t) : this.ground.removeAttachment(t); }; Game.currentGame.renderer.tickCallbacks[7] = () => { const t = Game.currentGame.world.replicator.getMsInThisTick(); for (const e in Game.currentGame.world.entities) { Game.currentGame.world.entities[e].tick(t, Game.currentGame.world.replicator.msPerTick); }; }; } else { Game.currentGame.world._createEntity = Game.currentGame.world.createEntity; Game.currentGame.world.createEntity = function(t) { if (this.entities[t.uid]) return; this._createEntity(t); game.script.stashIndicators.onEntityCreated(t); }; Game.currentGame.world._removeEntity = Game.currentGame.world.removeEntity; Game.currentGame.world.removeEntity = function(t) { if (["Tree", "Stone", "NeutralCamp"].indexOf(this.entities[t].fromTick.model) > -1) return; game.script.showAoe.onEntityRemoved(t); this._removeEntity(t); }; }; game.assetManager.models ||= {}; game.assetManager.models.rangeIndicatorModel = function(e, innerRGB = {r: 0xc8, g: 0xa0, b: 0x0}, borderRGB = {r: 0xff, g: 0xc8, b: 0x0}, lineWidth = 8) { const container = new game.renderer.nodeType(); container.isCircular = e.isCircular || false; container.goldRegion = new game.renderer.graphicsType(); container.goldRegion.setAlpha(0.1); if (container.isCircular) { container.goldRegion.drawCircle(0, 0, e.radius, innerRGB, borderRGB, lineWidth); } else { container.goldRegion.drawRect(-e.width / 2, -e.height / 2, e.width / 2, e.height / 2, innerRGB, borderRGB, lineWidth); }; container.addAttachment(container.goldRegion); return container; }; /* @Misc. */ document.getElementsByClassName('hud-party-tag')[0].setAttribute('maxlength', 49); document.getElementsByClassName('hud-intro-name')[0].setAttribute('maxlength', 29); /* @Bindings */ (function MapFunctionsToElem() { for (let page in menu) { const [optionsElem, moreElem] = document.querySelectorAll("#" + page + " > div"); for (let option in menu[page]) { const {name, description, more, isToggle, callback, customButtonText, onCallback, offCallback} = menu[page][option]; const hasMore = !!more; const itemElem = document.createElement("div"); itemElem.id = option + "-div"; itemElem.innerHTML = `

    ${name}

    ${description} ${game.options.options[option] === undefined ? "" : ` `} ${isToggle === false ? `` : ""} ${hasMore ? `` : ""} `; optionsElem.appendChild(itemElem); if (hasMore) { const {html, functions, bind} = more; const moreContainer = document.createElement("div"); moreContainer.innerHTML = html; moreContainer.style.display = "none"; moreElem.appendChild(moreContainer); functions?.(); const toggleElem = getId("more-" + option); toggleElem.onclick = () => { refreshMore(page); bind?.(); moreContainer.style.display = "block"; } } if (!(game.options.options[option] === undefined)) { addFunctionToElem({id: 'toggle-' + option, option, buttonText: '', colors: "underline-red?", isToggle, callback, onCallback, offCallback}); } else if (isToggle === false) { addFunctionToElem({id: name, option, buttonText: '', colors: "underline-red?", isToggle, callback, onCallback, offCallback}); }; }; }; })(); (function MapHandlers() { for (const functionName in game.script) game.script[functionName]?.init?.(); for (const subFunctionName in game.script.sockets) { if (typeof game.script.sockets[subFunctionName] == "object") {} // && (game.script.sockets[subFunctionName].parent = game.script.sockets); }; const keybindListeners = {}; for (const functionName in game.options.options) { if (!game.script[functionName] || game.script[functionName].handlers === undefined) continue; const usesHandlers = game.script[functionName].handlers; for (const {type, names} of usesHandlers) { console.log(type, functionName, names); switch(type) { case "rpc": for (const name of names) { game.network.addRpcHandler(name, game.script[functionName][name].bind(game.script[functionName])); }; break; case "entityUpdate": game.network.addEntityUpdateHandler(game.script[functionName][names].bind(game.script[functionName])); break; case "tickUpdate": game.ui._events[names].push(game.script[functionName][names].bind(game.script[functionName][names])); break; case "keybind": if (typeof names == "object") { for (const name of names) { keybindListeners[name] ||= []; keybindListeners[name].push(functionName); }; } else { keybindListeners[names] ||= []; keybindListeners[names].push(functionName); }; break; case "packetFunc": game.network[names[0]] = function(data) { game.script[functionName][names[1]](data); this.sendPacket(names[2], data); }; break; }; }; }; for (const name in keybindListeners) { document.addEventListener(name, function(e) { if (document.activeElement.tagName.toLowerCase() !== "input" && document.activeElement.tagName.toLowerCase() !== "textarea") { for (const functionName of keybindListeners[name]) game.script[functionName][name](e); }; }); }; })();