Greasy Fork is available in English.

gimkitcheat

A userscript that allows you to cheat across various gimkit games

// ==UserScript==
// @name        gimkitcheat
// @description A userscript that allows you to cheat across various gimkit games
// @namespace   https://www.github.com/TheLazySquid/GimkitCheat
// @match       https://www.gimkit.com/join*
// @run-at      document-start
// @iconURL     https://www.gimkit.com/favicon.png
// @author      TheLazySquid
// @version     0.3.5
// @license     ISC
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// ==/UserScript==
(function () {
  'use strict';

  var version = "0.3.5";

  function utf8Read$1(bytes, offset, length) {
  	var string = '', chr = 0;
  	for (var i = offset, end = offset + length; i < end; i++) {
  		var byte = bytes[i];
  		if ((byte & 0x80) === 0x00) {
  			string += String.fromCharCode(byte);
  			continue;
  		}
  		if ((byte & 0xe0) === 0xc0) {
  			string += String.fromCharCode(
  				((byte & 0x1f) << 6) |
  				(bytes[++i] & 0x3f)
  			);
  			continue;
  		}
  		if ((byte & 0xf0) === 0xe0) {
  			string += String.fromCharCode(
  				((byte & 0x0f) << 12) |
  				((bytes[++i] & 0x3f) << 6) |
  				((bytes[++i] & 0x3f) << 0)
  			);
  			continue;
  		}
  		if ((byte & 0xf8) === 0xf0) {
  			chr = ((byte & 0x07) << 18) |
  				((bytes[++i] & 0x3f) << 12) |
  				((bytes[++i] & 0x3f) << 6) |
  				((bytes[++i] & 0x3f) << 0);
  			if (chr >= 0x010000) { // surrogate pair
  				chr -= 0x010000;
  				string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
  			} else {
  				string += String.fromCharCode(chr);
  			}
  			continue;
  		}

  		console.error('Invalid byte ' + byte.toString(16));
  		// (do not throw error to avoid server/client from crashing due to hack attemps)
  		// throw new Error('Invalid byte ' + byte.toString(16));
  	}
  	return string;
  }

  function int8(bytes, it) {
  	return uint8(bytes, it) << 24 >> 24;
  }
  function uint8(bytes, it) {
  	return bytes[it.offset++];
  }
  function int16(bytes, it) {
  	return uint16(bytes, it) << 16 >> 16;
  }
  function uint16(bytes, it) {
  	return bytes[it.offset++] | bytes[it.offset++] << 8;
  }
  function int32(bytes, it) {
  	return bytes[it.offset++] | bytes[it.offset++] << 8 | bytes[it.offset++] << 16 | bytes[it.offset++] << 24;
  }
  function uint32(bytes, it) {
  	return int32(bytes, it) >>> 0;
  }
  function int64(bytes, it) {
  	const low = uint32(bytes, it);
  	const high = int32(bytes, it) * Math.pow(2, 32);
  	return high + low;
  }
  function uint64(bytes, it) {
  	const low = uint32(bytes, it);
  	const high = uint32(bytes, it) * Math.pow(2, 32);
  	return high + low;
  }const _int32 = new Int32Array(2);
  const _float32 = new Float32Array(_int32.buffer);
  const _float64 = new Float64Array(_int32.buffer);

  function readFloat32(bytes, it) {
  	_int32[0] = int32(bytes, it);
  	return _float32[0];
  }
  function readFloat64(bytes, it) {
  	_int32[0 ] = int32(bytes, it);
  	_int32[1 ] = int32(bytes, it);
  	return _float64[0];
  }
  function string(bytes, it) {
  	const prefix = bytes[it.offset++];
  	let length;

  	if (prefix < 0xc0) {
  		// fixstr
  		length = prefix & 0x1f;

  	} else if (prefix === 0xd9) {
  		length = uint8(bytes, it);

  	} else if (prefix === 0xda) {
  		length = uint16(bytes, it);

  	} else if (prefix === 0xdb) {
  		length = uint32(bytes, it);
  	}

  	const value = utf8Read$1(bytes, it.offset, length);
  	it.offset += length;

  	return value;
  }

  function stringCheck(bytes, it) {
  	const prefix = bytes[it.offset];
  	return (
  		// fixstr
  		(prefix < 0xc0 && prefix > 0xa0) ||
  		// str 8
  		prefix === 0xd9 ||
  		// str 16
  		prefix === 0xda ||
  		// str 32
  		prefix === 0xdb
  	);
  }

  function number(bytes, it) {
  	const prefix = bytes[it.offset++];

  	if (prefix < 0x80) {
  		// positive fixint
  		return prefix;

  	} else if (prefix === 0xca) {
  		// float 32
  		return readFloat32(bytes, it);

  	} else if (prefix === 0xcb) {
  		// float 64
  		return readFloat64(bytes, it);

  	} else if (prefix === 0xcc) {
  		// uint 8
  		return uint8(bytes, it);

  	} else if (prefix === 0xcd) {
  		// uint 16
  		return uint16(bytes, it);

  	} else if (prefix === 0xce) {
  		// uint 32
  		return uint32(bytes, it);

  	} else if (prefix === 0xcf) {
  		// uint 64
  		return uint64(bytes, it);

  	} else if (prefix === 0xd0) {
  		// int 8
  		return int8(bytes, it);

  	} else if (prefix === 0xd1) {
  		// int 16
  		return int16(bytes, it);

  	} else if (prefix === 0xd2) {
  		// int 32
  		return int32(bytes, it);

  	} else if (prefix === 0xd3) {
  		// int 64
  		return int64(bytes, it);

  	} else if (prefix > 0xdf) {
  		// negative fixint
  		return (0xff - prefix + 1) * -1
  	}
  }

  const Protocol = {
      // Room-related (10~19)
      JOIN_ROOM: 10,
      ERROR: 11,
      LEAVE_ROOM: 12,
      ROOM_DATA: 13,
      ROOM_STATE: 14,
      ROOM_STATE_PATCH: 15,
      ROOM_DATA_SCHEMA: 16
  };

  function Decoder(buffer, offset) {
      this._offset = offset;
      if (buffer instanceof ArrayBuffer) {
          this._buffer = buffer;
          this._view = new DataView(this._buffer);
      }
      else if (ArrayBuffer.isView(buffer)) {
          this._buffer = buffer.buffer;
          this._view = new DataView(this._buffer, buffer.byteOffset, buffer.byteLength);
      }
      else {
          throw new Error('Invalid argument');
      }
  }
  function utf8Read(view, offset, length) {
      var string = '', chr = 0;
      for (var i = offset, end = offset + length; i < end; i++) {
          var byte = view.getUint8(i);
          if ((byte & 0x80) === 0x00) {
              string += String.fromCharCode(byte);
              continue;
          }
          if ((byte & 0xe0) === 0xc0) {
              string += String.fromCharCode(((byte & 0x1f) << 6) |
                  (view.getUint8(++i) & 0x3f));
              continue;
          }
          if ((byte & 0xf0) === 0xe0) {
              string += String.fromCharCode(((byte & 0x0f) << 12) |
                  ((view.getUint8(++i) & 0x3f) << 6) |
                  ((view.getUint8(++i) & 0x3f) << 0));
              continue;
          }
          if ((byte & 0xf8) === 0xf0) {
              chr = ((byte & 0x07) << 18) |
                  ((view.getUint8(++i) & 0x3f) << 12) |
                  ((view.getUint8(++i) & 0x3f) << 6) |
                  ((view.getUint8(++i) & 0x3f) << 0);
              if (chr >= 0x010000) { // surrogate pair
                  chr -= 0x010000;
                  string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
              }
              else {
                  string += String.fromCharCode(chr);
              }
              continue;
          }
          throw new Error('Invalid byte ' + byte.toString(16));
      }
      return string;
  }
  Decoder.prototype._array = function (length) {
      var value = new Array(length);
      for (var i = 0; i < length; i++) {
          value[i] = this._parse();
      }
      return value;
  };
  Decoder.prototype._map = function (length) {
      var key = '', value = {};
      for (var i = 0; i < length; i++) {
          key = this._parse();
          value[key] = this._parse();
      }
      return value;
  };
  Decoder.prototype._str = function (length) {
      var value = utf8Read(this._view, this._offset, length);
      this._offset += length;
      return value;
  };
  Decoder.prototype._bin = function (length) {
      var value = this._buffer.slice(this._offset, this._offset + length);
      this._offset += length;
      return value;
  };
  Decoder.prototype._parse = function () {
      var prefix = this._view.getUint8(this._offset++);
      var value, length = 0, type = 0, hi = 0, lo = 0;
      if (prefix < 0xc0) {
          // positive fixint
          if (prefix < 0x80) {
              return prefix;
          }
          // fixmap
          if (prefix < 0x90) {
              return this._map(prefix & 0x0f);
          }
          // fixarray
          if (prefix < 0xa0) {
              return this._array(prefix & 0x0f);
          }
          // fixstr
          return this._str(prefix & 0x1f);
      }
      // negative fixint
      if (prefix > 0xdf) {
          return (0xff - prefix + 1) * -1;
      }
      switch (prefix) {
          // nil
          case 0xc0:
              return null;
          // false
          case 0xc2:
              return false;
          // true
          case 0xc3:
              return true;
          // bin
          case 0xc4:
              length = this._view.getUint8(this._offset);
              this._offset += 1;
              return this._bin(length);
          case 0xc5:
              length = this._view.getUint16(this._offset);
              this._offset += 2;
              return this._bin(length);
          case 0xc6:
              length = this._view.getUint32(this._offset);
              this._offset += 4;
              return this._bin(length);
          // ext
          case 0xc7:
              length = this._view.getUint8(this._offset);
              type = this._view.getInt8(this._offset + 1);
              this._offset += 2;
              return [type, this._bin(length)];
          case 0xc8:
              length = this._view.getUint16(this._offset);
              type = this._view.getInt8(this._offset + 2);
              this._offset += 3;
              return [type, this._bin(length)];
          case 0xc9:
              length = this._view.getUint32(this._offset);
              type = this._view.getInt8(this._offset + 4);
              this._offset += 5;
              return [type, this._bin(length)];
          // float
          case 0xca:
              value = this._view.getFloat32(this._offset);
              this._offset += 4;
              return value;
          case 0xcb:
              value = this._view.getFloat64(this._offset);
              this._offset += 8;
              return value;
          // uint
          case 0xcc:
              value = this._view.getUint8(this._offset);
              this._offset += 1;
              return value;
          case 0xcd:
              value = this._view.getUint16(this._offset);
              this._offset += 2;
              return value;
          case 0xce:
              value = this._view.getUint32(this._offset);
              this._offset += 4;
              return value;
          case 0xcf:
              hi = this._view.getUint32(this._offset) * Math.pow(2, 32);
              lo = this._view.getUint32(this._offset + 4);
              this._offset += 8;
              return hi + lo;
          // int
          case 0xd0:
              value = this._view.getInt8(this._offset);
              this._offset += 1;
              return value;
          case 0xd1:
              value = this._view.getInt16(this._offset);
              this._offset += 2;
              return value;
          case 0xd2:
              value = this._view.getInt32(this._offset);
              this._offset += 4;
              return value;
          case 0xd3:
              hi = this._view.getInt32(this._offset) * Math.pow(2, 32);
              lo = this._view.getUint32(this._offset + 4);
              this._offset += 8;
              return hi + lo;
          // fixext
          case 0xd4:
              type = this._view.getInt8(this._offset);
              this._offset += 1;
              if (type === 0x00) {
                  this._offset += 1;
                  return void 0;
              }
              return [type, this._bin(1)];
          case 0xd5:
              type = this._view.getInt8(this._offset);
              this._offset += 1;
              return [type, this._bin(2)];
          case 0xd6:
              type = this._view.getInt8(this._offset);
              this._offset += 1;
              return [type, this._bin(4)];
          case 0xd7:
              type = this._view.getInt8(this._offset);
              this._offset += 1;
              if (type === 0x00) {
                  hi = this._view.getInt32(this._offset) * Math.pow(2, 32);
                  lo = this._view.getUint32(this._offset + 4);
                  this._offset += 8;
                  return new Date(hi + lo);
              }
              return [type, this._bin(8)];
          case 0xd8:
              type = this._view.getInt8(this._offset);
              this._offset += 1;
              return [type, this._bin(16)];
          // str
          case 0xd9:
              length = this._view.getUint8(this._offset);
              this._offset += 1;
              return this._str(length);
          case 0xda:
              length = this._view.getUint16(this._offset);
              this._offset += 2;
              return this._str(length);
          case 0xdb:
              length = this._view.getUint32(this._offset);
              this._offset += 4;
              return this._str(length);
          // array
          case 0xdc:
              length = this._view.getUint16(this._offset);
              this._offset += 2;
              return this._array(length);
          case 0xdd:
              length = this._view.getUint32(this._offset);
              this._offset += 4;
              return this._array(length);
          // map
          case 0xde:
              length = this._view.getUint16(this._offset);
              this._offset += 2;
              return this._map(length);
          case 0xdf:
              length = this._view.getUint32(this._offset);
              this._offset += 4;
              return this._map(length);
      }
      throw new Error('Could not parse');
  };
  function decode$1(buffer, offset) {
      if (offset === void 0) { offset = 0; }
      var decoder = new Decoder(buffer, offset);
      var value = decoder._parse();
      if (decoder._offset !== buffer.byteLength) {
          throw new Error((buffer.byteLength - decoder._offset) + ' trailing bytes');
      }
      return value;
  }
  // 
  // ENCODER
  // 
  function utf8Write(view, offset, str) {
      var c = 0;
      for (var i = 0, l = str.length; i < l; i++) {
          c = str.charCodeAt(i);
          if (c < 0x80) {
              view.setUint8(offset++, c);
          }
          else if (c < 0x800) {
              view.setUint8(offset++, 0xc0 | (c >> 6));
              view.setUint8(offset++, 0x80 | (c & 0x3f));
          }
          else if (c < 0xd800 || c >= 0xe000) {
              view.setUint8(offset++, 0xe0 | (c >> 12));
              view.setUint8(offset++, 0x80 | (c >> 6) & 0x3f);
              view.setUint8(offset++, 0x80 | (c & 0x3f));
          }
          else {
              i++;
              c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
              view.setUint8(offset++, 0xf0 | (c >> 18));
              view.setUint8(offset++, 0x80 | (c >> 12) & 0x3f);
              view.setUint8(offset++, 0x80 | (c >> 6) & 0x3f);
              view.setUint8(offset++, 0x80 | (c & 0x3f));
          }
      }
  }
  function utf8Length(str) {
      var c = 0, length = 0;
      for (var i = 0, l = str.length; i < l; i++) {
          c = str.charCodeAt(i);
          if (c < 0x80) {
              length += 1;
          }
          else if (c < 0x800) {
              length += 2;
          }
          else if (c < 0xd800 || c >= 0xe000) {
              length += 3;
          }
          else {
              i++;
              length += 4;
          }
      }
      return length;
  }
  function _encode(bytes, defers, value) {
      var type = typeof value, i = 0, l = 0, hi = 0, lo = 0, length = 0, size = 0;
      if (type === 'string') {
          length = utf8Length(value);
          // fixstr
          if (length < 0x20) {
              bytes.push(length | 0xa0);
              size = 1;
          }
          // str 8
          else if (length < 0x100) {
              bytes.push(0xd9, length);
              size = 2;
          }
          // str 16
          else if (length < 0x10000) {
              bytes.push(0xda, length >> 8, length);
              size = 3;
          }
          // str 32
          else if (length < 0x100000000) {
              bytes.push(0xdb, length >> 24, length >> 16, length >> 8, length);
              size = 5;
          }
          else {
              throw new Error('String too long');
          }
          defers.push({ _str: value, _length: length, _offset: bytes.length });
          return size + length;
      }
      if (type === 'number') {
          // TODO: encode to float 32?
          // float 64
          if (Math.floor(value) !== value || !isFinite(value)) {
              bytes.push(0xcb);
              defers.push({ _float: value, _length: 8, _offset: bytes.length });
              return 9;
          }
          if (value >= 0) {
              // positive fixnum
              if (value < 0x80) {
                  bytes.push(value);
                  return 1;
              }
              // uint 8
              if (value < 0x100) {
                  bytes.push(0xcc, value);
                  return 2;
              }
              // uint 16
              if (value < 0x10000) {
                  bytes.push(0xcd, value >> 8, value);
                  return 3;
              }
              // uint 32
              if (value < 0x100000000) {
                  bytes.push(0xce, value >> 24, value >> 16, value >> 8, value);
                  return 5;
              }
              // uint 64
              hi = (value / Math.pow(2, 32)) >> 0;
              lo = value >>> 0;
              bytes.push(0xcf, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo);
              return 9;
          }
          else {
              // negative fixnum
              if (value >= -0x20) {
                  bytes.push(value);
                  return 1;
              }
              // int 8
              if (value >= -0x80) {
                  bytes.push(0xd0, value);
                  return 2;
              }
              // int 16
              if (value >= -0x8000) {
                  bytes.push(0xd1, value >> 8, value);
                  return 3;
              }
              // int 32
              if (value >= -0x80000000) {
                  bytes.push(0xd2, value >> 24, value >> 16, value >> 8, value);
                  return 5;
              }
              // int 64
              hi = Math.floor(value / Math.pow(2, 32));
              lo = value >>> 0;
              bytes.push(0xd3, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo);
              return 9;
          }
      }
      if (type === 'object') {
          // nil
          if (value === null) {
              bytes.push(0xc0);
              return 1;
          }
          if (Array.isArray(value)) {
              length = value.length;
              // fixarray
              if (length < 0x10) {
                  bytes.push(length | 0x90);
                  size = 1;
              }
              // array 16
              else if (length < 0x10000) {
                  bytes.push(0xdc, length >> 8, length);
                  size = 3;
              }
              // array 32
              else if (length < 0x100000000) {
                  bytes.push(0xdd, length >> 24, length >> 16, length >> 8, length);
                  size = 5;
              }
              else {
                  throw new Error('Array too large');
              }
              for (i = 0; i < length; i++) {
                  size += _encode(bytes, defers, value[i]);
              }
              return size;
          }
          // fixext 8 / Date
          if (value instanceof Date) {
              var time = value.getTime();
              hi = Math.floor(time / Math.pow(2, 32));
              lo = time >>> 0;
              bytes.push(0xd7, 0, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo);
              return 10;
          }
          if (value instanceof ArrayBuffer) {
              length = value.byteLength;
              // bin 8
              if (length < 0x100) {
                  bytes.push(0xc4, length);
                  size = 2;
              }
              else 
              // bin 16
              if (length < 0x10000) {
                  bytes.push(0xc5, length >> 8, length);
                  size = 3;
              }
              else 
              // bin 32
              if (length < 0x100000000) {
                  bytes.push(0xc6, length >> 24, length >> 16, length >> 8, length);
                  size = 5;
              }
              else {
                  throw new Error('Buffer too large');
              }
              defers.push({ _bin: value, _length: length, _offset: bytes.length });
              return size + length;
          }
          if (typeof value.toJSON === 'function') {
              return _encode(bytes, defers, value.toJSON());
          }
          var keys = [], key = '';
          var allKeys = Object.keys(value);
          for (i = 0, l = allKeys.length; i < l; i++) {
              key = allKeys[i];
              if (typeof value[key] !== 'function') {
                  keys.push(key);
              }
          }
          length = keys.length;
          // fixmap
          if (length < 0x10) {
              bytes.push(length | 0x80);
              size = 1;
          }
          // map 16
          else if (length < 0x10000) {
              bytes.push(0xde, length >> 8, length);
              size = 3;
          }
          // map 32
          else if (length < 0x100000000) {
              bytes.push(0xdf, length >> 24, length >> 16, length >> 8, length);
              size = 5;
          }
          else {
              throw new Error('Object too large');
          }
          for (i = 0; i < length; i++) {
              key = keys[i];
              size += _encode(bytes, defers, key);
              size += _encode(bytes, defers, value[key]);
          }
          return size;
      }
      // false/true
      if (type === 'boolean') {
          bytes.push(value ? 0xc3 : 0xc2);
          return 1;
      }
      // fixext 1 / undefined
      if (type === 'undefined') {
          bytes.push(0xd4, 0, 0);
          return 3;
      }
      throw new Error('Could not encode');
  }
  function encode$1(value) {
      var bytes = [];
      var defers = [];
      var size = _encode(bytes, defers, value);
      var buf = new ArrayBuffer(size);
      var view = new DataView(buf);
      var deferIndex = 0;
      var deferWritten = 0;
      var nextOffset = -1;
      if (defers.length > 0) {
          nextOffset = defers[0]._offset;
      }
      var defer, deferLength = 0, offset = 0;
      for (var i = 0, l = bytes.length; i < l; i++) {
          view.setUint8(deferWritten + i, bytes[i]);
          if (i + 1 !== nextOffset) {
              continue;
          }
          defer = defers[deferIndex];
          deferLength = defer._length;
          offset = deferWritten + nextOffset;
          if (defer._bin) {
              var bin = new Uint8Array(defer._bin);
              for (var j = 0; j < deferLength; j++) {
                  view.setUint8(offset + j, bin[j]);
              }
          }
          else if (defer._str) {
              utf8Write(view, offset, defer._str);
          }
          else if (defer._float !== undefined) {
              view.setFloat64(offset, defer._float);
          }
          deferIndex++;
          deferWritten += deferLength;
          if (defers[deferIndex]) {
              nextOffset = defers[deferIndex]._offset;
          }
      }
      return buf;
  }

  function decodeExport(packet) {
      const u8arr = new Uint8Array(packet);
      const bytes = Array.from(u8arr);
      const prefix = bytes[0];

      if(prefix == Protocol.ROOM_DATA) {
          let it = { offset: 1 };

          stringCheck(bytes, it) ? string(bytes, it) : number(bytes, it);
          let parsed = decode$1(packet, it.offset);
          return parsed
      } else {
          return null; // hopefully isn't important lol
      }
  }

  function encodeExport(channel, packet) {
      let header = [Protocol.ROOM_DATA];
      let channelEncoded = encode$1(channel);
      let packetEncoded = encode$1(packet);

      // combine the arraybuffers
      let combined = new Uint8Array(channelEncoded.byteLength + packetEncoded.byteLength + header.length);
      combined.set(header);
      combined.set(new Uint8Array(channelEncoded), header.length);
      combined.set(new Uint8Array(packetEncoded), header.length + channelEncoded.byteLength);

      return combined.buffer
  }

  var colyseus = {
      decode: decodeExport,
      encode: encodeExport
  };

  // this code was stolen from the original Gimkit Util extension
  function n(t, e, n) {
      for (var i = 0, s = 0, o = n.length; s < o; s++)(i = n.charCodeAt(s)) < 128 ? t.setUint8(e++, i) : (i < 2048 ? t.setUint8(e++, 192 | i >> 6) : (i < 55296 || 57344 <= i ? t.setUint8(e++, 224 | i >> 12) : (s++, i = 65536 + ((1023 & i) << 10 | 1023 & n.charCodeAt(s)), t.setUint8(e++, 240 | i >> 18), t.setUint8(e++, 128 | i >> 12 & 63)), t.setUint8(e++, 128 | i >> 6 & 63)), t.setUint8(e++, 128 | 63 & i));
  }

  function encode(t, e, s) {
      const o = {
          type: 2,
          data: ["blueboat_SEND_MESSAGE", {
              room: s,
              key: t,
              data: e
          }],
          options: {
              compress: !0
          },
          nsp: "/"
      };
      return function(t) {
          var e = [],
              i = [],
              s = function t(e, n, i) {
                  var s = typeof i,
                      o = 0,
                      r = 0,
                      a = 0,
                      c = 0,
                      l = 0,
                      u = 0;
                  if ("string" === s) {
                      if ((l = function(t) {
                              for (var e = 0, n = 0, i = 0, s = t.length; i < s; i++)(e = t.charCodeAt(i)) < 128 ? n += 1 : e < 2048 ? n += 2 : e < 55296 || 57344 <= e ? n += 3 : (i++, n += 4);
                              return n
                          }(i)) < 32) e.push(160 | l), u = 1;
                      else if (l < 256) e.push(217, l), u = 2;
                      else if (l < 65536) e.push(218, l >> 8, l), u = 3;
                      else {
                          if (!(l < 4294967296)) throw new Error("String too long");
                          e.push(219, l >> 24, l >> 16, l >> 8, l), u = 5;
                      }
                      return n.push({
                          h: i,
                          u: l,
                          t: e.length
                      }), u + l
                  }
                  if ("number" === s) return Math.floor(i) === i && isFinite(i) ? 0 <= i ? i < 128 ? (e.push(i), 1) : i < 256 ? (e.push(204, i), 2) : i < 65536 ? (e.push(205, i >> 8, i), 3) : i < 4294967296 ? (e.push(206, i >> 24, i >> 16, i >> 8, i), 5) : (a = i / Math.pow(2, 32) >> 0, c = i >>> 0, e.push(207, a >> 24, a >> 16, a >> 8, a, c >> 24, c >> 16, c >> 8, c), 9) : -32 <= i ? (e.push(i), 1) : -128 <= i ? (e.push(208, i), 2) : -32768 <= i ? (e.push(209, i >> 8, i), 3) : -2147483648 <= i ? (e.push(210, i >> 24, i >> 16, i >> 8, i), 5) : (a = Math.floor(i / Math.pow(2, 32)), c = i >>> 0, e.push(211, a >> 24, a >> 16, a >> 8, a, c >> 24, c >> 16, c >> 8, c), 9) : (e.push(203), n.push({
                      o: i,
                      u: 8,
                      t: e.length
                  }), 9);
                  if ("object" === s) {
                      if (null === i) return e.push(192), 1;
                      if (Array.isArray(i)) {
                          if ((l = i.length) < 16) e.push(144 | l), u = 1;
                          else if (l < 65536) e.push(220, l >> 8, l), u = 3;
                          else {
                              if (!(l < 4294967296)) throw new Error("Array too large");
                              e.push(221, l >> 24, l >> 16, l >> 8, l), u = 5;
                          }
                          for (o = 0; o < l; o++) u += t(e, n, i[o]);
                          return u
                      }
                      if (i instanceof Date) {
                          var h = i.getTime();
                          return a = Math.floor(h / Math.pow(2, 32)), c = h >>> 0, e.push(215, 0, a >> 24, a >> 16, a >> 8, a, c >> 24, c >> 16, c >> 8, c), 10
                      }
                      if (i instanceof ArrayBuffer) {
                          if ((l = i.byteLength) < 256) e.push(196, l), u = 2;
                          else if (l < 65536) e.push(197, l >> 8, l), u = 3;
                          else {
                              if (!(l < 4294967296)) throw new Error("Buffer too large");
                              e.push(198, l >> 24, l >> 16, l >> 8, l), u = 5;
                          }
                          return n.push({
                              l: i,
                              u: l,
                              t: e.length
                          }), u + l
                      }
                      if ("function" == typeof i.toJSON) return t(e, n, i.toJSON());
                      var d = [],
                          f = "",
                          p = Object.keys(i);
                      for (o = 0, r = p.length; o < r; o++) "function" != typeof i[f = p[o]] && d.push(f);
                      if ((l = d.length) < 16) e.push(128 | l), u = 1;
                      else if (l < 65536) e.push(222, l >> 8, l), u = 3;
                      else {
                          if (!(l < 4294967296)) throw new Error("Object too large");
                          e.push(223, l >> 24, l >> 16, l >> 8, l), u = 5;
                      }
                      for (o = 0; o < l; o++) u += t(e, n, f = d[o]), u += t(e, n, i[f]);
                      return u
                  }
                  if ("boolean" === s) return e.push(i ? 195 : 194), 1;
                  if ("undefined" === s) return e.push(212, 0, 0), 3;
                  throw new Error("Could not encode")
              }(e, i, t),
              o = new ArrayBuffer(s),
              r = new DataView(o),
              a = 0,
              c = 0,
              l = -1;
          0 < i.length && (l = i[0].t);
          for (var u, h = 0, d = 0, f = 0, p = e.length; f < p; f++)
              if (r.setUint8(c + f, e[f]), f + 1 === l) {
                  if (h = (u = i[a]).u, d = c + l, u.l)
                      for (var g = new Uint8Array(u.l), E = 0; E < h; E++) r.setUint8(d + E, g[E]);
                  else u.h ? n(r, d, u.h) : void 0 !== u.o && r.setFloat64(d, u.o);
                  c += h, i[++a] && (l = i[a].t);
              } let y = Array.from(new Uint8Array(o));
          y.unshift(4);
          return new Uint8Array(y).buffer 
      }(o)
  }

  function decode(packet) {
      function e(t) {
          if (this.t = 0, t instanceof ArrayBuffer) this.i = t, this.s = new DataView(this.i);
          else {
              if (!ArrayBuffer.isView(t)) return null;
              this.i = t.buffer, this.s = new DataView(this.i, t.byteOffset, t.byteLength);
          }
      }

      e.prototype.g = function(t) {
          for (var e = new Array(t), n = 0; n < t; n++) e[n] = this.v();
          return e
      }, e.prototype.M = function(t) {
          for (var e = {}, n = 0; n < t; n++) e[this.v()] = this.v();
          return e
      }, e.prototype.h = function(t) {
          var e = function(t, e, n) {
              for (var i = "", s = 0, o = e, r = e + n; o < r; o++) {
                  var a = t.getUint8(o);
                  if (0 != (128 & a))
                      if (192 != (224 & a))
                          if (224 != (240 & a)) {
                              if (240 != (248 & a)) throw new Error("Invalid byte " + a.toString(16));
                              65536 <= (s = (7 & a) << 18 | (63 & t.getUint8(++o)) << 12 | (63 & t.getUint8(++o)) << 6 | (63 & t.getUint8(++o)) << 0) ? (s -= 65536, i += String.fromCharCode(55296 + (s >>> 10), 56320 + (1023 & s))) : i += String.fromCharCode(s);
                          } else i += String.fromCharCode((15 & a) << 12 | (63 & t.getUint8(++o)) << 6 | (63 & t.getUint8(++o)) << 0);
                  else i += String.fromCharCode((31 & a) << 6 | 63 & t.getUint8(++o));
                  else i += String.fromCharCode(a);
              }
              return i
          }(this.s, this.t, t);
          return this.t += t, e
      }, e.prototype.l = function(t) {
          var e = this.i.slice(this.t, this.t + t);
          return this.t += t, e
      }, e.prototype.v = function() {
          if(!this.s) return null;
          var t, e = this.s.getUint8(this.t++),
              n = 0,
              i = 0,
              s = 0,
              o = 0;
          if (e < 192) return e < 128 ? e : e < 144 ? this.M(15 & e) : e < 160 ? this.g(15 & e) : this.h(31 & e);
          if (223 < e) return -1 * (255 - e + 1);
          switch (e) {
              case 192:
                  return null;
              case 194:
                  return !1;
              case 195:
                  return !0;
              case 196:
                  return n = this.s.getUint8(this.t), this.t += 1, this.l(n);
              case 197:
                  return n = this.s.getUint16(this.t), this.t += 2, this.l(n);
              case 198:
                  return n = this.s.getUint32(this.t), this.t += 4, this.l(n);
              case 199:
                  return n = this.s.getUint8(this.t), i = this.s.getInt8(this.t + 1), this.t += 2, [i, this.l(n)];
              case 200:
                  return n = this.s.getUint16(this.t), i = this.s.getInt8(this.t + 2), this.t += 3, [i, this.l(n)];
              case 201:
                  return n = this.s.getUint32(this.t), i = this.s.getInt8(this.t + 4), this.t += 5, [i, this.l(n)];
              case 202:
                  return t = this.s.getFloat32(this.t), this.t += 4, t;
              case 203:
                  return t = this.s.getFloat64(this.t), this.t += 8, t;
              case 204:
                  return t = this.s.getUint8(this.t), this.t += 1, t;
              case 205:
                  return t = this.s.getUint16(this.t), this.t += 2, t;
              case 206:
                  return t = this.s.getUint32(this.t), this.t += 4, t;
              case 207:
                  return s = this.s.getUint32(this.t) * Math.pow(2, 32), o = this.s.getUint32(this.t + 4), this.t += 8, s + o;
              case 208:
                  return t = this.s.getInt8(this.t), this.t += 1, t;
              case 209:
                  return t = this.s.getInt16(this.t), this.t += 2, t;
              case 210:
                  return t = this.s.getInt32(this.t), this.t += 4, t;
              case 211:
                  return s = this.s.getInt32(this.t) * Math.pow(2, 32), o = this.s.getUint32(this.t + 4), this.t += 8, s + o;
              case 212:
                  return i = this.s.getInt8(this.t), this.t += 1, 0 === i ? void(this.t += 1) : [i, this.l(1)];
              case 213:
                  return i = this.s.getInt8(this.t), this.t += 1, [i, this.l(2)];
              case 214:
                  return i = this.s.getInt8(this.t), this.t += 1, [i, this.l(4)];
              case 215:
                  return i = this.s.getInt8(this.t), this.t += 1, 0 === i ? (s = this.s.getInt32(this.t) * Math.pow(2, 32), o = this.s.getUint32(this.t + 4), this.t += 8, new Date(s + o)) : [i, this.l(8)];
              case 216:
                  return i = this.s.getInt8(this.t), this.t += 1, [i, this.l(16)];
              case 217:
                  return n = this.s.getUint8(this.t), this.t += 1, this.h(n);
              case 218:
                  return n = this.s.getUint16(this.t), this.t += 2, this.h(n);
              case 219:
                  return n = this.s.getUint32(this.t), this.t += 4, this.h(n);
              case 220:
                  return n = this.s.getUint16(this.t), this.t += 2, this.g(n);
              case 221:
                  return n = this.s.getUint32(this.t), this.t += 4, this.g(n);
              case 222:
                  return n = this.s.getUint16(this.t), this.t += 2, this.M(n);
              case 223:
                  return n = this.s.getUint32(this.t), this.t += 4, this.M(n)
          }
          throw new Error("Could not parse")
      };

      const q = function(t) {
          var n = new e(t = t.slice(1)),
              i = n.v();
          if (n.t === t.byteLength) return i;
          return null
      }(packet);

      return q?.data?.[1];
  }

  var blueboat = {
      encode,
      decode
  };

  function HexAlphaToRGBA(hex, alpha) {
      let r = parseInt(hex.slice(1, 3), 16);
      let g = parseInt(hex.slice(3, 5), 16);
      let b = parseInt(hex.slice(5, 7), 16);
      return `rgba(${r}, ${g}, ${b}, ${alpha})`;
  }
  function RGBAtoHexAlpha(rgba) {
      let [r, g, b, a] = rgba.slice(5, -1).split(",").map(x => parseFloat(x.trim()));
      let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
      return [hex, a];
  }
  function parseChangePacket(packet) {
      let returnVar = [];
      for (let change of packet.changes) {
          let data = {};
          let keys = change[1].map((index) => packet.values[index]);
          for (let i = 0; i < keys.length; i++) {
              data[keys[i]] = change[2][i];
          }
          returnVar.push({
              id: change[0],
              data
          });
      }
      return returnVar;
  }

  // @ts-ignore (can't be bothered to figure out how to import this)
  class SocketHandler extends EventTarget {
      constructor(cheat) {
          super();
          this.socket = null;
          this.hasFired = false;
          this.transportType = "unknown";
          this.blueboatRoomId = null;
          this.cheat = cheat;
      }
      getSocket() {
          let handlerThis = this;
          if (!Object.isFrozen(WebSocket)) {
              // intercept any outgoing socket connections
              WebSocket.prototype._send = WebSocket.prototype.send;
              WebSocket.prototype.send = function (data) {
                  // if the url is a local url, don't intercept it
                  if (this.url.startsWith("ws://localhost"))
                      return this._send(data);
                  handlerThis.registerSocket(this);
                  if (!handlerThis.socket)
                      return;
                  handlerThis.socket._send(data);
                  // attempt to get the room id
                  if (handlerThis.transportType == "blueboat") {
                      let decoded = blueboat.decode(data);
                      if (decoded.roomId)
                          handlerThis.blueboatRoomId = decoded.roomId;
                      if (decoded.room)
                          handlerThis.blueboatRoomId = decoded.room;
                      if (!handlerThis.blueboatRoomId)
                          handlerThis.cheat.log("Room ID: ", handlerThis.blueboatRoomId);
                  }
              };
          }
          else {
              // periodically attempt to extract the socket, in case something failed
              let tryGetSocket = setInterval(() => {
                  var _a, _b, _c, _d, _e;
                  let gotSocket = (_e = (_d = (_c = (_b = (_a = window === null || window === void 0 ? void 0 : window.stores) === null || _a === void 0 ? void 0 : _a.network) === null || _b === void 0 ? void 0 : _b.room) === null || _c === void 0 ? void 0 : _c.connection) === null || _d === void 0 ? void 0 : _d.transport) === null || _e === void 0 ? void 0 : _e.ws;
                  if (gotSocket) {
                      handlerThis.registerSocket(gotSocket);
                      clearInterval(tryGetSocket);
                  }
              }, 100);
          }
      }
      registerSocket(socket) {
          if (this.hasFired)
              return;
          this.socket = socket;
          this.hasFired = true;
          this.dispatchEvent(new CustomEvent("socket", { detail: socket }));
          // detect the transport type
          if ("stores" in unsafeWindow)
              this.transportType = "colyseus";
          else
              this.transportType = "blueboat";
          let handlerThis = this;
          socket.addEventListener("message", (e) => {
              // decode the message
              let decoded;
              if (this.transportType == "colyseus")
                  decoded = colyseus.decode(e.data);
              else
                  decoded = blueboat.decode(e.data);
              if (!decoded)
                  return;
              handlerThis.dispatchEvent(new CustomEvent("recieveMessage", { detail: decoded }));
              if (typeof decoded != "object")
                  return;
              if ('changes' in decoded) {
                  let parsed = parseChangePacket(decoded);
                  handlerThis.dispatchEvent(new CustomEvent("recieveChanges", { detail: parsed }));
              }
          });
      }
      sendData(channel, data) {
          if (!this.socket)
              return;
          if (!this.blueboatRoomId && this.transportType == "blueboat")
              return this.cheat.log("Room ID not found, can't send data");
          let encoded;
          if (this.transportType == "colyseus")
              encoded = colyseus.encode(channel, data);
          else
              encoded = blueboat.encode(channel, data, this.blueboatRoomId);
          this.socket.send(encoded);
      }
  }

  class KeybindManager {
      constructor() {
          this.keys = new Set();
          this.binds = [];
          this.addListeners();
      }
      addListeners() {
          window.addEventListener("keydown", (e) => {
              this.keys.add(e.key.toLowerCase());
              this.checkBinds(e);
          });
          window.addEventListener("keyup", (e) => {
              this.keys.delete(e.key.toLowerCase());
          });
          window.addEventListener("blur", () => {
              this.keys.clear();
          });
      }
      checkBinds(e) {
          var _a;
          if (e.repeat)
              return;
          for (let bind of this.binds) {
              if (!bind.keys.has(e.key.toLowerCase()))
                  continue;
              if (bind.keys.size == 0)
                  continue;
              // if the bind is exclusive, make sure no other keys are pressed
              if (bind.exclusive && bind.keys.size != this.keys.size)
                  continue;
              // check whether the keys in the bind are pressed
              if (Array.from(bind.keys).every(key => this.keys.has(key))) {
                  (_a = bind.callback) === null || _a === void 0 ? void 0 : _a.call(bind);
              }
          }
      }
      registerBind(bind) {
          if (this.binds.includes(bind))
              return;
          this.binds.push(bind);
      }
      removeBind(bind) {
          if (!this.binds.includes(bind))
              return;
          this.binds.splice(this.binds.indexOf(bind), 1);
      }
      clearBinds() {
          this.binds = [];
      }
  }

  var css = "#gc_hud {\r\n    position: absolute;\r\n    top: 0;\r\n    left: 0;\r\n    width: 100%;\r\n    height: 100%;\r\n    z-index: 999999999999;\r\n    pointer-events: none;\r\n    color: var(--text-color);\r\n}\r\n\r\n#gc_hud .menu_controls {\r\n    width: 100%;\r\n    height: 20px;\r\n    background-color: var(--menu-controls-bg-color);\r\n    color: var(--menu-controls-text-color);\r\n    border-radius: 5px 5px 0px 0px;\r\n    text-align: center;\r\n    position: relative;\r\n}\r\n\r\n#gc_hud .menu_minimizer {\r\n    margin-left: 20px;\r\n    margin-right: 20px;\r\n    position: absolute;\r\n    top: 0;\r\n    right: 0;\r\n    user-select: none;\r\n}\r\n\r\n#gc_hud .menu {\r\n    pointer-events: auto;\r\n    position: absolute;\r\n    background-color: var(--menu-bg-color);\r\n    display: inline-block;\r\n    border-radius: 5px;\r\n    overflow-x: hidden;\r\n    overflow-y: hidden;\r\n    resize: both;\r\n    width: 300px;\r\n    height: 200px;\r\n    outline: 3px solid var(--menu-border-color);\r\n}\r\n\r\n#gc_hud .menu.minimized {\r\n    height: 20px !important;\r\n    overflow-y: hidden;\r\n    resize: horizontal;\r\n}\r\n\r\n#gc_hud .group {\r\n    margin: 0px;\r\n    padding: 0px;\r\n    width: 100%;\r\n    /* allocate some space at the top and bottom */\r\n    height: calc(100% - 40px); \r\n    position: absolute;\r\n    top: 20px;\r\n    left: 0;\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: center;\r\n    overflow-y: auto;\r\n    overflow-x: hidden;\r\n}\r\n\r\n#gc_hud .button, #gc_hud .group_opener {\r\n    background-color: var(--button-bg-color);\r\n    border: 1px solid var(--button-border-color);\r\n}\r\n\r\n#gc_hud .toggle {\r\n    background-color: var(--toggle-bg-color);\r\n    border: 1px solid var(--toggle-border-color);\r\n}\r\n\r\n#gc_hud .button, #gc_hud .toggle, #gc_hud .group_opener {\r\n    border-radius: 5px;\r\n    padding: 5px;\r\n    margin: 5px;\r\n    cursor: pointer;\r\n    width: 90%;\r\n    transition: transform 0.2s ease-in-out;\r\n}\r\n\r\n/* make it bounce smaller when clicked */\r\n#gc_hud .button:active, #gc_hud .toggle:active, #gc_hud .group_opener:active {\r\n    transform: scale(0.93);\r\n}\r\n\r\n#gc_hud .colorpicker {\r\n    width: 100%;\r\n}\r\n\r\n#gc_hud .colorpicker_wrapper {\r\n    width: 100%;\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: space-around;\r\n    align-items: center;\r\n    margin: 5px;\r\n}\r\n\r\n#gc_hud .colorpicker_opacity_wrapper {\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: space-around;\r\n    align-items: center;\r\n}\r\n\r\n#gc_hud .colorpicker_preview {\r\n    width: 50px;\r\n    height: 50px;\r\n    border-radius: 5px;\r\n    opacity: 1;\r\n}\r\n\r\n#gc_hud .text {\r\n    text-align: center;\r\n    width: 100%;\r\n}\r\n\r\n#gc_hud .textinput_wrapper, #gc_hud .dropdown_wrapper, #gc_hud .slider_wrapper {\r\n    width: 100%;\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: space-around;\r\n    align-items: center;\r\n}\r\n\r\n#gc_hud .textinput {\r\n    width: 90%;\r\n    border-radius: 5px;\r\n    border: 1px solid var(--textinput-border-color);\r\n    background-color: var(--textinput-bg-color);\r\n    color: var(--text-color);\r\n    padding: 5px;\r\n    margin: 5px;\r\n}\r\n\r\n#gc_hud .dropdown {\r\n    width: 90%;\r\n    border-radius: 5px;\r\n    border: 1px solid var(--dropdown-border-color);\r\n    background-color: var(--dropdown-bg-color);\r\n    color: var(--text-color);\r\n    padding: 5px;\r\n    margin: 5px;\r\n}\r\n\r\n#gc_hud .toggle_wrapper, #gc_hud .button_wrapper {\r\n    width: 90%;\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: space-around;\r\n    align-items: center;\r\n    padding: 0px;\r\n}\r\n\r\n#gc_hud .toggle, #gc_hud .button {\r\n    /* make it take up as much space as possible */\r\n    width: 100%;\r\n    margin-left: 0px;\r\n    margin-right: 0px;\r\n}\r\n\r\n#gc_hud .keybind_opener {\r\n    width: 30px;\r\n    height: 30px;\r\n    font-size: 30px;\r\n    margin-left: 10px;\r\n}\r\n\r\n#gc_hud .keybind_editor_wrapper {\r\n    background-color: var(--keybind-editor-bg-color);\r\n    border: 3px solid var(--keybind-editor-border-color);\r\n    border-radius: 8px;\r\n}\r\n\r\n#gc_hud .keybind_editor {\r\n    width: 50vw;\r\n    height: 50vh;\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: space-around;\r\n    align-items: center;\r\n    color: var(--text-color);\r\n}\r\n\r\n#gc_hud .close {\r\n    position: absolute;\r\n    top: 0;\r\n    right: 0;\r\n    width: 20px;\r\n    height: 20px;\r\n    font-size: 20px;\r\n    cursor: pointer;\r\n    user-select: none;\r\n}\r\n\r\n#gc_hud .keybind_title {\r\n    font-size: 40px;\r\n    text-align: center;\r\n}\r\n\r\n#gc_hud .keybind_controls {\r\n    width: 100%;\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: space-around;\r\n    align-items: center;\r\n}\r\n\r\n#gc_hud .keybind_display {\r\n    border: 3px solid white;\r\n    min-width: 300px;\r\n    border-radius: 5px;\r\n    height: 50px;\r\n    text-align: center;\r\n    display: flex;\r\n    justify-content: center;\r\n    align-items: center;\r\n    cursor: pointer;\r\n}\r\n\r\n#gc_hud .keybind_exclusive {\r\n    display: flex;\r\n}\r\n\r\n#gc_hud .keybind_exclusive .text {\r\n    margin-right: 10px;\r\n}\r\n\r\n#gc_hud .keybind_editor .action {\r\n    cursor: pointer;\r\n}\r\n\r\n#gc_hud .slider {\r\n    width: 90%;\r\n    margin: 5px;\r\n}\r\n\r\n.gc_overlay_canvas {\r\n    position: absolute;\r\n    top: 0;\r\n    left: 0;\r\n    width: 100vw;\r\n    height: 100vh;\r\n    z-index: 9999;\r\n    pointer-events: none;\r\n}\r\n\r\n@keyframes slide_out_left {\r\n    0% {\r\n        transform: translateX(0);\r\n        opacity: 1;\r\n        pointer-events: all;\r\n    }\r\n\r\n    100% {\r\n        transform: translateX(-100%);\r\n        opacity: 0;\r\n        pointer-events: none;\r\n    }\r\n}\r\n\r\n@keyframes slide_out_right {\r\n    0% {\r\n        transform: translateX(0);\r\n        opacity: 1;\r\n        pointer-events: all;\r\n    }\r\n\r\n    100% {\r\n        transform: translateX(100%);\r\n        opacity: 0;\r\n        pointer-events: none;\r\n    }\r\n}\r\n\r\n@keyframes slide_in_left {\r\n    0% {\r\n        transform: translateX(-100%);\r\n        opacity: 0;\r\n        pointer-events: none;\r\n    }\r\n\r\n    100% {\r\n        transform: translateX(0);\r\n        opacity: 1;\r\n        pointer-events: all;\r\n    }\r\n}\r\n\r\n@keyframes slide_in_right {\r\n    0% {\r\n        transform: translateX(100%);\r\n        opacity: 0;\r\n        pointer-events: none;\r\n    }\r\n\r\n    100% {\r\n        transform: translateX(0);\r\n        opacity: 1;\r\n        pointer-events: all;\r\n    }\r\n}\r\n\r\n@keyframes idle {}";

  class HudElement extends EventTarget {
      // any is used to avoid circular dependencies
      constructor(group, options) {
          super();
          this.group = null;
          this.options = null;
          this.element = null;
          this.type = 'element';
          this.group = group;
          this.options = options;
      }
      remove() {
          var _a;
          (_a = this.element) === null || _a === void 0 ? void 0 : _a.remove();
          this.group.elements.splice(this.group.elements.indexOf(this), 1);
      }
  }

  class Text extends HudElement {
      constructor(group, options) {
          super(group, options);
          this.type = "text";
          this.element = document.createElement("div");
          this.element.classList.add("text");
          this.element.innerText = this.options.text;
      }
      set text(text) {
          this.element.innerText = text;
      }
      get text() {
          return this.element.innerText;
      }
  }

  class GroupOpener extends HudElement {
      constructor(group, options) {
          super(group, options);
          this.type = "groupOpener";
          this.element = document.createElement("button");
          this.element.classList.add("group_opener");
          this.element.innerText = this.options.text;
          this.element.addEventListener("click", () => {
              var _a;
              let direction = (_a = this.options.direction) !== null && _a !== void 0 ? _a : "right";
              let oppositeDirection = direction == "right" ? "left" : "right";
              // open the group
              this.group.slide("out", direction);
              let groupToOpen = this.group.menu.getAnyGroup(this.options.openGroup);
              if (!groupToOpen)
                  return;
              groupToOpen.slide("in", oppositeDirection);
          });
      }
      set text(text) {
          this.element.innerText = text;
      }
      get text() {
          return this.element.innerText;
      }
  }

  class ColorPicker extends HudElement {
      constructor(group, options) {
          var _a, _b;
          super(group, options);
          this.opacitySlider = null;
          this.colorPicker = null;
          this.preview = null;
          this.type = "colorpicker";
          // create the element
          let element = document.createElement("div");
          element.innerHTML = `
            <div class="text">${this.options.text}</div>
            <div class="colorpicker_wrapper">
                <div class="colorpicker_opacity_wrapper">
                    <div class="text">Opacity</div>
                    <input type="range" min="0" max="255" value="255" class="colorpicker_opacity">
                </div>
                <input type="color" value="#ffffff" class="colorpicker_color">
                <div class="colorpicker_preview"></div>
            </div>
        `;
          element.classList.add("colorpicker");
          this.opacitySlider = element.querySelector(".colorpicker_opacity");
          this.colorPicker = element.querySelector(".colorpicker_color");
          this.preview = element.querySelector(".colorpicker_preview");
          if (this.options.bindVar)
              this.color = window.cheat.hud.syncedVars.get("cssVars").get(this.options.bindVar);
          else if (this.options.color)
              this.color = this.options.color;
          // prevent the menu from being dragged when the slider is moved
          this.opacitySlider.addEventListener("mousedown", (e) => { e.stopPropagation(); });
          (_a = this.opacitySlider) === null || _a === void 0 ? void 0 : _a.addEventListener("input", () => { this.updatePreview(); });
          (_b = this.colorPicker) === null || _b === void 0 ? void 0 : _b.addEventListener("input", () => { this.updatePreview(); });
          this.element = element;
          this.updatePreview();
      }
      updatePreview() {
          let color = this.colorPicker.value;
          let opacity = parseInt(this.opacitySlider.value);
          this.preview.style.backgroundColor = color;
          this.preview.style.opacity = `${opacity / 255}`;
          if (this.options.bindVar) {
              window.cheat.hud.updateCssVar(this.options.bindVar, this.color);
          }
          this.dispatchEvent(new CustomEvent("change", {
              detail: this.color
          }));
      }
      set color(color) {
          let [hex, alpha] = RGBAtoHexAlpha(color);
          this.colorPicker.value = hex;
          this.opacitySlider.value = `${255 * alpha}`;
          this.updatePreview();
      }
      get color() {
          let color = this.colorPicker.value;
          let opacity = parseInt(this.opacitySlider.value);
          let rgba = HexAlphaToRGBA(color, opacity / 255);
          return rgba;
      }
  }

  function keyboard() {
  	return (new DOMParser().parseFromString("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 50 40\"><rect width=\"40\" height=\"26\" x=\"4\" y=\"10\" rx=\"2.038\" ry=\"2.234\" style=\"fill:#000;fill-opacity:1;fill-rule:evenodd;stroke:#fff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none\"/><rect width=\"40\" height=\"26\" x=\"4\" y=\"10\" rx=\"2.038\" ry=\"2.234\" style=\"fill:#000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\"/><path d=\"M14 30h19M7 30h5M35 30h6M7 16h4M13 16h4M19 16h4M25 16h4M31 16h4M34 23h4M28 23h4M22 23h4M16 23h4M37 16h4M10 23h4\" style=\"fill:none;fill-opacity:.75;fill-rule:evenodd;stroke:#fff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\"/></svg>", 'image/svg+xml')).firstChild;
  }

  // @ts-ignore
  class KeybindEditor {
      constructor(options) {
          var _a;
          this.capturing = false;
          this.keys = new Set();
          this.actionState = "start";
          this.type = "keybindEditor";
          this.options = options;
          this.keybindOpener = keyboard();
          this.keybindOpener.classList.add("keybind_opener");
          this.keybindEditor = document.createElement("dialog");
          this.keybindEditor.classList.add("keybind_editor_wrapper");
          this.keybindEditor.innerHTML = `
			<div class="keybind_editor">
				<div class="close">x</div>
				<h1 class="keybind_title">${options.title}</h1>
				<div class="keybind_controls">
					<div class="keybind_display"></div>
					<div class="keybind_exclusive">
						<div class="text">Exclusive?</div>
						<input type="checkbox" class="exclusive" />
					</div>
					<div class="action">Start Capture</div>
				</div>
			</div>
		`;
          let bindExclusive = (_a = options.exclusive) !== null && _a !== void 0 ? _a : false;
          let existingKeybind = window.cheat.hud.syncedVars.get("keybinds").get(options.id);
          if (existingKeybind) {
              this.keys = new Set(existingKeybind.keys);
              bindExclusive = existingKeybind.exclusive;
          }
          else if (options.keys)
              this.keys = options.keys;
          if (bindExclusive)
              this.keybindEditor.querySelector(".exclusive").setAttribute("checked", "true");
          this.bind = {
              keys: this.keys,
              exclusive: bindExclusive,
              callback: options.callback
          };
          window.cheat.keybindManager.registerBind(this.bind);
          this.updateAction();
          this.updateDisplay();
          this.addEventListeners();
      }
      addEventListeners() {
          let action = this.keybindEditor.querySelector(".action");
          let close = this.keybindEditor.querySelector(".close");
          let display = this.keybindEditor.querySelector(".keybind_display");
          let exclusive = this.keybindEditor.querySelector(".exclusive");
          this.keybindOpener.addEventListener("click", () => {
              this.keybindEditor.showModal();
          });
          // prevent the menu from being dragged by the dialog
          this.keybindEditor.addEventListener("mousedown", (e) => {
              e.stopPropagation();
              if (!this.capturing)
                  return;
              if (e.target == action)
                  return;
              this.endCapture();
          });
          display.addEventListener("mousedown", (e) => {
              e.stopPropagation();
              this.beginCapture();
          });
          close.addEventListener("click", () => {
              this.keybindEditor.close();
          });
          action.addEventListener("click", () => {
              if (this.actionState == "Start Capture")
                  this.beginCapture();
              else if (this.actionState == "End Capture")
                  this.endCapture();
              else if (this.actionState == "Reset") {
                  this.keys.clear();
                  this.updateDisplay();
                  this.updateAction();
              }
          });
          exclusive.addEventListener("change", () => {
              this.bind.exclusive = exclusive.checked;
              this.syncBind();
          });
      }
      beginCapture() {
          this.capturing = true;
          this.keys.clear();
          this.keybindEditor.querySelector(".keybind_display").innerHTML = "Press any key...";
          document.addEventListener("keydown", this.keybindCapture.bind(this));
          this.updateAction();
      }
      keybindCapture(e) {
          if (!this.capturing)
              return;
          e.preventDefault();
          if (e.key === "Escape" || e.key === "Enter") {
              this.endCapture();
              return;
          }
          this.keys.add(e.key.toLowerCase());
          this.updateDisplay();
      }
      endCapture() {
          this.capturing = false;
          document.removeEventListener("keydown", this.keybindCapture.bind(this));
          this.updateAction();
          this.syncBind();
      }
      updateDisplay() {
          let keybindDisplay = this.keybindEditor.querySelector(".keybind_display");
          let keys = Array.from(this.keys);
          // replace space with "space"
          keys = keys.map((key) => key === " " ? "space" : key);
          keybindDisplay.innerHTML = keys.join(" + ");
      }
      updateAction() {
          let action = this.keybindEditor.querySelector(".action");
          if (this.capturing)
              this.actionState = "End Capture";
          else if (this.keys.size == 0)
              this.actionState = "Start Capture";
          else
              this.actionState = "Reset";
          action.innerHTML = this.actionState;
      }
      attachTo(element) {
          element.appendChild(this.keybindOpener);
          element.appendChild(this.keybindEditor);
      }
      syncBind() {
          if (!this.options.id)
              return;
          window.cheat.hud.updateKeybind(this.options.id, {
              keys: Array.from(this.keys),
              exclusive: this.bind.exclusive
          });
      }
  }

  class Button extends HudElement {
      constructor(group, options) {
          var _a, _b;
          super(group, options);
          this.type = "button";
          let element = document.createElement("div");
          element.classList.add("button_wrapper");
          element.innerHTML = `
            <button class="button">${this.options.text}</button>
        `;
          this.element = element;
          this.button = element.querySelector("button");
          this.button.addEventListener("click", () => {
              var _a;
              this.dispatchEvent(new CustomEvent("click"));
              if (this.options.runFunction)
                  (_a = window.cheat.funcs.get(this.options.runFunction)) === null || _a === void 0 ? void 0 : _a.call(this);
          });
          if (this.options.keybind) {
              let keybindEditor = new KeybindEditor({
                  title: (_a = options.title) !== null && _a !== void 0 ? _a : `Set keybind for ${this.button.innerText}`,
                  keys: (_b = options.defaultKeybind) !== null && _b !== void 0 ? _b : new Set(),
                  exclusive: false,
                  callback: () => {
                      this.dispatchEvent(new CustomEvent("click"));
                  }
              });
              keybindEditor.attachTo(element);
          }
      }
      set text(text) {
          this.button.innerText = text;
      }
      get text() {
          return this.button.innerText;
      }
  }

  class TextInput extends HudElement {
      constructor(group, options) {
          var _a;
          super(group, options);
          this.input = null;
          this.type = "textInput";
          let element = document.createElement("div");
          element.innerHTML = `
            <div class="text">${this.options.text}</div>
            <input type="text" class="textinput" placeholder="${(_a = this.options.placeholder) !== null && _a !== void 0 ? _a : ""}">
        `;
          element.classList.add("textinput_wrapper");
          this.element = element;
          this.input = element.querySelector("input");
          this.input.addEventListener("input", () => {
              this.dispatchEvent(new CustomEvent("input", {
                  detail: this.text
              }));
          });
      }
      set text(text) {
          this.input.value = text;
      }
      get text() {
          return this.input.value;
      }
  }

  class Toggle extends HudElement {
      constructor(group, options) {
          var _a, _b, _c;
          super(group, options);
          this.type = "toggle";
          // create the element
          let element = document.createElement("div");
          element.innerHTML = `
			<button class="toggle"></button>
		`;
          element.classList.add("toggle_wrapper");
          // add a keybind if needed
          if (options.keybind) {
              let editorOptions = {
                  title: (_a = options.title) !== null && _a !== void 0 ? _a : "Set keybind for toggle",
                  keys: (_b = options.defaultKeybind) !== null && _b !== void 0 ? _b : new Set(),
                  exclusive: false,
                  callback: () => {
                      this.toggle();
                  }
              };
              if (options.keybindId)
                  editorOptions.id = options.keybindId;
              let keybindEditor = new KeybindEditor(editorOptions);
              keybindEditor.attachTo(element);
          }
          this.enabled = (_c = this.options.default) !== null && _c !== void 0 ? _c : false;
          this.element = element;
          this.button = element.querySelector("button");
          this._textEnabled = this.options.textEnabled;
          this._textDisabled = this.options.textDisabled;
          this.updateButton();
          // prevent the menu from being activated with enter
          this.button.addEventListener("keydown", (e) => e.preventDefault());
          this.button.addEventListener("click", () => {
              this.toggle();
          });
      }
      updateButton() {
          this.button.innerHTML = this.enabled ? this._textEnabled : this._textDisabled;
          this.button.classList.toggle("enabled", this.enabled);
      }
      toggle() {
          this.enabled = !this.enabled;
          this.updateButton();
          if (this.options.runFunction)
              window.cheat.funcs.get(this.options.runFunction)(this.enabled);
          this.dispatchEvent(new CustomEvent("change", {
              detail: this.enabled
          }));
      }
      get value() {
          var _a;
          return (_a = this.enabled) !== null && _a !== void 0 ? _a : false;
      }
      set value(value) {
          this.enabled = value;
          this.updateButton();
      }
      get textEnabled() {
          return this._textEnabled;
      }
      set textEnabled(text) {
          this._textEnabled = text;
          this.updateButton();
      }
      get textDisabled() {
          return this._textDisabled;
      }
      set textDisabled(text) {
          this._textDisabled = text;
          this.updateButton();
      }
  }

  class Dropdown extends HudElement {
      constructor(group, options) {
          super(group, options);
          this.type = "dropdown";
          // create the element
          this.element = document.createElement("div");
          this.element.classList.add("dropdown_wrapper");
          this.element.innerHTML = `
			<div class="text">${options.text}</div>
			<select class="dropdown">
				${options.options.map((option) => {
            return `<option value="${option}" ${option === options.default ? "selected" : ""}>
						${option}
					</option>`;
        }).join("")}
			</select>
		`;
          this.select = this.element.querySelector(".dropdown");
          // prevent accidental navigation with arrows
          this.select.addEventListener("keydown", (e) => e.preventDefault());
          // add the event listener
          this.select.addEventListener("change", () => {
              if (options.runFunction) {
                  window.cheat.funcs.get(this.options.runFunction)(this.select.value);
              }
              this.dispatchEvent(new CustomEvent("change", {
                  detail: this.select.value
              }));
          });
      }
      addOption(option) {
          let optionElement = document.createElement("option");
          optionElement.value = option;
          optionElement.innerText = option;
          this.select.appendChild(optionElement);
      }
      removeOption(option) {
          let optionElement = this.select.querySelector(`option[value="${option}"]`);
          if (optionElement) {
              this.select.removeChild(optionElement);
          }
          if (this.select.value === option) {
              this.select.value = this.select.options[0].value;
          }
      }
      setOptions(options) {
          this.select.innerHTML = "";
          options.forEach((option) => {
              this.addOption(option);
          });
      }
      get value() {
          return this.select.value;
      }
      set value(value) {
          this.select.value = value;
      }
  }

  class Slider extends HudElement {
      constructor(group, options) {
          var _a;
          super(group, options);
          this.type = "slider";
          let element = document.createElement("div");
          element.classList.add("slider_wrapper");
          element.innerHTML = `
			<div class = "text">${options.text}</div>
			<input type = "range" min = "${options.min}" max = "${options.max}" value = "${(_a = options.default) !== null && _a !== void 0 ? _a : 0}" class = "slider">
		`;
          this.slider = element.querySelector(".slider");
          this.element = element;
          // prevent the slider from dragging the menu when clicked
          this.slider.addEventListener("mousedown", (e) => {
              e.stopPropagation();
          });
          // listen for changes
          this.slider.addEventListener("input", () => {
              if (this.options.runFunction)
                  window.cheat.funcs.get(this.options.runFunction)(this.slider.value);
              this.dispatchEvent(new CustomEvent("change", {
                  detail: this.slider.value
              }));
          });
      }
      set value(value) {
          this.slider.value = value.toString();
      }
      get value() {
          return Number(this.slider.value);
      }
  }

  class Group {
      constructor(menu, parentGroup, name, isRoot = false) {
          this.name = "";
          this.hideTimeout = null;
          this.element = null;
          this.isRoot = false;
          this.groups = [];
          this.parentGroup = null;
          this.elements = [];
          this.menu = null;
          this.menu = menu;
          this.parentGroup = parentGroup;
          this.name = name;
          this.isRoot = isRoot;
          this.init();
      }
      init() {
          var _a, _b;
          let element = document.createElement("div");
          element.classList.add("group");
          if (!this.isRoot)
              element.style.display = "none";
          this.element = element;
          (_b = (_a = this.menu) === null || _a === void 0 ? void 0 : _a.element) === null || _b === void 0 ? void 0 : _b.appendChild(element);
          // add a back button if this isn't the root group
          if (!this.isRoot) {
              this.addElement("groupopener", {
                  text: "Back",
                  openGroup: this.parentGroup.name,
                  direction: "right"
              });
          }
      }
      addElement(type, options) {
          var _a;
          let element;
          switch (type.toLowerCase()) {
              case "text":
                  element = new Text(this, options);
                  break;
              case "groupopener":
                  element = new GroupOpener(this, options);
                  break;
              case "colorpicker":
                  element = new ColorPicker(this, options);
                  break;
              case "button":
                  element = new Button(this, options);
                  break;
              case "textinput":
                  element = new TextInput(this, options);
                  break;
              case "toggle":
                  element = new Toggle(this, options);
                  break;
              case "dropdown":
                  element = new Dropdown(this, options);
                  break;
              case "slider":
                  element = new Slider(this, options);
                  break;
              default:
                  console.error(`Unknown element type: ${type}`);
          }
          if (!element)
              return null;
          (_a = this.element) === null || _a === void 0 ? void 0 : _a.appendChild(element.element);
          this.elements.push(element);
          return element;
      }
      slide(mode, direction) {
          if (this.hideTimeout)
              clearTimeout(this.hideTimeout);
          this.element.style.animation = `slide_${mode}_${direction} both 0.5s`;
          if (mode == "in")
              this.element.style.display = "flex";
          else if (mode == "out") {
              this.hideTimeout = setTimeout(() => this.element.style.display = "none", 500);
          }
      }
      createGroup(name) {
          let existingGroup = this.menu.getAnyGroup(name);
          if (existingGroup)
              return existingGroup;
          let group = new Group(this.menu, this, name);
          this.groups.push(group);
          this.menu.groups.push(group);
          // add a button to open the group
          this.addElement("groupopener", {
              text: name,
              openGroup: name,
              direction: "left"
          });
          return group;
      }
      group(name) {
          for (let i = 0; i < this.groups.length; i++) {
              if (this.groups[i].name == name) {
                  return this.groups[i];
              }
          }
          return null;
      }
      remove() {
          var _a, _b;
          (_a = this.element) === null || _a === void 0 ? void 0 : _a.remove();
          (_b = this.parentGroup) === null || _b === void 0 ? void 0 : _b.groups.splice(this.parentGroup.groups.indexOf(this), 1);
          this.menu.groups.splice(this.menu.groups.indexOf(this), 1);
      }
      clearElements() {
          for (let i = 0; i < this.elements.length; i++) {
              let element = this.elements[i];
              if (element.type == "groupOpener")
                  continue;
              element.remove();
              i--;
          }
      }
      loadFromObject(object) {
          const loadGroups = () => {
              if (object.groups) {
                  for (let group of object.groups) {
                      let newGroup = this.createGroup(group.name);
                      newGroup.loadFromObject(group);
                  }
              }
          };
          const loadElements = () => {
              if (object.elements) {
                  for (let element of object.elements) {
                      this.addElement(element.type, element.options);
                  }
              }
          };
          if (object.order == "elementsFirst") {
              loadElements();
              loadGroups();
          }
          else {
              loadGroups();
              loadElements();
          }
      }
  }

  class MenuControls {
      constructor(menu) {
          this.menu = null;
          this.element = null;
          this.menu = menu;
          this.init();
      }
      init() {
          var _a, _b;
          let element = document.createElement("div");
          element.classList.add("menu_controls");
          element.innerHTML = this.menu.name;
          // create the minimizer
          let minimizer = document.createElement("div");
          minimizer.classList.add("menu_minimizer");
          this.element = element;
          this.element.appendChild(minimizer);
          (_b = (_a = this.menu) === null || _a === void 0 ? void 0 : _a.element) === null || _b === void 0 ? void 0 : _b.appendChild(element);
          this.updateMinimizer();
          minimizer.addEventListener("click", () => {
              var _a;
              (_a = this.menu) === null || _a === void 0 ? void 0 : _a.minimize();
              this.updateMinimizer();
          });
      }
      updateMinimizer() {
          var _a;
          if (!this.element)
              return;
          let minimizer = this.element.querySelector(".menu_minimizer");
          if (!minimizer)
              return;
          minimizer.innerHTML = ((_a = this.menu) === null || _a === void 0 ? void 0 : _a.minimized) ? "+" : "-";
      }
  }

  class Menu {
      // any is used to avoid circular dependencies
      constructor(hud, name, transform) {
          this.hud = null;
          this.element = null;
          this.rootGroup = null;
          this.name = "";
          this.groups = [];
          this.minimized = false;
          this.transform = {
              top: 0,
              left: 0,
              width: 300,
              height: 200,
              minimized: false
          };
          this.hud = hud;
          this.name = name;
          if (transform)
              this.transform = transform;
          this.init();
      }
      applyTransform(transform) {
          var _a;
          if (!this.element)
              return;
          if (transform.height < 50)
              transform.height = 50;
          if (transform.width < 50)
              transform.width = 50;
          this.element.style.top = `${transform.top}px`;
          this.element.style.left = `${transform.left}px`;
          this.element.style.width = `${transform.width}px`;
          this.element.style.height = `${transform.height}px`;
          this.minimize((_a = transform.minimized) !== null && _a !== void 0 ? _a : false);
      }
      init() {
          var _a;
          let element = document.createElement("div");
          element.classList.add("menu");
          this.element = element;
          (_a = this.hud) === null || _a === void 0 ? void 0 : _a.element.appendChild(element);
          this.applyTransform(this.transform);
          // create the menu controls
          new MenuControls(this);
          // create the root group
          let rootGroup = new Group(this, null, "root", true);
          this.rootGroup = rootGroup;
          this.groups.push(rootGroup);
          // add the root group to the menu
          if (rootGroup.element) {
              this.element.appendChild(rootGroup.element);
          }
          this.addListeners();
      }
      addListeners() {
          if (!this.element)
              return;
          let dragging = false;
          let dragStart = { x: 0, y: 0 };
          let dragDistance = 0;
          this.element.addEventListener("mousedown", (e) => {
              dragging = true;
              dragStart.x = e.clientX;
              dragStart.y = e.clientY;
              dragDistance = 0;
          });
          // cancel dragging if it's being resized
          window.addEventListener("mouseup", () => { dragging = false; });
          let observer = new ResizeObserver((e) => {
              // if the element is invisible ignore it
              if (e[0].contentRect.width == 0 || e[0].contentRect.height == 0)
                  return;
              dragging = false;
              this.transform.width = e[0].contentRect.width;
              if (!this.minimized)
                  this.transform.height = e[0].contentRect.height;
              this.syncTransform();
          });
          observer.observe(this.element);
          window.addEventListener("mousemove", (e) => {
              if (!dragging)
                  return;
              dragDistance += Math.abs(e.clientX - dragStart.x) + Math.abs(e.clientY - dragStart.y);
              if (dragDistance < 10)
                  return;
              let x = e.clientX - dragStart.x;
              let y = e.clientY - dragStart.y;
              this.element.style.left = `${this.element.offsetLeft + x}px`;
              this.element.style.top = `${this.element.offsetTop + y}px`;
              // sync the transform
              this.transform.top = this.element.offsetTop;
              this.transform.left = this.element.offsetLeft;
              this.syncTransform();
              // prevent the menu from going off screen
              if (this.element.offsetLeft < 0)
                  this.element.style.left = "0px";
              if (this.element.offsetTop < 0)
                  this.element.style.top = "0px";
              if (this.element.offsetLeft + this.element.offsetWidth > window.innerWidth)
                  this.element.style.left = `${window.innerWidth - this.element.offsetWidth}px`;
              if (this.element.offsetTop + this.element.offsetHeight > window.innerHeight)
                  this.element.style.top = `${window.innerHeight - this.element.offsetHeight}px`;
              dragStart.x = e.clientX;
              dragStart.y = e.clientY;
          });
      }
      syncTransform() {
          this.hud.updateMenuTransform(this.name, this.transform);
      }
      // adding a group to the menu instead places it in the root group
      createGroup(name) {
          return this.rootGroup.createGroup(name);
      }
      group(name) {
          return this.rootGroup.group(name);
      }
      addElement(type, options) {
          return this.rootGroup.addElement(type, options);
      }
      getAnyGroup(name) {
          for (let group of this.groups) {
              if (group.name == name)
                  return group;
          }
          return null;
      }
      remove() {
          var _a;
          (_a = this.element) === null || _a === void 0 ? void 0 : _a.remove();
          this.hud.menus.splice(this.hud.menus.indexOf(this), 1);
      }
      minimize(force = null) {
          if (force == null)
              this.minimized = !this.minimized;
          else
              this.minimized = force;
          this.element.classList.toggle("minimized", this.minimized);
          this.transform.minimized = this.minimized;
          this.syncTransform();
      }
      loadFromObject(object) {
          this.rootGroup.loadFromObject(object);
      }
  }

  const DefaultCss = new Map([
      ["menu-bg-color", "rgba(0, 0, 0, 0.5)"],
      ["menu-border-color", "rgba(0, 0, 0, 0)"],
      ["text-color", "rgba(255, 255, 255, 1)"],
      ["button-bg-color", "rgba(0, 0, 0, 0.5)"],
      ["button-border-color", "rgba(255, 255, 255, 1)"],
      ["menu-controls-bg-color", "rgba(0, 0, 255, 0.5)"],
      ["menu-controls-text-color", "rgba(255, 255, 255, 1)"],
      ["textinput-border-color", "rgba(255, 255, 255, 1)"],
      ["textinput-bg-color", "rgba(0, 0, 0, 0.5)"],
      ["toggle-bg-color", "rgba(0, 0, 0, 0.5)"],
      ["toggle-border-color", "rgba(255, 255, 255, 1)"],
      ["dropdown-bg-color", "rgba(0, 0, 0, 0.5)"],
      ["dropdown-border-color", "rgba(255, 255, 255, 1)"],
      ["keybind-editor-bg-color", "rgba(0, 0, 0, 0.75)"],
      ["keybind-editor-border-color", "rgba(255, 255, 255, 1)"]
  ]);
  const DefaultMenuTransforms = new Map([
      ["HUD Customization", {
              top: 10,
              left: 10,
              width: Math.min(window.innerWidth / 4, 350),
              height: window.innerHeight / 2,
              minimized: false
          }],
      ["Devtools", {
              top: window.innerHeight / 2 + 10,
              left: 10,
              width: Math.min(window.innerWidth / 4, 350),
              height: window.innerHeight / 2,
              minimized: true
          }],
      ["General Cheats", {
              top: 10,
              left: window.innerWidth / 3 + 20,
              width: Math.min(window.innerWidth / 4, 350),
              height: window.innerHeight / 2,
              minimized: false
          }],
      ["Cheats for gamemodes", {
              top: 10,
              left: window.innerWidth / 3 * 2 + 30,
              width: Math.min(window.innerWidth / 4, 350),
              height: window.innerHeight / 2,
              minimized: false
          }]
  ]);
  const DefaultKeybinds = new Map([]);
  const HudCustomizerMenu = {
      menus: [
          {
              name: "HUD Customization",
              groups: [
                  {
                      name: "General",
                      order: "elementsFirst",
                      elements: [
                          {
                              type: "colorpicker",
                              options: {
                                  text: "Text Color",
                                  bindVar: "text-color"
                              }
                          }
                      ],
                      groups: [
                          {
                              name: "Menu Appearance",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Menu Background Color",
                                          bindVar: "menu-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Menu Border Color",
                                          bindVar: "menu-border-color"
                                      }
                                  }
                              ]
                          },
                          {
                              name: "Menu Controls Appearance",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Menu Controls Background Color",
                                          bindVar: "menu-controls-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Menu Controls Text Color",
                                          bindVar: "menu-controls-text-color"
                                      }
                                  }
                              ]
                          },
                          {
                              name: "Keybind Editor Appearance",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Keybind Editor Background Color",
                                          bindVar: "keybind-editor-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Keybind Editor Border Color",
                                          bindVar: "keybind-editor-border-color"
                                      }
                                  }
                              ]
                          }
                      ]
                  },
                  {
                      name: "Elements",
                      groups: [
                          {
                              name: "Buttons",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Button Background Color",
                                          bindVar: "button-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Button Border Color",
                                          bindVar: "button-border-color"
                                      }
                                  }
                              ]
                          },
                          {
                              name: "Text Inputs",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Text Input Background Color",
                                          bindVar: "textinput-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Text Input Border Color",
                                          bindVar: "textinput-border-color"
                                      }
                                  }
                              ]
                          },
                          {
                              name: "Toggles",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Toggle Background Color",
                                          bindVar: "toggle-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Toggle Border Color",
                                          bindVar: "toggle-border-color"
                                      }
                                  }
                              ]
                          },
                          {
                              name: "Dropdowns",
                              elements: [
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Dropdown Background Color",
                                          bindVar: "dropdown-bg-color"
                                      }
                                  },
                                  {
                                      type: "colorpicker",
                                      options: {
                                          text: "Dropdown Border Color",
                                          bindVar: "dropdown-border-color"
                                      }
                                  }
                              ]
                          }
                      ]
                  }
              ],
              elements: [
                  {
                      type: "button",
                      options: {
                          text: "Reset settings",
                          runFunction: "resetSettings"
                      }
                  }
              ]
          }
      ]
  };

  class OverlayCanvas {
      constructor() {
          this.canvas = document.createElement("canvas");
          this.canvas.classList.add("gc_overlay_canvas");
          this.canvas.width = window.innerWidth;
          this.canvas.height = window.innerHeight;
          // keep the canvas scaled to the window size
          window.addEventListener("resize", () => {
              this.canvas.width = window.innerWidth;
              this.canvas.height = window.innerHeight;
          });
      }
      get context() {
          return this.canvas.getContext("2d");
      }
  }

  // @ts-ignore
  class Hud {
      constructor(cheat) {
          this.element = null;
          this.menus = [];
          this.cssVarsSheet = null;
          // so we can access this globally while it's being constructed
          window.cheat.hud = this;
          this.syncedVars = new Map();
          this.cheat = cheat;
          this.cheat.funcs.set("resetSettings", this.resetSettings.bind(this));
          this.loadSyncedVar("cssVars", DefaultCss);
          this.loadSyncedVar("menuTransforms", DefaultMenuTransforms);
          this.loadSyncedVar("keybinds", DefaultKeybinds);
          this.updateCssVars();
          this.init();
          // load the customizer menu by default
          this.loadFromObject(HudCustomizerMenu);
          this.addToggle();
      }
      resetSettings() {
          if (!confirm("Setting updates will only take place after you reload the page, are you sure you want to reset settings?"))
              return;
          GM_deleteValue("cssVars");
          GM_deleteValue("menuTransforms");
          GM_deleteValue("keybinds");
      }
      addToggle() {
          this.cheat.keybindManager.registerBind({
              keys: new Set(["\\"]),
              exclusive: false,
              callback: () => {
                  if (this.element) {
                      this.element.style.display = this.element.style.display == "none" ? "" : "none";
                  }
              }
          });
      }
      createMenu(name) {
          var _a;
          let existingMenu = this.menu(name);
          if (existingMenu)
              return existingMenu;
          let menuTransform = (_a = this.syncedVars.get("menuTransforms")) === null || _a === void 0 ? void 0 : _a.get(name);
          let menu = new Menu(this, name, menuTransform);
          this.menus.push(menu);
          return menu;
      }
      menu(name) {
          for (let i = 0; i < this.menus.length; i++) {
              if (this.menus[i].name == name) {
                  return this.menus[i];
              }
          }
          return null;
      }
      loadSyncedVar(name, defaultValue) {
          let loadedValue = GM_getValue(name, "{}");
          let storedValue = JSON.parse(loadedValue);
          for (let [key, value] of defaultValue) {
              if (!storedValue[key])
                  storedValue[key] = value;
          }
          this.syncedVars.set(name, new Map(Object.entries(storedValue)));
      }
      updateCssVar(key, value) {
          var _a, _b;
          (_a = this.syncedVars.get("cssVars")) === null || _a === void 0 ? void 0 : _a.set(key, value);
          this.updateCssVars();
          // save the css vars
          let cssVars = JSON.stringify(Object.fromEntries((_b = this.syncedVars.get("cssVars")) !== null && _b !== void 0 ? _b : []));
          GM_setValue("cssVars", cssVars);
      }
      updateMenuTransform(name, transform) {
          var _a, _b;
          (_a = this.syncedVars.get("menuTransforms")) === null || _a === void 0 ? void 0 : _a.set(name, transform);
          // save the menu transforms
          let menuTransforms = JSON.stringify(Object.fromEntries((_b = this.syncedVars.get("menuTransforms")) !== null && _b !== void 0 ? _b : []));
          GM_setValue("menuTransforms", menuTransforms);
      }
      updateKeybind(id, value) {
          var _a, _b;
          console.log(id, value);
          (_a = this.syncedVars.get("keybinds")) === null || _a === void 0 ? void 0 : _a.set(id, value);
          // save the keybinds
          let keybinds = JSON.stringify(Object.fromEntries((_b = this.syncedVars.get("keybinds")) !== null && _b !== void 0 ? _b : []));
          GM_setValue("keybinds", keybinds);
      }
      updateCssVars() {
          var _a;
          if (!this.cssVarsSheet) {
              this.cssVarsSheet = new CSSStyleSheet();
              document.adoptedStyleSheets = [...document.adoptedStyleSheets, this.cssVarsSheet];
          }
          let cssVars = ":root {\n";
          for (let [key, value] of (_a = this.syncedVars.get("cssVars")) !== null && _a !== void 0 ? _a : []) {
              cssVars += `\t--${key}: ${value};\n`;
          }
          cssVars += "}";
          this.cssVarsSheet.replaceSync(cssVars);
      }
      init() {
          let style = new CSSStyleSheet();
          style.replaceSync(css);
          document.adoptedStyleSheets = [...document.adoptedStyleSheets, style];
          let hud = document.createElement("div");
          hud.id = "gc_hud";
          this.element = hud;
          // the body is not loaded yet, so we have to wait
          document.addEventListener("DOMContentLoaded", () => {
              document.body.appendChild(hud);
          });
          this.updateCssVars();
      }
      loadFromObject(obj) {
          for (let menu of obj.menus) {
              let newMenu = this.createMenu(menu.name);
              newMenu.loadFromObject(menu);
          }
      }
      createOverlayCanvas() {
          var _a;
          let canvas = new OverlayCanvas();
          (_a = document.body) === null || _a === void 0 ? void 0 : _a.appendChild(canvas.canvas);
          return canvas;
      }
  }

  const hudAddition$7 = {
      menus: [
          {
              name: "Devtools",
              elements: [
                  {
                      type: "toggle",
                      options: {
                          textEnabled: "Stop logging incoming messages",
                          textDisabled: "Log incoming messages",
                          default: false,
                          runFunction: "logIncomingMessages"
                      }
                  },
                  {
                      type: "button",
                      options: {
                          text: "Log closest device",
                          runFunction: "logClosestDevice"
                      }
                  }
              ]
          }
      ]
  };
  class DevtoolsClass {
      constructor() {
          this.name = "Gimkit Cheat Devtools";
          this.hudAddition = hudAddition$7;
          this.loggingIncomingMessages = false;
          this.funcs = new Map([
              ["logIncomingMessages", (enabled) => {
                      this.loggingIncomingMessages = enabled;
                  }],
              ["logClosestDevice", () => {
                      this.logClosestDevice();
                  }]
          ]);
      }
      init(cheat) {
          cheat.socketHandler.addEventListener("recieveMessage", (e) => {
              if (!this.loggingIncomingMessages)
                  return;
              cheat.log("Incoming message", e.detail);
          });
      }
      logClosestDevice() {
          var _a, _b, _c, _d, _e, _f, _g, _h;
          let devices = (_e = (_d = (_c = (_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.scene) === null || _c === void 0 ? void 0 : _c.worldManager) === null || _d === void 0 ? void 0 : _d.devices) === null || _e === void 0 ? void 0 : _e.devicesInView;
          let body = (_h = (_g = (_f = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _f === void 0 ? void 0 : _f.phaser) === null || _g === void 0 ? void 0 : _g.mainCharacter) === null || _h === void 0 ? void 0 : _h.body;
          let closest = null;
          let closestDistance = Infinity;
          for (let device of devices) {
              if (device.interactiveZones.zones.length == 0)
                  continue;
              let distance = Math.sqrt(Math.pow(device.x - body.x, 2) + Math.pow(device.y - body.y, 2));
              if (distance < closestDistance) {
                  closest = device;
                  closestDistance = distance;
              }
          }
          console.log(closest);
      }
  }
  function Devtools() {
      return new DevtoolsClass();
  }

  const hudAddition$6 = {
      menus: [
          {
              name: "General Cheats",
              elements: [
                  {
                      type: "toggle",
                      options: {
                          textEnabled: "Stop auto answering",
                          textDisabled: "Auto answer",
                          default: false,
                          runFunction: "setAutoAnswer",
                          keybind: true,
                          keybindId: "autoAnswer"
                      }
                  }
              ]
          }
      ]
  };
  class AutoanswerClass {
      constructor() {
          this.name = "Autoanswer";
          this.hudAddition = hudAddition$6;
          this.autoAnswering = false;
          this.funcs = new Map([
              ["setAutoAnswer", (enabled) => {
                      this.autoAnswering = enabled;
                  }]
          ]);
          this.currentQuestionId = "";
          this.answerDeviceId = "";
          this.questions = [];
          // blueboat specific
          this.questionIdList = [];
          this.currentQuestionIndex = 0;
      }
      init(cheat) {
          cheat.socketHandler.addEventListener("recieveMessage", (e) => {
              var _a;
              if (cheat.socketHandler.transportType == "colyseus")
                  return;
              // get the questions and question list
              if (((_a = e.detail) === null || _a === void 0 ? void 0 : _a.key) != "STATE_UPDATE")
                  return;
              switch (e.detail.data.type) {
                  case "GAME_QUESTIONS":
                      this.questions = e.detail.data.value;
                      break;
                  case "PLAYER_QUESTION_LIST":
                      this.questionIdList = e.detail.data.value.questionList;
                      this.currentQuestionIndex = e.detail.data.value.questionIndex;
                      break;
                  case "PLAYER_QUESTION_LIST_INDEX":
                      this.currentQuestionIndex = e.detail.data.value;
                      break;
              }
          });
          cheat.socketHandler.addEventListener("recieveChanges", (e) => {
              var _a, _b, _c;
              let changes = e.detail;
              for (let change of changes) {
                  // try to get the device ID of the answer device
                  for (let [key, value] of Object.entries(change.data)) {
                      if (key != "GLOBAL_questions")
                          continue;
                      this.questions = JSON.parse(value);
                      this.answerDeviceId = change.id;
                  }
                  // check whether it includes the new question ID
                  for (let [key, value] of Object.entries(change.data)) {
                      if (key.includes("currentQuestionId") && key.includes((_c = (_b = (_a = unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.mainCharacter) === null || _c === void 0 ? void 0 : _c.id)) {
                          this.currentQuestionId = value;
                      }
                  }
              }
          });
          setInterval(() => {
              var _a;
              if (!this.autoAnswering)
                  return;
              if (cheat.socketHandler.transportType == "colyseus") {
                  if (this.currentQuestionId == "")
                      return;
                  let correctQuestion = (_a = this.questions) === null || _a === void 0 ? void 0 : _a.find(q => q._id == this.currentQuestionId);
                  if (!correctQuestion)
                      return;
                  let correctAnswerId = correctQuestion.answers.find((a) => a.correct)._id;
                  let packet = {
                      key: 'answered',
                      deviceId: this.answerDeviceId,
                      data: {
                          answer: correctAnswerId
                      }
                  };
                  cheat.socketHandler.sendData("MESSAGE_FOR_DEVICE", packet);
              }
              else {
                  let questionId = this.questionIdList[this.currentQuestionIndex];
                  let answerId = this.questions.find(q => q._id == questionId).answers.find((a) => a.correct)._id;
                  cheat.socketHandler.sendData("QUESTION_ANSWERED", {
                      answer: answerId,
                      questionId: questionId
                  });
              }
          }, 1000);
      }
  }
  function Autoanswer() {
      return new AutoanswerClass();
  }

  let skins = ["Unchanged", "default_browngreen", "default_cyan", "default_darkblue", "default_darkgreen", "default_darkpurple", "default_gray", "default_grayblue", "default_graybrown", "default_hotpink", "default_lightbrown", "default_lightgreen", "default_lightpink", "default_lightpurple", "default_lightyellow", "default_lime", "default_maroon", "default_orange", "default_pink", "default_red", "default_yellow", "sunny", "glassHalfFull", "stripeDoubleGreen", "sprinklesRed", "dayOne", "vortexAgent", "echoAgent", "grayGradient", "mustache", "clown", "redNinja", "redDeliciousApple", "polkaDotBlueAndYellow", "fadedBlueGradient", "whiteAndBlueVerticalStripes", "volcanoCracks", "pinkPaste", "yellowCracksPurple", "glyphsYellowBrown", "camoBlue", "glyphsOrangeBlue", "purplePaste", "mustacheBrown", "mustachePink", "polkaDotWhiteAndRed", "camoTan", "camoGreen", "stripeDoublePurple", "stripeDoubleRed", "stripeDoubleYellow", "sprinklesChocolate", "coolRedBlueGradient", "mountainAndSun", "redDinoCostume", "pencilPack", "corn", "luchador", "fox", "burger", "galaxy", "cellBlue", "cellGold", "rockyWest", "puzzleRedGreen", "puzzleOrangeBlue", "puzzleGrayWhite", "puzzleGreenBlue", "puzzleYellowPurple", "pumpkin", "ghostCostume", "mummy", "fifthBirthday", "pumpkinPie", "feast", "frostBuddy", "festiveOnesieTan", "festiveOnesieRed", "festiveOnesieGreen", "festiveOnesieBlue", "hotChocolate", "snowglobe", "polkaDotFestive", "polkaDotFestiveReverse", "mustacheSanta", "firework", "gift", "snowman", "detective", "yinYang", "astroHelmet", "hamster", "pirate", "rockstar", "circuitGray", "circuitBlue", "circuitGreen", "roses", "heart", "zebra", "constellationBlackWhite", "constellationBlackGreen", "constellationPurpleYellow", "constellationPinkGreen", "constellationYellowPink", "squiggles", "frozenMummy", "leprechaun", "evilPlantGreen", "evilPlantPink", "fisher", "rainbowWave", "sketch", "sketchBlue", "bananaSplit", "eightBit", "gamerGreen", "gamerPink", "gamerPurple", "gamerYellow", "graduate", "graduateBlue", "arcticFox", "coffee", "partyPineapple", "sentryRobot", "construction", "clock", "crashTestDummy"];
  let trails = ["None", "origin_token"];
  skins = skins.sort();
  trails = trails.sort();
  const hudAddition$5 = {
      menus: [
          {
              name: "General Cheats",
              groups: [
                  {
                      name: "Cosmetic Picker",
                      elements: [
                          {
                              type: "text",
                              options: {
                                  text: "Select cosmetics to apply to your character. These changes are only visible to you."
                              }
                          },
                          {
                              type: "dropdown",
                              options: {
                                  text: "Selected Skin",
                                  options: skins,
                                  runFunction: "setSkin",
                                  default: "Unchanged"
                              }
                          },
                          {
                              type: "dropdown",
                              options: {
                                  text: "Selected Trail",
                                  options: trails,
                                  runFunction: "setTrail",
                                  default: "None"
                              }
                          }
                      ]
                  }
              ]
          }
      ]
  };
  class CosmeticpickerClass {
      constructor() {
          this.name = "Cosmetic Picker";
          this.hudAddition = hudAddition$5;
          this.funcs = new Map([
              ["setSkin", (skin) => {
                      this.setSkin(skin);
                  }],
              ["setTrail", (trail) => {
                      this.setTrail(trail);
                  }]
          ]);
          this.skinWaiting = false;
          this.trailWaiting = false;
      }
      setSkin(skin) {
          var _a, _b, _c, _d, _e, _f;
          if (skin == "Unchanged")
              return;
          if (!("stores" in unsafeWindow)) {
              if (this.skinWaiting)
                  return;
              let checkInterval = setInterval(() => {
                  if ("stores" in unsafeWindow) {
                      if (this.hasSkinApplied(skin))
                          clearInterval(checkInterval);
                      this.setSkin(skin);
                  }
              }, 100);
              this.skinWaiting = true;
              return;
          }
          let phaser = unsafeWindow.stores.phaser;
          let userId = (_a = phaser.mainCharacter) === null || _a === void 0 ? void 0 : _a.id;
          if (!userId)
              return;
          let skinId = `character_${skin}`;
          (_f = (_e = (_d = (_c = (_b = phaser.scene) === null || _b === void 0 ? void 0 : _b.characterManager) === null || _c === void 0 ? void 0 : _c.characters) === null || _d === void 0 ? void 0 : _d.get(userId)) === null || _e === void 0 ? void 0 : _e.skin) === null || _f === void 0 ? void 0 : _f.updateSkin(skinId);
      }
      hasSkinApplied(skin) {
          var _a, _b, _c, _d, _e;
          let phaser = unsafeWindow.stores.phaser;
          let userId = (_a = phaser.mainCharacter) === null || _a === void 0 ? void 0 : _a.id;
          if (!userId)
              return;
          let skinId = `character_${skin}`;
          return ((_e = (_d = (_c = (_b = phaser.scene) === null || _b === void 0 ? void 0 : _b.characterManager) === null || _c === void 0 ? void 0 : _c.characters) === null || _d === void 0 ? void 0 : _d.get(userId)) === null || _e === void 0 ? void 0 : _e.skin.skinId) == skinId;
      }
      setTrail(trail) {
          var _a, _b, _c, _d, _e, _f;
          if (!("stores" in unsafeWindow)) {
              if (this.trailWaiting)
                  return;
              let checkInterval = setInterval(() => {
                  if ("stores" in unsafeWindow) {
                      if (this.hasSkinApplied(trail))
                          clearInterval(checkInterval);
                      this.setTrail(trail);
                  }
              }, 100);
              this.trailWaiting = true;
              return;
          }
          let phaser = unsafeWindow.stores.phaser;
          let userId = (_a = phaser.mainCharacter) === null || _a === void 0 ? void 0 : _a.id;
          if (!userId)
              return;
          // blank trail is "None"
          if (trail == "None")
              trail = "";
          let trailId = `trail_${trail}`;
          (_f = (_e = (_d = (_c = (_b = phaser.scene) === null || _b === void 0 ? void 0 : _b.characterManager) === null || _c === void 0 ? void 0 : _c.characters) === null || _d === void 0 ? void 0 : _d.get(userId)) === null || _e === void 0 ? void 0 : _e.characterTrail) === null || _f === void 0 ? void 0 : _f.updateAppearance(trailId);
      }
  }
  function Cosmeticpicker() {
      return new CosmeticpickerClass();
  }

  const hudAddition$4 = {
      menus: [
          {
              name: "General Cheats",
              groups: [
                  {
                      name: "Player Highlighter",
                      elements: [
                          {
                              type: "toggle",
                              options: {
                                  textEnabled: "Stop Highlighting Teammates",
                                  textDisabled: "Highlight Teammates",
                                  runFunction: "highlightTeammates",
                                  keybind: true,
                                  keybindId: "highlightTeammates"
                              }
                          },
                          {
                              type: "toggle",
                              options: {
                                  textEnabled: "Stop Highlighting Enemies",
                                  textDisabled: "Highlight Enemies",
                                  runFunction: "highlightEnemies",
                                  keybind: true,
                                  keybindId: "highlightEnemies"
                              }
                          },
                          {
                              type: "slider",
                              options: {
                                  text: "Arrow Distance",
                                  min: 20,
                                  max: 750,
                                  default: 200,
                                  runFunction: "setArrowDistance"
                              }
                          }
                      ]
                  }
              ]
          }
      ]
  };
  class PlayerhighlighterClass {
      constructor() {
          this.name = "Player Highlighter";
          this.hudAddition = hudAddition$4;
          this.funcs = new Map([
              ["highlightTeammates", (value) => {
                      this.highlightingTeammates = value;
                  }],
              ["highlightEnemies", (value) => {
                      this.highlightingEnemies = value;
                  }],
              ["setArrowDistance", (value) => {
                      this.arrowDistance = value;
                  }]
          ]);
          this.highlightingTeammates = false;
          this.highlightingEnemies = false;
          this.ctx = null;
          this.canvas = null;
          this.arrowDistance = 200;
      }
      init(cheat) {
          setInterval(() => {
              var _a, _b;
              if (!((_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.scene))
                  return;
              if (this.canvas == null) {
                  this.canvas = cheat.hud.createOverlayCanvas();
                  this.ctx = this.canvas.context;
              }
              this.render();
          }, 100);
      }
      render() {
          var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
          (_a = this.ctx) === null || _a === void 0 ? void 0 : _a.clearRect(0, 0, (_c = (_b = this.canvas) === null || _b === void 0 ? void 0 : _b.canvas.width) !== null && _c !== void 0 ? _c : 1920, (_e = (_d = this.canvas) === null || _d === void 0 ? void 0 : _d.canvas.height) !== null && _e !== void 0 ? _e : 1080);
          let phaser = unsafeWindow.stores.phaser;
          let characters = phaser.scene.characterManager.characters;
          let user = phaser.mainCharacter;
          for (let [id, data] of characters) {
              if (id == user.id)
                  continue;
              let isEnemy = data.teamId != user.teamId;
              if (isEnemy && !this.highlightingEnemies)
                  continue;
              if (!isEnemy && !this.highlightingTeammates)
                  continue;
              this.ctx.strokeStyle = isEnemy ? "red" : "green";
              this.ctx.lineWidth = 5;
              // render an arrow pointing to the player
              let angle = Math.atan2(data.body.y - user.body.y, data.body.x - user.body.x);
              let distance = Math.sqrt(Math.pow(data.body.x - user.body.x, 2) + Math.pow(data.body.y - user.body.y, 2));
              let arrowDistance = Math.min(distance, this.arrowDistance);
              let arrowTip = {
                  x: Math.cos(angle) * arrowDistance + this.canvas.canvas.width / 2,
                  y: Math.sin(angle) * arrowDistance + this.canvas.canvas.height / 2
              };
              let leftTipAngle = angle - Math.PI / 4 * 3;
              let rightTipAngle = angle + Math.PI / 4 * 3;
              // draw a line from the center to both tips
              (_f = this.ctx) === null || _f === void 0 ? void 0 : _f.beginPath();
              (_g = this.ctx) === null || _g === void 0 ? void 0 : _g.moveTo(arrowTip.x, arrowTip.y);
              (_h = this.ctx) === null || _h === void 0 ? void 0 : _h.lineTo(Math.cos(leftTipAngle) * 50 + arrowTip.x, Math.sin(leftTipAngle) * 50 + arrowTip.y);
              (_j = this.ctx) === null || _j === void 0 ? void 0 : _j.moveTo(arrowTip.x, arrowTip.y);
              (_k = this.ctx) === null || _k === void 0 ? void 0 : _k.lineTo(Math.cos(rightTipAngle) * 50 + arrowTip.x, Math.sin(rightTipAngle) * 50 + arrowTip.y);
              (_l = this.ctx) === null || _l === void 0 ? void 0 : _l.stroke();
              // write the user's name and distance
              this.ctx.fillStyle = "black";
              this.ctx.font = "20px Verdana";
              this.ctx.textAlign = "center";
              this.ctx.textBaseline = "middle";
              this.ctx.fillText(`${data.nametag.name} (${Math.round(distance)})`, arrowTip.x, arrowTip.y);
          }
      }
  }
  function Playerhighlighter() {
      return new PlayerhighlighterClass();
  }

  class FreecamClass {
      constructor() {
          this.name = "Cosmetic Picker";
          this.freecamming = false;
          this.freeCamPos = { x: 0, y: 0 };
          this.toggleFreecam = null;
          this.spectateMenu = null;
          this.keys = new Set();
          this.lastPlayers = [];
      }
      init(cheat) {
          let camGroup = cheat.hud.createMenu("General Cheats").createGroup("Freecam");
          // initialize all the elements
          let toggleFreecam = camGroup.addElement("toggle", {
              textEnabled: "Stop Freecamming",
              textDisabled: "Unbind Camera",
              keybind: true,
              keybindId: "toggleFreecam"
          });
          toggleFreecam.addEventListener("change", (e) => {
              if (!this.camHelper) {
                  toggleFreecam.value = false;
                  return;
              }
              this.enableFreecam(e.detail);
          });
          let dropdown = camGroup.addElement("dropdown", {
              text: "Spectate Player",
              options: ["None"]
          });
          dropdown.addEventListener("change", (e) => {
              this.spectatePlayer(e.detail);
          });
          this.toggleFreecam = toggleFreecam;
          this.spectateMenu = dropdown;
          cheat.addEventListener('gameLoaded', () => {
              this.camHelper = unsafeWindow.stores.phaser.scene.cameraHelper;
              // add in the update loop
              setInterval(() => {
                  this.update();
              }, 1000 / 60);
          });
          window.addEventListener("keydown", (e) => {
              if (!this.freecamming)
                  return;
              if (!e.key.includes("Arrow"))
                  return;
              e.stopImmediatePropagation();
              this.keys.add(e.key);
          });
          window.addEventListener("keyup", (e) => {
              this.keys.delete(e.key);
          });
      }
      enableFreecam(value) {
          let phaser = unsafeWindow.stores.phaser;
          let camera = phaser.scene.cameras.cameras[0];
          if (value) {
              this.camHelper.stopFollow();
              this.freeCamPos.x = camera.midPoint.x;
              this.freeCamPos.y = camera.midPoint.y;
              camera.useBounds = false;
          }
          else {
              let charObj = phaser.scene.characterManager.characters.get(phaser.mainCharacter.id).body;
              this.camHelper.startFollowingObject({ object: charObj });
              camera.useBounds = true;
          }
          this.freecamming = value;
      }
      spectatePlayer(name) {
          // prevent freecamming if we already are
          this.enableFreecam(false);
          if (name == "None")
              return;
          this.toggleFreecam.value = true;
          let phaser = unsafeWindow.stores.phaser;
          let players = phaser.scene.characterManager.characters;
          for (let [id, player] of players) {
              if (player.nametag.name == name) {
                  this.camHelper.startFollowingObject({ object: player.body });
                  break;
              }
          }
      }
      update() {
          this.updateSpectatablePlayers();
          if (!this.freecamming)
              return;
          // move the camera
          if (this.keys.has("ArrowUp"))
              this.freeCamPos.y -= 20;
          if (this.keys.has("ArrowDown"))
              this.freeCamPos.y += 20;
          if (this.keys.has("ArrowLeft"))
              this.freeCamPos.x -= 20;
          if (this.keys.has("ArrowRight"))
              this.freeCamPos.x += 20;
          this.camHelper.goTo(this.freeCamPos);
      }
      updateSpectatablePlayers() {
          var _a;
          let phaser = unsafeWindow.stores.phaser;
          let players = phaser.scene.characterManager.characters;
          let options = ["None"];
          for (let [id, player] of players) {
              if (id == phaser.mainCharacter.id)
                  continue;
              options.push(player.nametag.name);
          }
          // make sure the list of players has changed
          let same = true;
          if (this.lastPlayers.length != options.length)
              same = false;
          else {
              for (let i = 0; i < this.lastPlayers.length; i++) {
                  if (this.lastPlayers[i] != options[i]) {
                      same = false;
                      break;
                  }
              }
          }
          if (same)
              return;
          this.lastPlayers = options;
          (_a = this.spectateMenu) === null || _a === void 0 ? void 0 : _a.setOptions(options);
      }
  }
  function Freecam() {
      return new FreecamClass();
  }

  var UpgradeType;
  (function (UpgradeType) {
      UpgradeType["Insurance"] = "insurance";
      UpgradeType["Money Per Question"] = "moneyPerQuestion";
      UpgradeType["Multiplier"] = "multiplier";
      UpgradeType["Streak Bonus"] = "streakBonus";
  })(UpgradeType || (UpgradeType = {}));
  const hudAddition$3 = {
      menus: [
          {
              name: "Cheats for gamemodes",
              groups: [
                  {
                      name: "Classic",
                      elements: [
                          {
                              type: "toggle",
                              options: {
                                  textEnabled: "Stop Auto Purchasing",
                                  textDisabled: "Start Auto Purchasing",
                                  default: false,
                                  runFunction: "setAutoPurchasingClassic",
                                  keybind: true,
                                  keybindId: "autoPurchasingClassic"
                              }
                          }
                      ]
                  }
              ]
          }
      ]
  };
  class ClassicClass {
      constructor() {
          this.name = "Classic Script";
          this.money = 0;
          this.upgradeLevels = {
              insurance: 1,
              moneyPerQuestion: 1,
              multiplier: 1,
              streakBonus: 1
          };
          this.hudAddition = hudAddition$3;
          this.autoPurchasing = false;
          this.funcs = new Map([
              ["setAutoPurchasingClassic", (enabled) => {
                      this.autoPurchasing = enabled;
                      if (this.autoPurchasing)
                          this.checkAutoBuy();
                  }]
          ]);
          this.upgradesToGet = [
              ["Streak Bonus", 2, 20],
              ["Money Per Question", 3, 100],
              ["Streak Bonus", 3, 200],
              ["Multiplier", 3, 300],
              ["Streak Bonus", 4, 2000],
              ["Multiplier", 4, 2000],
              ["Money Per Question", 5, 10000],
              ["Streak Bonus", 5, 20000],
              ["Multiplier", 5, 12000],
              ["Money Per Question", 6, 75000],
              ["Multiplier", 6, 85000],
              ["Streak Bonus", 6, 200000],
              ["Streak Bonus", 7, 2000000],
              ["Streak Bonus", 8, 20000000],
              ["Multiplier", 7, 700000],
              ["Money Per Question", 9, 10000000],
              ["Multiplier", 8, 6500000],
              ["Streak Bonus", 9, 200000000],
              ["Multiplier", 9, 65000000],
              ["Streak Bonus", 10, 2000000000],
              ["Money Per Question", 10, 100000000],
              ["Multiplier", 10, 1000000000]
          ];
      }
      init(cheat) {
          this.cheat = cheat;
          // get the amount of money
          this.cheat.socketHandler.addEventListener("recieveMessage", (e) => {
              var _a, _b;
              if (this.cheat.socketHandler.transportType != "blueboat")
                  return;
              if (((_a = e.detail.data) === null || _a === void 0 ? void 0 : _a.type) == "UPGRADE_LEVELS") {
                  this.upgradeLevels = e.detail.data.value;
                  // delete any upgrades that we already have
                  for (let i = 0; i < this.upgradesToGet.length; i++) {
                      let upgrade = this.upgradesToGet[i];
                      // check if we have the upgrade
                      let upgradeAmount = this.upgradeLevels[UpgradeType[upgrade[0]]];
                      if (upgradeAmount >= upgrade[1]) {
                          this.upgradesToGet.splice(i, 1);
                          i--;
                      }
                  }
              }
              if (((_b = e.detail.data) === null || _b === void 0 ? void 0 : _b.type) == "BALANCE") {
                  this.money = e.detail.data.value;
                  this.checkAutoBuy();
              }
          });
      }
      checkAutoBuy() {
          if (!this.autoPurchasing)
              return;
          let upgrade = this.upgradesToGet[0];
          if (!upgrade)
              return;
          if (this.money >= upgrade[2]) {
              this.purchaseUpgrade(upgrade[0], upgrade[1]);
          }
      }
      purchaseUpgrade(name, level) {
          this.cheat.log("Purchasing upgrade", name, level);
          this.cheat.socketHandler.sendData("UPGRADE_PURCHASED", {
              upgradeName: name,
              level
          });
      }
  }
  function Classic() {
      return new ClassicClass();
  }

  const hudAddition$2 = {
      menus: [
          {
              name: "Cheats for gamemodes",
              groups: [
                  {
                      name: "Super Rich Mode",
                      elements: [
                          {
                              type: "toggle",
                              options: {
                                  textEnabled: "Stop Auto Purchasing",
                                  textDisabled: "Start Auto Purchasing",
                                  default: false,
                                  runFunction: "setAutoPurchasingRichMode",
                                  keybind: true,
                                  keybindId: "autoPurchasingRichMode"
                              }
                          }
                      ]
                  }
              ]
          }
      ]
  };
  class RichModeClass extends ClassicClass {
      constructor() {
          super(...arguments);
          this.name = "Rich Mode Script";
          this.hudAddition = hudAddition$2;
          this.funcs = new Map([
              ["setAutoPurchasingRichMode", (enabled) => {
                      this.autoPurchasing = enabled;
                      if (this.autoPurchasing)
                          this.checkAutoBuy();
                  }]
          ]);
          this.upgradesToGet = [
              ["Streak Bonus", 2, 10000],
              ["Money Per Question", 3, 5000],
              ["Streak Bonus", 3, 100000],
              ["Multiplier", 3, 150000],
              ["Streak Bonus", 4, 1000000],
              ["Multiplier", 4, 1000000],
              ["Money Per Question", 5, 5000000],
              ["Streak Bonus", 5, 10000000],
              ["Multiplier", 5, 6000000],
              ["Money Per Question", 6, 37500000],
              ["Multiplier", 6, 42500000],
              ["Streak Bonus", 6, 100000000],
              ["Streak Bonus", 7, 1000000000],
              ["Streak Bonus", 8, 10000000000],
              ["Multiplier", 7, 350000000],
              ["Money Per Question", 9, 5000000000],
              ["Multiplier", 8, 3250000000],
              ["Streak Bonus", 9, 100000000000],
              ["Multiplier", 9, 32500000000],
              ["Streak Bonus", 10, 1000000000000],
              ["Money Per Question", 10, 50000000000],
              ["Multiplier", 10, 500000000000]
          ];
      }
  }
  function RichMode() {
      return new RichModeClass();
  }

  class TrustNoOneClass {
      constructor() {
          this.name = "Trust No One Script";
          this.people = [];
      }
      init(cheat) {
          this.cheat = cheat;
          // add the imposter display
          let group = cheat.hud.createMenu("Cheats for gamemodes").createGroup("Trust No One");
          let text = group.addElement("text", {
              text: "Imposters: Waiting... (only works if you don't join mid-game)"
          });
          cheat.socketHandler.addEventListener("recieveMessage", (e) => {
              if (this.cheat.socketHandler.transportType != "blueboat")
                  return;
              if (e.detail.key == "IMPOSTER_MODE_PEOPLE") {
                  this.people = e.detail.data;
                  let imposters = this.people.filter((person) => person.role == "imposter");
                  text.text = `Imposter(s): ${imposters.map((person) => person.name).join(", ")}`;
              }
          });
      }
  }
  function TrustNoOne() {
      return new TrustNoOneClass();
  }

  const hudAddition$1 = {
      menus: [
          {
              name: "General Cheats",
              elements: [
                  {
                      type: "toggle",
                      options: {
                          textEnabled: "Stop instant use",
                          textDisabled: "Instant use",
                          default: true,
                          runFunction: "setInstantUse",
                          keybind: true,
                          keybindId: "instantUse"
                      }
                  }
              ]
          }
      ]
  };
  class InstantuseClass {
      constructor() {
          this.name = "Instantuse";
          this.hudAddition = hudAddition$1;
          this.instantUseEnabled = true;
          this.funcs = new Map([
              ["setInstantUse", (enabled) => {
                      this.instantUseEnabled = enabled;
                  }]
          ]);
      }
      init(cheat) {
          let self = this;
          cheat.keybindManager.registerBind({
              keys: new Set(["enter"]),
              exclusive: false,
              callback() {
                  self.useNearest();
              }
          });
      }
      useNearest() {
          var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
          let devices = (_e = (_d = (_c = (_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.scene) === null || _c === void 0 ? void 0 : _c.worldManager) === null || _d === void 0 ? void 0 : _d.devices) === null || _e === void 0 ? void 0 : _e.devicesInView;
          let body = (_h = (_g = (_f = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _f === void 0 ? void 0 : _f.phaser) === null || _g === void 0 ? void 0 : _g.mainCharacter) === null || _h === void 0 ? void 0 : _h.body;
          if (!devices || !body)
              return;
          let closest = null;
          let closestDistance = Infinity;
          for (let device of devices) {
              if (device.interactiveZones.zones.length == 0)
                  continue;
              let distance = Math.sqrt(Math.pow(device.x - body.x, 2) + Math.pow(device.y - body.y, 2));
              if (distance < closestDistance) {
                  closest = device;
                  closestDistance = distance;
              }
          }
          if (!closest)
              return;
          (_k = (_j = closest === null || closest === void 0 ? void 0 : closest.interactiveZones) === null || _j === void 0 ? void 0 : _j.onInteraction) === null || _k === void 0 ? void 0 : _k.call(_j);
      }
  }
  function Instantuse() {
      return new InstantuseClass();
  }

  /******************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */

  function __awaiter(thisArg, _arguments, P, generator) {
      function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
      return new (P || (P = Promise))(function (resolve, reject) {
          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
          function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
          step((generator = generator.apply(thisArg, _arguments || [])).next());
      });
  }

  const purchases = {
      "Capture The Flag": [
          {
              displayName: "Speed Upgrade",
              selector: {
                  grantedItemName: "Speed Upgrade",
              },
              reusable: false
          },
          {
              displayName: "Efficiency Upgrade",
              selector: {
                  grantedItemName: "Efficiency Upgrade",
              },
              reusable: false
          },
          {
              displayName: "Energy Per Question Upgrade",
              selector: {
                  grantedItemName: "Energy Per Question Upgrade",
              },
              reusable: false
          },
          {
              displayName: "InvisaBits",
              selector: {
                  grantedItemId: "silver-ore"
              },
              reusable: true
          }
      ],
      "Tag": [
          {
              displayName: "Speed Upgrade",
              selector: {
                  grantedItemName: "Speed Upgrade"
              },
              reusable: false
          },
          {
              displayName: "Efficiency Upgrade",
              selector: {
                  grantedItemName: "Efficiency Upgrade"
              },
              reusable: false
          },
          {
              displayName: "Energy Per Question Upgrade",
              selector: {
                  grantedItemName: "Energy Per Question Upgrade"
              },
              reusable: false
          },
          {
              displayName: "Endurance Upgrade",
              selector: {
                  grantedItemName: "Endurance Upgrade"
              },
              reusable: false
          }
      ],
      "Snowbrawl": [
          {
              displayName: "Med Pack",
              selector: {
                  grantedItemId: "medpack"
              },
              reusable: true
          },
          {
              displayName: "Shield Can",
              selector: {
                  grantedItemId: "shield-can"
              },
              reusable: true
          }
      ],
      "One Way Out": [
          {
              displayName: "Med Pack",
              selector: {
                  grantedItemId: "medpack"
              },
              reusable: true
          },
          {
              displayName: "Shield Can",
              selector: {
                  grantedItemId: "shield-can"
              },
              reusable: true
          }
      ],
      "Farmchain": {
          "Seeds": [
              {
                  displayName: "Corn Seed",
                  selector: {
                      grantedItemId: "yellow-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Wheat Seed",
                  selector: {
                      grantedItemId: "tan-seed",
                      grantAction: "Grant Item"
                  },
                  reusable: true
              },
              {
                  displayName: "Potato Seed",
                  selector: {
                      grantedItemId: "brown-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Grape Seed",
                  selector: {
                      grantedItemId: "purple-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Raspberry Seed",
                  selector: {
                      grantedItemId: "magenta-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Watermelon Seed",
                  selector: {
                      grantedItemId: "green-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Coffee Bean",
                  selector: {
                      grantedItemId: "bronze-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Orange Seed",
                  selector: {
                      grantedItemId: "orange-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Gimberry Seed",
                  selector: {
                      grantedItemId: "gold-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Cash Berry Seed",
                  selector: {
                      grantedItemId: "dark-green-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Pepper Seed",
                  selector: {
                      grantedItemId: "red-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Energy Bar Seed",
                  selector: {
                      grantedItemId: "blue-seed"
                  },
                  reusable: true
              },
              {
                  displayName: "Lottery Ticket Seed",
                  selector: {
                      grantedItemId: "teal-seed"
                  },
                  reusable: true
              }
          ],
          "Seed Unlocks": [
              {
                  displayName: "Wheat Seed Unlock",
                  selector: {
                      grantedItemName: "Wheat Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Potato Seed Unlock",
                  selector: {
                      grantedItemName: "Potato Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Grape Seed Unlock",
                  selector: {
                      grantedItemName: "Grape Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Raspberry Seed Unlock",
                  selector: {
                      grantedItemName: "Raspberry Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Watermelon Seed Unlock",
                  selector: {
                      grantedItemName: "Watermelon Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Coffee Bean Seed Unlock",
                  selector: {
                      grantedItemName: "Coffee Bean Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Orange Seed Unlock",
                  selector: {
                      grantedItemName: "Orange Seed Unlock"
                  },
                  reusable: false
              },
              {
                  displayName: "Gimberry Seed Unlock",
                  selector: {
                      grantedItemName: "Gimberry Seed Unlock"
                  },
                  reusable: false
              }
          ]
      }
  };

  class InstapurchasersClass {
      constructor() {
          this.name = "Purchasers";
      }
      init(cheat) {
          cheat.addEventListener("gameLoaded", () => {
              this.createButtons(cheat);
          });
      }
      createButtons(cheat) {
          var _a, _b, _c, _d, _e;
          let devices = (_e = (_d = (_c = (_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.scene) === null || _c === void 0 ? void 0 : _c.worldManager) === null || _d === void 0 ? void 0 : _d.devices) === null || _e === void 0 ? void 0 : _e.allDevices;
          if (!devices) {
              setTimeout(() => this.createButtons(cheat), 1000); // try again in case something went wrong
              return;
          }
          for (let gamemode in purchases) {
              this.createGamemodeButtons(gamemode, purchases[gamemode], cheat.hud.createMenu("Cheats for gamemodes"));
          }
      }
      createGamemodeButtons(gamemode, content, rootGroup) {
          var _a, _b, _c, _d, _e, _f, _g;
          let devices = (_e = (_d = (_c = (_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.scene) === null || _c === void 0 ? void 0 : _c.worldManager) === null || _d === void 0 ? void 0 : _d.devices) === null || _e === void 0 ? void 0 : _e.allDevices;
          let group = rootGroup.createGroup(gamemode);
          if (!Array.isArray(content)) {
              for (let [name, menu] of Object.entries(content)) {
                  this.createGamemodeButtons(name, menu, group);
              }
              return;
          }
          for (let purchase of content) {
              let { selector, displayName, reusable } = purchase;
              // filter devices by selector
              let purchaseDevices = devices.filter((device) => {
                  var _a;
                  let matches = true;
                  for (let [key, value] of Object.entries(selector)) {
                      if (((_a = device.options) === null || _a === void 0 ? void 0 : _a[key]) != value) {
                          matches = false;
                          break;
                      }
                  }
                  return matches;
              });
              if (purchaseDevices.length == 0)
                  continue;
              // sort them by price
              purchaseDevices.sort((a, b) => { var _a, _b; return ((_a = a === null || a === void 0 ? void 0 : a.options) === null || _a === void 0 ? void 0 : _a.amountOfRequiredItem) - ((_b = b === null || b === void 0 ? void 0 : b.options) === null || _b === void 0 ? void 0 : _b.amountOfRequiredItem); });
              let buttonText = `Purchase ${displayName} (${(_g = (_f = purchaseDevices[0]) === null || _f === void 0 ? void 0 : _f.options) === null || _g === void 0 ? void 0 : _g.amountOfRequiredItem})`;
              let button = group.addElement('button', {
                  text: buttonText
              });
              button.addEventListener('click', () => __awaiter(this, void 0, void 0, function* () {
                  var _h, _j, _k, _l, _m, _o, _p;
                  if (!((_j = (_h = purchaseDevices[0]) === null || _h === void 0 ? void 0 : _h.interactiveZones) === null || _j === void 0 ? void 0 : _j.onInteraction)) {
                      // this happened to me a few times and I don't know why, just re-get the devices
                      purchaseDevices = purchaseDevices.map((device) => {
                          return devices.find((d) => d.id == device.id);
                      });
                      return;
                  }
                  (_m = (_l = (_k = purchaseDevices[0]) === null || _k === void 0 ? void 0 : _k.interactiveZones) === null || _l === void 0 ? void 0 : _l.onInteraction) === null || _m === void 0 ? void 0 : _m.call(_l);
                  if (reusable)
                      return;
                  // check whether it was successfully purchased
                  // wait 500ms for the purchase to go through
                  yield new Promise((resolve) => setTimeout(resolve, 500));
                  if (purchaseDevices[0].state.active)
                      return; // it wasn't purchased
                  purchaseDevices.shift();
                  if (purchaseDevices.length == 0) {
                      button.remove();
                      return;
                  }
                  // update the button text
                  buttonText = `Purchase ${displayName} (${(_p = (_o = purchaseDevices[0]) === null || _o === void 0 ? void 0 : _o.options) === null || _p === void 0 ? void 0 : _p.amountOfRequiredItem})`;
                  button.text = buttonText;
              }));
          }
      }
  }
  function Instapurchasers() {
      return new InstapurchasersClass();
  }

  const hudAddition = {
      menus: [
          {
              name: "Cheats for gamemodes",
              groups: [
                  {
                      name: "Farmchain",
                      elements: [
                          {
                              type: "toggle",
                              options: {
                                  textEnabled: "Stop auto harvesting",
                                  textDisabled: "Start auto harvesting",
                                  keybind: true,
                                  keybindId: "autoHarvesting",
                                  default: true,
                                  runFunction: "setAutoHarvest"
                              }
                          },
                          {
                              type: "toggle",
                              options: {
                                  textEnabled: "Stop auto planting",
                                  textDisabled: "Start auto planting",
                                  keybind: true,
                                  keybindId: "autoPlanting",
                                  default: false,
                                  runFunction: "setAutoPlant"
                              }
                          }
                      ]
                  }
              ]
          }
      ]
  };
  const seedRanking = [
      'yellow-seed',
      'tan-seed',
      'brown-seed',
      'purple-seed',
      'magenta-seed',
      'green-seed',
      'bronze-seed',
      'orange-seed',
      'gold-seed',
      'dark-green-seed',
      'red-seed',
      'blue-seed',
      'teal-seed'
  ];
  class FarmchainClass {
      constructor() {
          this.name = "Farmchain";
          this.hudAddition = hudAddition;
          this.autoHarvesting = true;
          this.autoPlanting = false;
          this.funcs = new Map([
              ["setAutoHarvest", (enabled) => {
                      this.autoHarvesting = enabled;
                  }],
              ["setAutoPlant", (enabled) => {
                      this.autoPlanting = enabled;
                  }]
          ]);
      }
      init(cheat) {
          // set up auto harvest
          cheat.socketHandler.addEventListener("recieveChanges", (e) => {
              let changes = e.detail;
              for (let change of changes) {
                  for (let key in change.data) {
                      if (!key.endsWith("status") || change.data[key] != "availableForCollection")
                          continue;
                      // harvest it
                      let packet = {
                          key: "collect",
                          deviceId: change.id,
                          data: undefined
                      };
                      cheat.socketHandler.sendData("MESSAGE_FOR_DEVICE", packet);
                  }
              }
          });
          cheat.addEventListener("gameLoaded", () => {
              var _a, _b, _c, _d, _e, _f, _g;
              let devices = (_e = (_d = (_c = (_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.phaser) === null || _b === void 0 ? void 0 : _b.scene) === null || _c === void 0 ? void 0 : _c.worldManager) === null || _d === void 0 ? void 0 : _d.devices) === null || _e === void 0 ? void 0 : _e.allDevices;
              let plots = devices.filter((device) => device.options.style == "plant");
              let recipieDevices = {};
              for (let device of devices) {
                  if (!seedRanking.includes((_f = device.options) === null || _f === void 0 ? void 0 : _f.ingredient1Item))
                      continue;
                  recipieDevices[(_g = device.options) === null || _g === void 0 ? void 0 : _g.ingredient1Item] = device;
              }
              // set up auto plant
              setInterval(() => {
                  var _a, _b, _c;
                  if (!this.autoPlanting)
                      return;
                  let inventory = (_c = (_b = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.me) === null || _b === void 0 ? void 0 : _b.inventory) === null || _c === void 0 ? void 0 : _c.slots;
                  if (!inventory)
                      return;
                  // find the most valuable seed in the inventory
                  let mostValuableSeed = undefined;
                  for (let seed of seedRanking) {
                      if (inventory.has(seed)) {
                          mostValuableSeed = seed;
                          break;
                      }
                  }
                  if (!mostValuableSeed)
                      return;
                  // plant the seed in the last idle plot
                  let plantPlot = plots.findLast((plot) => plot.state.status == "idle");
                  cheat.socketHandler.sendData("MESSAGE_FOR_DEVICE", {
                      key: "craft",
                      deviceId: plantPlot.id,
                      data: {
                          recipe: recipieDevices[mostValuableSeed].id
                      }
                  });
              }, 50);
          });
      }
  }
  function Farmchain() {
      return new FarmchainClass();
  }

  // import { BotCreator } from './scripts/general/botcreator';
  class Cheat extends EventTarget {
      constructor() {
          super();
          this.keybindManager = new KeybindManager();
          this.funcs = new Map();
          this.scripts = [];
          // add cheat to the global scope
          window.cheat = this;
          this.socketHandler = new SocketHandler(this);
          this.socketHandler.addEventListener("socket", (e) => {
              cheat.log("Socket connected", e);
          });
          this.socketHandler.getSocket();
          this.hud = new Hud(this);
          // initialize any scripts
          this.scripts = [
              Devtools(),
              Instantuse(),
              Autoanswer(),
              Cosmeticpicker(),
              Playerhighlighter(),
              Freecam(),
              Classic(),
              RichMode(),
              TrustNoOne(),
              Farmchain(),
              Instapurchasers(),
              // BotCreator()
          ];
          this.initScripts();
          this.waitForLoad();
      }
      waitForLoad() {
          // colyseus exclusive
          let loadInterval = setInterval(() => {
              var _a, _b, _c, _d;
              let loadedData = (_a = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _a === void 0 ? void 0 : _a.loading;
              let loaded = (loadedData === null || loadedData === void 0 ? void 0 : loadedData.percentageAssetsLoaded) >= 100 && (loadedData === null || loadedData === void 0 ? void 0 : loadedData.completedInitialLoad)
                  && (loadedData === null || loadedData === void 0 ? void 0 : loadedData.loadedInitialDevices) && (loadedData === null || loadedData === void 0 ? void 0 : loadedData.loadedInitialTerrain);
              if (!loaded)
                  return;
              // check whether we've been assigned to a team
              let team = (_d = (_c = (_b = unsafeWindow === null || unsafeWindow === void 0 ? void 0 : unsafeWindow.stores) === null || _b === void 0 ? void 0 : _b.phaser) === null || _c === void 0 ? void 0 : _c.mainCharacter) === null || _d === void 0 ? void 0 : _d.teamId;
              if (team == "__NO_TEAM_ID")
                  return;
              clearInterval(loadInterval);
              this.log("Game Loaded");
              this.dispatchEvent(new CustomEvent("gameLoaded"));
          }, 1000 / 60);
          // TODO: Add blueboat load detection
      }
      initScripts() {
          for (let script of this.scripts) {
              // add functions
              if (script.funcs) {
                  for (let [name, func] of script.funcs) {
                      this.funcs.set(name, func);
                  }
              }
              // add hud additions
              if (script.hudAddition) {
                  this.hud.loadFromObject(script.hudAddition);
              }
              // initialize the script
              if (script.init) {
                  script.init(this);
              }
          }
      }
      antifreeze() {
          let nativeFreeze = Object.freeze;
          Object.freeze = (obj) => {
              var _a;
              if (((_a = obj.constructor) === null || _a === void 0 ? void 0 : _a.name) == "WebSocket" || obj.name == "WebSocket")
                  return obj;
              return nativeFreeze(obj);
          };
          // ignore any attempts to modify WebSocket.prototype.send
          var originalSend = WebSocket.prototype.send;
          Object.defineProperty(WebSocket.prototype, 'send', {
              configurable: false,
              enumerable: false,
              get: function () {
                  return originalSend;
              },
              set: function (value) {
                  if (value === originalSend) {
                      return; // allow setting to the original value
                  }
                  console.log("Attempted to modify WebSocket.prototype.send");
              }
          });
      }
      log(...args) {
          console.log("[GC]", ...args);
      }
      getScript(name) {
          for (let script of this.scripts) {
              if (script.name == name)
                  return script;
          }
          return null;
      }
  }
  const cheat = new Cheat();

  cheat.log("Loaded Gimkit Cheat version: " + version);
  cheat.antifreeze();
  // make sure the cheat is running
  if (Object.isFrozen(WebSocket)) {
      alert("WebSocket object is still frozen. Please try refreshing the page. If this persists, open an issue on GitHub.");
  }

})();