/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({

/***/ "./gulp/node_modules/base64-js/index.js":
/*!**********************************************!*\
  !*** ./gulp/node_modules/base64-js/index.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray

var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
  lookup[i] = code[i]
  revLookup[code.charCodeAt(i)] = i
}

// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function getLens (b64) {
  var len = b64.length

  if (len % 4 > 0) {
    throw new Error('Invalid string. Length must be a multiple of 4')
  }

  // Trim off extra bytes after placeholder bytes are found
  // See: https://github.com/beatgammit/base64-js/issues/42
  var validLen = b64.indexOf('=')
  if (validLen === -1) validLen = len

  var placeHoldersLen = validLen === len
    ? 0
    : 4 - (validLen % 4)

  return [validLen, placeHoldersLen]
}

// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function _byteLength (b64, validLen, placeHoldersLen) {
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function toByteArray (b64) {
  var tmp
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]

  var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

  var curByte = 0

  // if there are placeholders, only get up to the last complete 4 chars
  var len = placeHoldersLen > 0
    ? validLen - 4
    : validLen

  var i
  for (i = 0; i < len; i += 4) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 18) |
      (revLookup[b64.charCodeAt(i + 1)] << 12) |
      (revLookup[b64.charCodeAt(i + 2)] << 6) |
      revLookup[b64.charCodeAt(i + 3)]
    arr[curByte++] = (tmp >> 16) & 0xFF
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 2) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 2) |
      (revLookup[b64.charCodeAt(i + 1)] >> 4)
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 1) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 10) |
      (revLookup[b64.charCodeAt(i + 1)] << 4) |
      (revLookup[b64.charCodeAt(i + 2)] >> 2)
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  return arr
}

function tripletToBase64 (num) {
  return lookup[num >> 18 & 0x3F] +
    lookup[num >> 12 & 0x3F] +
    lookup[num >> 6 & 0x3F] +
    lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
  var tmp
  var output = []
  for (var i = start; i < end; i += 3) {
    tmp =
      ((uint8[i] << 16) & 0xFF0000) +
      ((uint8[i + 1] << 8) & 0xFF00) +
      (uint8[i + 2] & 0xFF)
    output.push(tripletToBase64(tmp))
  }
  return output.join('')
}

function fromByteArray (uint8) {
  var tmp
  var len = uint8.length
  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
  var parts = []
  var maxChunkLength = 16383 // must be multiple of 3

  // go through the array every three bytes, we'll deal with trailing stuff later
  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
    parts.push(encodeChunk(
      uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
    ))
  }

  // pad the end with zeros, but make sure to not forget the extra bytes
  if (extraBytes === 1) {
    tmp = uint8[len - 1]
    parts.push(
      lookup[tmp >> 2] +
      lookup[(tmp << 4) & 0x3F] +
      '=='
    )
  } else if (extraBytes === 2) {
    tmp = (uint8[len - 2] << 8) + uint8[len - 1]
    parts.push(
      lookup[tmp >> 10] +
      lookup[(tmp >> 4) & 0x3F] +
      lookup[(tmp << 2) & 0x3F] +
      '='
    )
  }

  return parts.join('')
}


/***/ }),

/***/ "./gulp/node_modules/ieee754/index.js":
/*!********************************************!*\
  !*** ./gulp/node_modules/ieee754/index.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

exports.read = function (buffer, offset, isLE, mLen, nBytes) {
  var e, m
  var eLen = (nBytes * 8) - mLen - 1
  var eMax = (1 << eLen) - 1
  var eBias = eMax >> 1
  var nBits = -7
  var i = isLE ? (nBytes - 1) : 0
  var d = isLE ? -1 : 1
  var s = buffer[offset + i]

  i += d

  e = s & ((1 << (-nBits)) - 1)
  s >>= (-nBits)
  nBits += eLen
  for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}

  m = e & ((1 << (-nBits)) - 1)
  e >>= (-nBits)
  nBits += mLen
  for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}

  if (e === 0) {
    e = 1 - eBias
  } else if (e === eMax) {
    return m ? NaN : ((s ? -1 : 1) * Infinity)
  } else {
    m = m + Math.pow(2, mLen)
    e = e - eBias
  }
  return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
}

exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
  var e, m, c
  var eLen = (nBytes * 8) - mLen - 1
  var eMax = (1 << eLen) - 1
  var eBias = eMax >> 1
  var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
  var i = isLE ? 0 : (nBytes - 1)
  var d = isLE ? 1 : -1
  var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0

  value = Math.abs(value)

  if (isNaN(value) || value === Infinity) {
    m = isNaN(value) ? 1 : 0
    e = eMax
  } else {
    e = Math.floor(Math.log(value) / Math.LN2)
    if (value * (c = Math.pow(2, -e)) < 1) {
      e--
      c *= 2
    }
    if (e + eBias >= 1) {
      value += rt / c
    } else {
      value += rt * Math.pow(2, 1 - eBias)
    }
    if (value * c >= 2) {
      e++
      c /= 2
    }

    if (e + eBias >= eMax) {
      m = 0
      e = eMax
    } else if (e + eBias >= 1) {
      m = ((value * c) - 1) * Math.pow(2, mLen)
      e = e + eBias
    } else {
      m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
      e = 0
    }
  }

  for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}

  e = (e << mLen) | m
  eLen += mLen
  for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}

  buffer[offset + i - d] |= s * 128
}


/***/ }),

/***/ "./gulp/node_modules/isarray/index.js":
/*!********************************************!*\
  !*** ./gulp/node_modules/isarray/index.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

var toString = {}.toString;

module.exports = Array.isArray || function (arr) {
  return toString.call(arr) == '[object Array]';
};


/***/ }),

/***/ "./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js":
/*!**************************************************************************!*\
  !*** ./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js ***!
  \**************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
 * @license  MIT
 */
/* eslint-disable no-proto */



var base64 = __webpack_require__(/*! base64-js */ "./gulp/node_modules/base64-js/index.js")
var ieee754 = __webpack_require__(/*! ieee754 */ "./gulp/node_modules/ieee754/index.js")
var isArray = __webpack_require__(/*! isarray */ "./gulp/node_modules/isarray/index.js")

exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50

/**
 * If `Buffer.TYPED_ARRAY_SUPPORT`:
 *   === true    Use Uint8Array implementation (fastest)
 *   === false   Use Object implementation (most compatible, even IE6)
 *
 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
 * Opera 11.6+, iOS 4.2+.
 *
 * Due to various browser bugs, sometimes the Object implementation will be used even
 * when the browser supports typed arrays.
 *
 * Note:
 *
 *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
 *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
 *
 *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
 *
 *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
 *     incorrect length in some situations.

 * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
 * get the Object implementation, which is slower but behaves correctly.
 */
Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
  ? global.TYPED_ARRAY_SUPPORT
  : typedArraySupport()

/*
 * Export kMaxLength after typed array support is determined.
 */
exports.kMaxLength = kMaxLength()

function typedArraySupport () {
  try {
    var arr = new Uint8Array(1)
    arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
    return arr.foo() === 42 && // typed array instances can be augmented
        typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
        arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
  } catch (e) {
    return false
  }
}

function kMaxLength () {
  return Buffer.TYPED_ARRAY_SUPPORT
    ? 0x7fffffff
    : 0x3fffffff
}

function createBuffer (that, length) {
  if (kMaxLength() < length) {
    throw new RangeError('Invalid typed array length')
  }
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    // Return an augmented `Uint8Array` instance, for best performance
    that = new Uint8Array(length)
    that.__proto__ = Buffer.prototype
  } else {
    // Fallback: Return an object instance of the Buffer class
    if (that === null) {
      that = new Buffer(length)
    }
    that.length = length
  }

  return that
}

/**
 * The Buffer constructor returns instances of `Uint8Array` that have their
 * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
 * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
 * and the `Uint8Array` methods. Square bracket notation works as expected -- it
 * returns a single octet.
 *
 * The `Uint8Array` prototype remains unmodified.
 */

function Buffer (arg, encodingOrOffset, length) {
  if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
    return new Buffer(arg, encodingOrOffset, length)
  }

  // Common case.
  if (typeof arg === 'number') {
    if (typeof encodingOrOffset === 'string') {
      throw new Error(
        'If encoding is specified then the first argument must be a string'
      )
    }
    return allocUnsafe(this, arg)
  }
  return from(this, arg, encodingOrOffset, length)
}

Buffer.poolSize = 8192 // not used by this implementation

// TODO: Legacy, not needed anymore. Remove in next major version.
Buffer._augment = function (arr) {
  arr.__proto__ = Buffer.prototype
  return arr
}

function from (that, value, encodingOrOffset, length) {
  if (typeof value === 'number') {
    throw new TypeError('"value" argument must not be a number')
  }

  if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
    return fromArrayBuffer(that, value, encodingOrOffset, length)
  }

  if (typeof value === 'string') {
    return fromString(that, value, encodingOrOffset)
  }

  return fromObject(that, value)
}

/**
 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
 * if value is a number.
 * Buffer.from(str[, encoding])
 * Buffer.from(array)
 * Buffer.from(buffer)
 * Buffer.from(arrayBuffer[, byteOffset[, length]])
 **/
Buffer.from = function (value, encodingOrOffset, length) {
  return from(null, value, encodingOrOffset, length)
}

if (Buffer.TYPED_ARRAY_SUPPORT) {
  Buffer.prototype.__proto__ = Uint8Array.prototype
  Buffer.__proto__ = Uint8Array
  if (typeof Symbol !== 'undefined' && Symbol.species &&
      Buffer[Symbol.species] === Buffer) {
    // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
    Object.defineProperty(Buffer, Symbol.species, {
      value: null,
      configurable: true
    })
  }
}

function assertSize (size) {
  if (typeof size !== 'number') {
    throw new TypeError('"size" argument must be a number')
  } else if (size < 0) {
    throw new RangeError('"size" argument must not be negative')
  }
}

function alloc (that, size, fill, encoding) {
  assertSize(size)
  if (size <= 0) {
    return createBuffer(that, size)
  }
  if (fill !== undefined) {
    // Only pay attention to encoding if it's a string. This
    // prevents accidentally sending in a number that would
    // be interpretted as a start offset.
    return typeof encoding === 'string'
      ? createBuffer(that, size).fill(fill, encoding)
      : createBuffer(that, size).fill(fill)
  }
  return createBuffer(that, size)
}

/**
 * Creates a new filled Buffer instance.
 * alloc(size[, fill[, encoding]])
 **/
Buffer.alloc = function (size, fill, encoding) {
  return alloc(null, size, fill, encoding)
}

function allocUnsafe (that, size) {
  assertSize(size)
  that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
  if (!Buffer.TYPED_ARRAY_SUPPORT) {
    for (var i = 0; i < size; ++i) {
      that[i] = 0
    }
  }
  return that
}

/**
 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
 * */
Buffer.allocUnsafe = function (size) {
  return allocUnsafe(null, size)
}
/**
 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
 */
Buffer.allocUnsafeSlow = function (size) {
  return allocUnsafe(null, size)
}

function fromString (that, string, encoding) {
  if (typeof encoding !== 'string' || encoding === '') {
    encoding = 'utf8'
  }

  if (!Buffer.isEncoding(encoding)) {
    throw new TypeError('"encoding" must be a valid string encoding')
  }

  var length = byteLength(string, encoding) | 0
  that = createBuffer(that, length)

  var actual = that.write(string, encoding)

  if (actual !== length) {
    // Writing a hex string, for example, that contains invalid characters will
    // cause everything after the first invalid character to be ignored. (e.g.
    // 'abxxcd' will be treated as 'ab')
    that = that.slice(0, actual)
  }

  return that
}

function fromArrayLike (that, array) {
  var length = array.length < 0 ? 0 : checked(array.length) | 0
  that = createBuffer(that, length)
  for (var i = 0; i < length; i += 1) {
    that[i] = array[i] & 255
  }
  return that
}

function fromArrayBuffer (that, array, byteOffset, length) {
  array.byteLength // this throws if `array` is not a valid ArrayBuffer

  if (byteOffset < 0 || array.byteLength < byteOffset) {
    throw new RangeError('\'offset\' is out of bounds')
  }

  if (array.byteLength < byteOffset + (length || 0)) {
    throw new RangeError('\'length\' is out of bounds')
  }

  if (byteOffset === undefined && length === undefined) {
    array = new Uint8Array(array)
  } else if (length === undefined) {
    array = new Uint8Array(array, byteOffset)
  } else {
    array = new Uint8Array(array, byteOffset, length)
  }

  if (Buffer.TYPED_ARRAY_SUPPORT) {
    // Return an augmented `Uint8Array` instance, for best performance
    that = array
    that.__proto__ = Buffer.prototype
  } else {
    // Fallback: Return an object instance of the Buffer class
    that = fromArrayLike(that, array)
  }
  return that
}

function fromObject (that, obj) {
  if (Buffer.isBuffer(obj)) {
    var len = checked(obj.length) | 0
    that = createBuffer(that, len)

    if (that.length === 0) {
      return that
    }

    obj.copy(that, 0, 0, len)
    return that
  }

  if (obj) {
    if ((typeof ArrayBuffer !== 'undefined' &&
        obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
      if (typeof obj.length !== 'number' || isnan(obj.length)) {
        return createBuffer(that, 0)
      }
      return fromArrayLike(that, obj)
    }

    if (obj.type === 'Buffer' && isArray(obj.data)) {
      return fromArrayLike(that, obj.data)
    }
  }

  throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
}

function checked (length) {
  // Note: cannot use `length < kMaxLength()` here because that fails when
  // length is NaN (which is otherwise coerced to zero.)
  if (length >= kMaxLength()) {
    throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                         'size: 0x' + kMaxLength().toString(16) + ' bytes')
  }
  return length | 0
}

function SlowBuffer (length) {
  if (+length != length) { // eslint-disable-line eqeqeq
    length = 0
  }
  return Buffer.alloc(+length)
}

Buffer.isBuffer = function isBuffer (b) {
  return !!(b != null && b._isBuffer)
}

Buffer.compare = function compare (a, b) {
  if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
    throw new TypeError('Arguments must be Buffers')
  }

  if (a === b) return 0

  var x = a.length
  var y = b.length

  for (var i = 0, len = Math.min(x, y); i < len; ++i) {
    if (a[i] !== b[i]) {
      x = a[i]
      y = b[i]
      break
    }
  }

  if (x < y) return -1
  if (y < x) return 1
  return 0
}

Buffer.isEncoding = function isEncoding (encoding) {
  switch (String(encoding).toLowerCase()) {
    case 'hex':
    case 'utf8':
    case 'utf-8':
    case 'ascii':
    case 'latin1':
    case 'binary':
    case 'base64':
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      return true
    default:
      return false
  }
}

Buffer.concat = function concat (list, length) {
  if (!isArray(list)) {
    throw new TypeError('"list" argument must be an Array of Buffers')
  }

  if (list.length === 0) {
    return Buffer.alloc(0)
  }

  var i
  if (length === undefined) {
    length = 0
    for (i = 0; i < list.length; ++i) {
      length += list[i].length
    }
  }

  var buffer = Buffer.allocUnsafe(length)
  var pos = 0
  for (i = 0; i < list.length; ++i) {
    var buf = list[i]
    if (!Buffer.isBuffer(buf)) {
      throw new TypeError('"list" argument must be an Array of Buffers')
    }
    buf.copy(buffer, pos)
    pos += buf.length
  }
  return buffer
}

function byteLength (string, encoding) {
  if (Buffer.isBuffer(string)) {
    return string.length
  }
  if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
      (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
    return string.byteLength
  }
  if (typeof string !== 'string') {
    string = '' + string
  }

  var len = string.length
  if (len === 0) return 0

  // Use a for loop to avoid recursion
  var loweredCase = false
  for (;;) {
    switch (encoding) {
      case 'ascii':
      case 'latin1':
      case 'binary':
        return len
      case 'utf8':
      case 'utf-8':
      case undefined:
        return utf8ToBytes(string).length
      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return len * 2
      case 'hex':
        return len >>> 1
      case 'base64':
        return base64ToBytes(string).length
      default:
        if (loweredCase) return utf8ToBytes(string).length // assume utf8
        encoding = ('' + encoding).toLowerCase()
        loweredCase = true
    }
  }
}
Buffer.byteLength = byteLength

function slowToString (encoding, start, end) {
  var loweredCase = false

  // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
  // property of a typed array.

  // This behaves neither like String nor Uint8Array in that we set start/end
  // to their upper/lower bounds if the value passed is out of range.
  // undefined is handled specially as per ECMA-262 6th Edition,
  // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
  if (start === undefined || start < 0) {
    start = 0
  }
  // Return early if start > this.length. Done here to prevent potential uint32
  // coercion fail below.
  if (start > this.length) {
    return ''
  }

  if (end === undefined || end > this.length) {
    end = this.length
  }

  if (end <= 0) {
    return ''
  }

  // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
  end >>>= 0
  start >>>= 0

  if (end <= start) {
    return ''
  }

  if (!encoding) encoding = 'utf8'

  while (true) {
    switch (encoding) {
      case 'hex':
        return hexSlice(this, start, end)

      case 'utf8':
      case 'utf-8':
        return utf8Slice(this, start, end)

      case 'ascii':
        return asciiSlice(this, start, end)

      case 'latin1':
      case 'binary':
        return latin1Slice(this, start, end)

      case 'base64':
        return base64Slice(this, start, end)

      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return utf16leSlice(this, start, end)

      default:
        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
        encoding = (encoding + '').toLowerCase()
        loweredCase = true
    }
  }
}

// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
// Buffer instances.
Buffer.prototype._isBuffer = true

function swap (b, n, m) {
  var i = b[n]
  b[n] = b[m]
  b[m] = i
}

Buffer.prototype.swap16 = function swap16 () {
  var len = this.length
  if (len % 2 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 16-bits')
  }
  for (var i = 0; i < len; i += 2) {
    swap(this, i, i + 1)
  }
  return this
}

Buffer.prototype.swap32 = function swap32 () {
  var len = this.length
  if (len % 4 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 32-bits')
  }
  for (var i = 0; i < len; i += 4) {
    swap(this, i, i + 3)
    swap(this, i + 1, i + 2)
  }
  return this
}

Buffer.prototype.swap64 = function swap64 () {
  var len = this.length
  if (len % 8 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 64-bits')
  }
  for (var i = 0; i < len; i += 8) {
    swap(this, i, i + 7)
    swap(this, i + 1, i + 6)
    swap(this, i + 2, i + 5)
    swap(this, i + 3, i + 4)
  }
  return this
}

Buffer.prototype.toString = function toString () {
  var length = this.length | 0
  if (length === 0) return ''
  if (arguments.length === 0) return utf8Slice(this, 0, length)
  return slowToString.apply(this, arguments)
}

Buffer.prototype.equals = function equals (b) {
  if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
  if (this === b) return true
  return Buffer.compare(this, b) === 0
}

Buffer.prototype.inspect = function inspect () {
  var str = ''
  var max = exports.INSPECT_MAX_BYTES
  if (this.length > 0) {
    str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
    if (this.length > max) str += ' ... '
  }
  return '<Buffer ' + str + '>'
}

Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
  if (!Buffer.isBuffer(target)) {
    throw new TypeError('Argument must be a Buffer')
  }

  if (start === undefined) {
    start = 0
  }
  if (end === undefined) {
    end = target ? target.length : 0
  }
  if (thisStart === undefined) {
    thisStart = 0
  }
  if (thisEnd === undefined) {
    thisEnd = this.length
  }

  if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
    throw new RangeError('out of range index')
  }

  if (thisStart >= thisEnd && start >= end) {
    return 0
  }
  if (thisStart >= thisEnd) {
    return -1
  }
  if (start >= end) {
    return 1
  }

  start >>>= 0
  end >>>= 0
  thisStart >>>= 0
  thisEnd >>>= 0

  if (this === target) return 0

  var x = thisEnd - thisStart
  var y = end - start
  var len = Math.min(x, y)

  var thisCopy = this.slice(thisStart, thisEnd)
  var targetCopy = target.slice(start, end)

  for (var i = 0; i < len; ++i) {
    if (thisCopy[i] !== targetCopy[i]) {
      x = thisCopy[i]
      y = targetCopy[i]
      break
    }
  }

  if (x < y) return -1
  if (y < x) return 1
  return 0
}

// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
//
// Arguments:
// - buffer - a Buffer to search
// - val - a string, Buffer, or number
// - byteOffset - an index into `buffer`; will be clamped to an int32
// - encoding - an optional encoding, relevant is val is a string
// - dir - true for indexOf, false for lastIndexOf
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
  // Empty buffer means no match
  if (buffer.length === 0) return -1

  // Normalize byteOffset
  if (typeof byteOffset === 'string') {
    encoding = byteOffset
    byteOffset = 0
  } else if (byteOffset > 0x7fffffff) {
    byteOffset = 0x7fffffff
  } else if (byteOffset < -0x80000000) {
    byteOffset = -0x80000000
  }
  byteOffset = +byteOffset  // Coerce to Number.
  if (isNaN(byteOffset)) {
    // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
    byteOffset = dir ? 0 : (buffer.length - 1)
  }

  // Normalize byteOffset: negative offsets start from the end of the buffer
  if (byteOffset < 0) byteOffset = buffer.length + byteOffset
  if (byteOffset >= buffer.length) {
    if (dir) return -1
    else byteOffset = buffer.length - 1
  } else if (byteOffset < 0) {
    if (dir) byteOffset = 0
    else return -1
  }

  // Normalize val
  if (typeof val === 'string') {
    val = Buffer.from(val, encoding)
  }

  // Finally, search either indexOf (if dir is true) or lastIndexOf
  if (Buffer.isBuffer(val)) {
    // Special case: looking for empty string/buffer always fails
    if (val.length === 0) {
      return -1
    }
    return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
  } else if (typeof val === 'number') {
    val = val & 0xFF // Search for a byte value [0-255]
    if (Buffer.TYPED_ARRAY_SUPPORT &&
        typeof Uint8Array.prototype.indexOf === 'function') {
      if (dir) {
        return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
      } else {
        return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
      }
    }
    return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
  }

  throw new TypeError('val must be string, number or Buffer')
}

function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
  var indexSize = 1
  var arrLength = arr.length
  var valLength = val.length

  if (encoding !== undefined) {
    encoding = String(encoding).toLowerCase()
    if (encoding === 'ucs2' || encoding === 'ucs-2' ||
        encoding === 'utf16le' || encoding === 'utf-16le') {
      if (arr.length < 2 || val.length < 2) {
        return -1
      }
      indexSize = 2
      arrLength /= 2
      valLength /= 2
      byteOffset /= 2
    }
  }

  function read (buf, i) {
    if (indexSize === 1) {
      return buf[i]
    } else {
      return buf.readUInt16BE(i * indexSize)
    }
  }

  var i
  if (dir) {
    var foundIndex = -1
    for (i = byteOffset; i < arrLength; i++) {
      if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
        if (foundIndex === -1) foundIndex = i
        if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
      } else {
        if (foundIndex !== -1) i -= i - foundIndex
        foundIndex = -1
      }
    }
  } else {
    if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
    for (i = byteOffset; i >= 0; i--) {
      var found = true
      for (var j = 0; j < valLength; j++) {
        if (read(arr, i + j) !== read(val, j)) {
          found = false
          break
        }
      }
      if (found) return i
    }
  }

  return -1
}

Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
  return this.indexOf(val, byteOffset, encoding) !== -1
}

Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
  return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
}

Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
  return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
}

function hexWrite (buf, string, offset, length) {
  offset = Number(offset) || 0
  var remaining = buf.length - offset
  if (!length) {
    length = remaining
  } else {
    length = Number(length)
    if (length > remaining) {
      length = remaining
    }
  }

  // must be an even number of digits
  var strLen = string.length
  if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')

  if (length > strLen / 2) {
    length = strLen / 2
  }
  for (var i = 0; i < length; ++i) {
    var parsed = parseInt(string.substr(i * 2, 2), 16)
    if (isNaN(parsed)) return i
    buf[offset + i] = parsed
  }
  return i
}

function utf8Write (buf, string, offset, length) {
  return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
}

function asciiWrite (buf, string, offset, length) {
  return blitBuffer(asciiToBytes(string), buf, offset, length)
}

function latin1Write (buf, string, offset, length) {
  return asciiWrite(buf, string, offset, length)
}

function base64Write (buf, string, offset, length) {
  return blitBuffer(base64ToBytes(string), buf, offset, length)
}

function ucs2Write (buf, string, offset, length) {
  return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
}

Buffer.prototype.write = function write (string, offset, length, encoding) {
  // Buffer#write(string)
  if (offset === undefined) {
    encoding = 'utf8'
    length = this.length
    offset = 0
  // Buffer#write(string, encoding)
  } else if (length === undefined && typeof offset === 'string') {
    encoding = offset
    length = this.length
    offset = 0
  // Buffer#write(string, offset[, length][, encoding])
  } else if (isFinite(offset)) {
    offset = offset | 0
    if (isFinite(length)) {
      length = length | 0
      if (encoding === undefined) encoding = 'utf8'
    } else {
      encoding = length
      length = undefined
    }
  // legacy write(string, encoding, offset, length) - remove in v0.13
  } else {
    throw new Error(
      'Buffer.write(string, encoding, offset[, length]) is no longer supported'
    )
  }

  var remaining = this.length - offset
  if (length === undefined || length > remaining) length = remaining

  if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
    throw new RangeError('Attempt to write outside buffer bounds')
  }

  if (!encoding) encoding = 'utf8'

  var loweredCase = false
  for (;;) {
    switch (encoding) {
      case 'hex':
        return hexWrite(this, string, offset, length)

      case 'utf8':
      case 'utf-8':
        return utf8Write(this, string, offset, length)

      case 'ascii':
        return asciiWrite(this, string, offset, length)

      case 'latin1':
      case 'binary':
        return latin1Write(this, string, offset, length)

      case 'base64':
        // Warning: maxLength not taken into account in base64Write
        return base64Write(this, string, offset, length)

      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return ucs2Write(this, string, offset, length)

      default:
        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
        encoding = ('' + encoding).toLowerCase()
        loweredCase = true
    }
  }
}

Buffer.prototype.toJSON = function toJSON () {
  return {
    type: 'Buffer',
    data: Array.prototype.slice.call(this._arr || this, 0)
  }
}

function base64Slice (buf, start, end) {
  if (start === 0 && end === buf.length) {
    return base64.fromByteArray(buf)
  } else {
    return base64.fromByteArray(buf.slice(start, end))
  }
}

function utf8Slice (buf, start, end) {
  end = Math.min(buf.length, end)
  var res = []

  var i = start
  while (i < end) {
    var firstByte = buf[i]
    var codePoint = null
    var bytesPerSequence = (firstByte > 0xEF) ? 4
      : (firstByte > 0xDF) ? 3
      : (firstByte > 0xBF) ? 2
      : 1

    if (i + bytesPerSequence <= end) {
      var secondByte, thirdByte, fourthByte, tempCodePoint

      switch (bytesPerSequence) {
        case 1:
          if (firstByte < 0x80) {
            codePoint = firstByte
          }
          break
        case 2:
          secondByte = buf[i + 1]
          if ((secondByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
            if (tempCodePoint > 0x7F) {
              codePoint = tempCodePoint
            }
          }
          break
        case 3:
          secondByte = buf[i + 1]
          thirdByte = buf[i + 2]
          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
            if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
              codePoint = tempCodePoint
            }
          }
          break
        case 4:
          secondByte = buf[i + 1]
          thirdByte = buf[i + 2]
          fourthByte = buf[i + 3]
          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
            if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
              codePoint = tempCodePoint
            }
          }
      }
    }

    if (codePoint === null) {
      // we did not generate a valid codePoint so insert a
      // replacement char (U+FFFD) and advance only 1 byte
      codePoint = 0xFFFD
      bytesPerSequence = 1
    } else if (codePoint > 0xFFFF) {
      // encode to utf16 (surrogate pair dance)
      codePoint -= 0x10000
      res.push(codePoint >>> 10 & 0x3FF | 0xD800)
      codePoint = 0xDC00 | codePoint & 0x3FF
    }

    res.push(codePoint)
    i += bytesPerSequence
  }

  return decodeCodePointsArray(res)
}

// Based on http://stackoverflow.com/a/22747272/680742, the browser with
// the lowest limit is Chrome, with 0x10000 args.
// We go 1 magnitude less, for safety
var MAX_ARGUMENTS_LENGTH = 0x1000

function decodeCodePointsArray (codePoints) {
  var len = codePoints.length
  if (len <= MAX_ARGUMENTS_LENGTH) {
    return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
  }

  // Decode in chunks to avoid "call stack size exceeded".
  var res = ''
  var i = 0
  while (i < len) {
    res += String.fromCharCode.apply(
      String,
      codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
    )
  }
  return res
}

function asciiSlice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; ++i) {
    ret += String.fromCharCode(buf[i] & 0x7F)
  }
  return ret
}

function latin1Slice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; ++i) {
    ret += String.fromCharCode(buf[i])
  }
  return ret
}

function hexSlice (buf, start, end) {
  var len = buf.length

  if (!start || start < 0) start = 0
  if (!end || end < 0 || end > len) end = len

  var out = ''
  for (var i = start; i < end; ++i) {
    out += toHex(buf[i])
  }
  return out
}

function utf16leSlice (buf, start, end) {
  var bytes = buf.slice(start, end)
  var res = ''
  for (var i = 0; i < bytes.length; i += 2) {
    res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
  }
  return res
}

Buffer.prototype.slice = function slice (start, end) {
  var len = this.length
  start = ~~start
  end = end === undefined ? len : ~~end

  if (start < 0) {
    start += len
    if (start < 0) start = 0
  } else if (start > len) {
    start = len
  }

  if (end < 0) {
    end += len
    if (end < 0) end = 0
  } else if (end > len) {
    end = len
  }

  if (end < start) end = start

  var newBuf
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    newBuf = this.subarray(start, end)
    newBuf.__proto__ = Buffer.prototype
  } else {
    var sliceLen = end - start
    newBuf = new Buffer(sliceLen, undefined)
    for (var i = 0; i < sliceLen; ++i) {
      newBuf[i] = this[i + start]
    }
  }

  return newBuf
}

/*
 * Need to make sure that buffer isn't trying to write out of bounds.
 */
function checkOffset (offset, ext, length) {
  if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
  if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
}

Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var val = this[offset]
  var mul = 1
  var i = 0
  while (++i < byteLength && (mul *= 0x100)) {
    val += this[offset + i] * mul
  }

  return val
}

Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) {
    checkOffset(offset, byteLength, this.length)
  }

  var val = this[offset + --byteLength]
  var mul = 1
  while (byteLength > 0 && (mul *= 0x100)) {
    val += this[offset + --byteLength] * mul
  }

  return val
}

Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 1, this.length)
  return this[offset]
}

Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  return this[offset] | (this[offset + 1] << 8)
}

Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  return (this[offset] << 8) | this[offset + 1]
}

Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return ((this[offset]) |
      (this[offset + 1] << 8) |
      (this[offset + 2] << 16)) +
      (this[offset + 3] * 0x1000000)
}

Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset] * 0x1000000) +
    ((this[offset + 1] << 16) |
    (this[offset + 2] << 8) |
    this[offset + 3])
}

Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var val = this[offset]
  var mul = 1
  var i = 0
  while (++i < byteLength && (mul *= 0x100)) {
    val += this[offset + i] * mul
  }
  mul *= 0x80

  if (val >= mul) val -= Math.pow(2, 8 * byteLength)

  return val
}

Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var i = byteLength
  var mul = 1
  var val = this[offset + --i]
  while (i > 0 && (mul *= 0x100)) {
    val += this[offset + --i] * mul
  }
  mul *= 0x80

  if (val >= mul) val -= Math.pow(2, 8 * byteLength)

  return val
}

Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 1, this.length)
  if (!(this[offset] & 0x80)) return (this[offset])
  return ((0xff - this[offset] + 1) * -1)
}

Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  var val = this[offset] | (this[offset + 1] << 8)
  return (val & 0x8000) ? val | 0xFFFF0000 : val
}

Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  var val = this[offset + 1] | (this[offset] << 8)
  return (val & 0x8000) ? val | 0xFFFF0000 : val
}

Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset]) |
    (this[offset + 1] << 8) |
    (this[offset + 2] << 16) |
    (this[offset + 3] << 24)
}

Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset] << 24) |
    (this[offset + 1] << 16) |
    (this[offset + 2] << 8) |
    (this[offset + 3])
}

Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)
  return ieee754.read(this, offset, true, 23, 4)
}

Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)
  return ieee754.read(this, offset, false, 23, 4)
}

Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 8, this.length)
  return ieee754.read(this, offset, true, 52, 8)
}

Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 8, this.length)
  return ieee754.read(this, offset, false, 52, 8)
}

function checkInt (buf, value, offset, ext, max, min) {
  if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
  if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
  if (offset + ext > buf.length) throw new RangeError('Index out of range')
}

Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) {
    var maxBytes = Math.pow(2, 8 * byteLength) - 1
    checkInt(this, value, offset, byteLength, maxBytes, 0)
  }

  var mul = 1
  var i = 0
  this[offset] = value & 0xFF
  while (++i < byteLength && (mul *= 0x100)) {
    this[offset + i] = (value / mul) & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) {
    var maxBytes = Math.pow(2, 8 * byteLength) - 1
    checkInt(this, value, offset, byteLength, maxBytes, 0)
  }

  var i = byteLength - 1
  var mul = 1
  this[offset + i] = value & 0xFF
  while (--i >= 0 && (mul *= 0x100)) {
    this[offset + i] = (value / mul) & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
  this[offset] = (value & 0xff)
  return offset + 1
}

function objectWriteUInt16 (buf, value, offset, littleEndian) {
  if (value < 0) value = 0xffff + value + 1
  for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
    buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
      (littleEndian ? i : 1 - i) * 8
  }
}

Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value & 0xff)
    this[offset + 1] = (value >>> 8)
  } else {
    objectWriteUInt16(this, value, offset, true)
  }
  return offset + 2
}

Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 8)
    this[offset + 1] = (value & 0xff)
  } else {
    objectWriteUInt16(this, value, offset, false)
  }
  return offset + 2
}

function objectWriteUInt32 (buf, value, offset, littleEndian) {
  if (value < 0) value = 0xffffffff + value + 1
  for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
    buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
  }
}

Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset + 3] = (value >>> 24)
    this[offset + 2] = (value >>> 16)
    this[offset + 1] = (value >>> 8)
    this[offset] = (value & 0xff)
  } else {
    objectWriteUInt32(this, value, offset, true)
  }
  return offset + 4
}

Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 24)
    this[offset + 1] = (value >>> 16)
    this[offset + 2] = (value >>> 8)
    this[offset + 3] = (value & 0xff)
  } else {
    objectWriteUInt32(this, value, offset, false)
  }
  return offset + 4
}

Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) {
    var limit = Math.pow(2, 8 * byteLength - 1)

    checkInt(this, value, offset, byteLength, limit - 1, -limit)
  }

  var i = 0
  var mul = 1
  var sub = 0
  this[offset] = value & 0xFF
  while (++i < byteLength && (mul *= 0x100)) {
    if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
      sub = 1
    }
    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) {
    var limit = Math.pow(2, 8 * byteLength - 1)

    checkInt(this, value, offset, byteLength, limit - 1, -limit)
  }

  var i = byteLength - 1
  var mul = 1
  var sub = 0
  this[offset + i] = value & 0xFF
  while (--i >= 0 && (mul *= 0x100)) {
    if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
      sub = 1
    }
    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
  if (value < 0) value = 0xff + value + 1
  this[offset] = (value & 0xff)
  return offset + 1
}

Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value & 0xff)
    this[offset + 1] = (value >>> 8)
  } else {
    objectWriteUInt16(this, value, offset, true)
  }
  return offset + 2
}

Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 8)
    this[offset + 1] = (value & 0xff)
  } else {
    objectWriteUInt16(this, value, offset, false)
  }
  return offset + 2
}

Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value & 0xff)
    this[offset + 1] = (value >>> 8)
    this[offset + 2] = (value >>> 16)
    this[offset + 3] = (value >>> 24)
  } else {
    objectWriteUInt32(this, value, offset, true)
  }
  return offset + 4
}

Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  if (value < 0) value = 0xffffffff + value + 1
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 24)
    this[offset + 1] = (value >>> 16)
    this[offset + 2] = (value >>> 8)
    this[offset + 3] = (value & 0xff)
  } else {
    objectWriteUInt32(this, value, offset, false)
  }
  return offset + 4
}

function checkIEEE754 (buf, value, offset, ext, max, min) {
  if (offset + ext > buf.length) throw new RangeError('Index out of range')
  if (offset < 0) throw new RangeError('Index out of range')
}

function writeFloat (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
  }
  ieee754.write(buf, value, offset, littleEndian, 23, 4)
  return offset + 4
}

Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
  return writeFloat(this, value, offset, true, noAssert)
}

Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
  return writeFloat(this, value, offset, false, noAssert)
}

function writeDouble (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
  }
  ieee754.write(buf, value, offset, littleEndian, 52, 8)
  return offset + 8
}

Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
  return writeDouble(this, value, offset, true, noAssert)
}

Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
  return writeDouble(this, value, offset, false, noAssert)
}

// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function copy (target, targetStart, start, end) {
  if (!start) start = 0
  if (!end && end !== 0) end = this.length
  if (targetStart >= target.length) targetStart = target.length
  if (!targetStart) targetStart = 0
  if (end > 0 && end < start) end = start

  // Copy 0 bytes; we're done
  if (end === start) return 0
  if (target.length === 0 || this.length === 0) return 0

  // Fatal error conditions
  if (targetStart < 0) {
    throw new RangeError('targetStart out of bounds')
  }
  if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
  if (end < 0) throw new RangeError('sourceEnd out of bounds')

  // Are we oob?
  if (end > this.length) end = this.length
  if (target.length - targetStart < end - start) {
    end = target.length - targetStart + start
  }

  var len = end - start
  var i

  if (this === target && start < targetStart && targetStart < end) {
    // descending copy from end
    for (i = len - 1; i >= 0; --i) {
      target[i + targetStart] = this[i + start]
    }
  } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
    // ascending copy from start
    for (i = 0; i < len; ++i) {
      target[i + targetStart] = this[i + start]
    }
  } else {
    Uint8Array.prototype.set.call(
      target,
      this.subarray(start, start + len),
      targetStart
    )
  }

  return len
}

// Usage:
//    buffer.fill(number[, offset[, end]])
//    buffer.fill(buffer[, offset[, end]])
//    buffer.fill(string[, offset[, end]][, encoding])
Buffer.prototype.fill = function fill (val, start, end, encoding) {
  // Handle string cases:
  if (typeof val === 'string') {
    if (typeof start === 'string') {
      encoding = start
      start = 0
      end = this.length
    } else if (typeof end === 'string') {
      encoding = end
      end = this.length
    }
    if (val.length === 1) {
      var code = val.charCodeAt(0)
      if (code < 256) {
        val = code
      }
    }
    if (encoding !== undefined && typeof encoding !== 'string') {
      throw new TypeError('encoding must be a string')
    }
    if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
      throw new TypeError('Unknown encoding: ' + encoding)
    }
  } else if (typeof val === 'number') {
    val = val & 255
  }

  // Invalid ranges are not set to a default, so can range check early.
  if (start < 0 || this.length < start || this.length < end) {
    throw new RangeError('Out of range index')
  }

  if (end <= start) {
    return this
  }

  start = start >>> 0
  end = end === undefined ? this.length : end >>> 0

  if (!val) val = 0

  var i
  if (typeof val === 'number') {
    for (i = start; i < end; ++i) {
      this[i] = val
    }
  } else {
    var bytes = Buffer.isBuffer(val)
      ? val
      : utf8ToBytes(new Buffer(val, encoding).toString())
    var len = bytes.length
    for (i = 0; i < end - start; ++i) {
      this[i + start] = bytes[i % len]
    }
  }

  return this
}

// HELPER FUNCTIONS
// ================

var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g

function base64clean (str) {
  // Node strips out invalid characters like \n and \t from the string, base64-js does not
  str = stringtrim(str).replace(INVALID_BASE64_RE, '')
  // Node converts strings with length < 2 to ''
  if (str.length < 2) return ''
  // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
  while (str.length % 4 !== 0) {
    str = str + '='
  }
  return str
}

function stringtrim (str) {
  if (str.trim) return str.trim()
  return str.replace(/^\s+|\s+$/g, '')
}

function toHex (n) {
  if (n < 16) return '0' + n.toString(16)
  return n.toString(16)
}

function utf8ToBytes (string, units) {
  units = units || Infinity
  var codePoint
  var length = string.length
  var leadSurrogate = null
  var bytes = []

  for (var i = 0; i < length; ++i) {
    codePoint = string.charCodeAt(i)

    // is surrogate component
    if (codePoint > 0xD7FF && codePoint < 0xE000) {
      // last char was a lead
      if (!leadSurrogate) {
        // no lead yet
        if (codePoint > 0xDBFF) {
          // unexpected trail
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          continue
        } else if (i + 1 === length) {
          // unpaired lead
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          continue
        }

        // valid lead
        leadSurrogate = codePoint

        continue
      }

      // 2 leads in a row
      if (codePoint < 0xDC00) {
        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
        leadSurrogate = codePoint
        continue
      }

      // valid surrogate pair
      codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
    } else if (leadSurrogate) {
      // valid bmp char, but last char was a lead
      if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
    }

    leadSurrogate = null

    // encode utf8
    if (codePoint < 0x80) {
      if ((units -= 1) < 0) break
      bytes.push(codePoint)
    } else if (codePoint < 0x800) {
      if ((units -= 2) < 0) break
      bytes.push(
        codePoint >> 0x6 | 0xC0,
        codePoint & 0x3F | 0x80
      )
    } else if (codePoint < 0x10000) {
      if ((units -= 3) < 0) break
      bytes.push(
        codePoint >> 0xC | 0xE0,
        codePoint >> 0x6 & 0x3F | 0x80,
        codePoint & 0x3F | 0x80
      )
    } else if (codePoint < 0x110000) {
      if ((units -= 4) < 0) break
      bytes.push(
        codePoint >> 0x12 | 0xF0,
        codePoint >> 0xC & 0x3F | 0x80,
        codePoint >> 0x6 & 0x3F | 0x80,
        codePoint & 0x3F | 0x80
      )
    } else {
      throw new Error('Invalid code point')
    }
  }

  return bytes
}

function asciiToBytes (str) {
  var byteArray = []
  for (var i = 0; i < str.length; ++i) {
    // Node's code seems to be doing this and not & 0x7F..
    byteArray.push(str.charCodeAt(i) & 0xFF)
  }
  return byteArray
}

function utf16leToBytes (str, units) {
  var c, hi, lo
  var byteArray = []
  for (var i = 0; i < str.length; ++i) {
    if ((units -= 2) < 0) break

    c = str.charCodeAt(i)
    hi = c >> 8
    lo = c % 256
    byteArray.push(lo)
    byteArray.push(hi)
  }

  return byteArray
}

function base64ToBytes (str) {
  return base64.toByteArray(base64clean(str))
}

function blitBuffer (src, dst, offset, length) {
  for (var i = 0; i < length; ++i) {
    if ((i + offset >= dst.length) || (i >= src.length)) break
    dst[i + offset] = src[i]
  }
  return i
}

function isnan (val) {
  return val !== val // eslint-disable-line no-self-compare
}

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./gulp/node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./gulp/node_modules/webpack/buildin/global.js":
/*!***********************************!*\
  !*** (webpack)/buildin/global.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || new Function("return this")();
} catch (e) {
	// This works if the window reference is available
	if (typeof window === "object") g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ "./node_modules/ajv/lib/ajv.js":
/*!*************************************!*\
  !*** ./node_modules/ajv/lib/ajv.js ***!
  \*************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var compileSchema = __webpack_require__(/*! ./compile */ "./node_modules/ajv/lib/compile/index.js")
  , resolve = __webpack_require__(/*! ./compile/resolve */ "./node_modules/ajv/lib/compile/resolve.js")
  , Cache = __webpack_require__(/*! ./cache */ "./node_modules/ajv/lib/cache.js")
  , SchemaObject = __webpack_require__(/*! ./compile/schema_obj */ "./node_modules/ajv/lib/compile/schema_obj.js")
  , stableStringify = __webpack_require__(/*! fast-json-stable-stringify */ "./node_modules/fast-json-stable-stringify/index.js")
  , formats = __webpack_require__(/*! ./compile/formats */ "./node_modules/ajv/lib/compile/formats.js")
  , rules = __webpack_require__(/*! ./compile/rules */ "./node_modules/ajv/lib/compile/rules.js")
  , $dataMetaSchema = __webpack_require__(/*! ./data */ "./node_modules/ajv/lib/data.js")
  , util = __webpack_require__(/*! ./compile/util */ "./node_modules/ajv/lib/compile/util.js");

module.exports = Ajv;

Ajv.prototype.validate = validate;
Ajv.prototype.compile = compile;
Ajv.prototype.addSchema = addSchema;
Ajv.prototype.addMetaSchema = addMetaSchema;
Ajv.prototype.validateSchema = validateSchema;
Ajv.prototype.getSchema = getSchema;
Ajv.prototype.removeSchema = removeSchema;
Ajv.prototype.addFormat = addFormat;
Ajv.prototype.errorsText = errorsText;

Ajv.prototype._addSchema = _addSchema;
Ajv.prototype._compile = _compile;

Ajv.prototype.compileAsync = __webpack_require__(/*! ./compile/async */ "./node_modules/ajv/lib/compile/async.js");
var customKeyword = __webpack_require__(/*! ./keyword */ "./node_modules/ajv/lib/keyword.js");
Ajv.prototype.addKeyword = customKeyword.add;
Ajv.prototype.getKeyword = customKeyword.get;
Ajv.prototype.removeKeyword = customKeyword.remove;
Ajv.prototype.validateKeyword = customKeyword.validate;

var errorClasses = __webpack_require__(/*! ./compile/error_classes */ "./node_modules/ajv/lib/compile/error_classes.js");
Ajv.ValidationError = errorClasses.Validation;
Ajv.MissingRefError = errorClasses.MissingRef;
Ajv.$dataMetaSchema = $dataMetaSchema;

var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema';

var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes', 'strictDefaults' ];
var META_SUPPORT_DATA = ['/properties'];

/**
 * Creates validator instance.
 * Usage: `Ajv(opts)`
 * @param {Object} opts optional options
 * @return {Object} ajv instance
 */
function Ajv(opts) {
  if (!(this instanceof Ajv)) return new Ajv(opts);
  opts = this._opts = util.copy(opts) || {};
  setLogger(this);
  this._schemas = {};
  this._refs = {};
  this._fragments = {};
  this._formats = formats(opts.format);

  this._cache = opts.cache || new Cache;
  this._loadingSchemas = {};
  this._compilations = [];
  this.RULES = rules();
  this._getId = chooseGetId(opts);

  opts.loopRequired = opts.loopRequired || Infinity;
  if (opts.errorDataPath == 'property') opts._errorDataPathProperty = true;
  if (opts.serialize === undefined) opts.serialize = stableStringify;
  this._metaOpts = getMetaSchemaOptions(this);

  if (opts.formats) addInitialFormats(this);
  if (opts.keywords) addInitialKeywords(this);
  addDefaultMetaSchema(this);
  if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta);
  if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: 'boolean'}});
  addInitialSchemas(this);
}



/**
 * Validate data using schema
 * Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize.
 * @this   Ajv
 * @param  {String|Object} schemaKeyRef key, ref or schema object
 * @param  {Any} data to be validated
 * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`).
 */
function validate(schemaKeyRef, data) {
  var v;
  if (typeof schemaKeyRef == 'string') {
    v = this.getSchema(schemaKeyRef);
    if (!v) throw new Error('no schema with key or ref "' + schemaKeyRef + '"');
  } else {
    var schemaObj = this._addSchema(schemaKeyRef);
    v = schemaObj.validate || this._compile(schemaObj);
  }

  var valid = v(data);
  if (v.$async !== true) this.errors = v.errors;
  return valid;
}


/**
 * Create validating function for passed schema.
 * @this   Ajv
 * @param  {Object} schema schema object
 * @param  {Boolean} _meta true if schema is a meta-schema. Used internally to compile meta schemas of custom keywords.
 * @return {Function} validating function
 */
function compile(schema, _meta) {
  var schemaObj = this._addSchema(schema, undefined, _meta);
  return schemaObj.validate || this._compile(schemaObj);
}


/**
 * Adds schema to the instance.
 * @this   Ajv
 * @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored.
 * @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
 * @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead.
 * @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead.
 * @return {Ajv} this for method chaining
 */
function addSchema(schema, key, _skipValidation, _meta) {
  if (Array.isArray(schema)){
    for (var i=0; i<schema.length; i++) this.addSchema(schema[i], undefined, _skipValidation, _meta);
    return this;
  }
  var id = this._getId(schema);
  if (id !== undefined && typeof id != 'string')
    throw new Error('schema id must be string');
  key = resolve.normalizeId(key || id);
  checkUnique(this, key);
  this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
  return this;
}


/**
 * Add schema that will be used to validate other schemas
 * options in META_IGNORE_OPTIONS are alway set to false
 * @this   Ajv
 * @param {Object} schema schema object
 * @param {String} key optional schema key
 * @param {Boolean} skipValidation true to skip schema validation, can be used to override validateSchema option for meta-schema
 * @return {Ajv} this for method chaining
 */
function addMetaSchema(schema, key, skipValidation) {
  this.addSchema(schema, key, skipValidation, true);
  return this;
}


/**
 * Validate schema
 * @this   Ajv
 * @param {Object} schema schema to validate
 * @param {Boolean} throwOrLogError pass true to throw (or log) an error if invalid
 * @return {Boolean} true if schema is valid
 */
function validateSchema(schema, throwOrLogError) {
  var $schema = schema.$schema;
  if ($schema !== undefined && typeof $schema != 'string')
    throw new Error('$schema must be a string');
  $schema = $schema || this._opts.defaultMeta || defaultMeta(this);
  if (!$schema) {
    this.logger.warn('meta-schema not available');
    this.errors = null;
    return true;
  }
  var valid = this.validate($schema, schema);
  if (!valid && throwOrLogError) {
    var message = 'schema is invalid: ' + this.errorsText();
    if (this._opts.validateSchema == 'log') this.logger.error(message);
    else throw new Error(message);
  }
  return valid;
}


function defaultMeta(self) {
  var meta = self._opts.meta;
  self._opts.defaultMeta = typeof meta == 'object'
                            ? self._getId(meta) || meta
                            : self.getSchema(META_SCHEMA_ID)
                              ? META_SCHEMA_ID
                              : undefined;
  return self._opts.defaultMeta;
}


/**
 * Get compiled schema from the instance by `key` or `ref`.
 * @this   Ajv
 * @param  {String} keyRef `key` that was passed to `addSchema` or full schema reference (`schema.id` or resolved id).
 * @return {Function} schema validating function (with property `schema`).
 */
function getSchema(keyRef) {
  var schemaObj = _getSchemaObj(this, keyRef);
  switch (typeof schemaObj) {
    case 'object': return schemaObj.validate || this._compile(schemaObj);
    case 'string': return this.getSchema(schemaObj);
    case 'undefined': return _getSchemaFragment(this, keyRef);
  }
}


function _getSchemaFragment(self, ref) {
  var res = resolve.schema.call(self, { schema: {} }, ref);
  if (res) {
    var schema = res.schema
      , root = res.root
      , baseId = res.baseId;
    var v = compileSchema.call(self, schema, root, undefined, baseId);
    self._fragments[ref] = new SchemaObject({
      ref: ref,
      fragment: true,
      schema: schema,
      root: root,
      baseId: baseId,
      validate: v
    });
    return v;
  }
}


function _getSchemaObj(self, keyRef) {
  keyRef = resolve.normalizeId(keyRef);
  return self._schemas[keyRef] || self._refs[keyRef] || self._fragments[keyRef];
}


/**
 * Remove cached schema(s).
 * If no parameter is passed all schemas but meta-schemas are removed.
 * If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed.
 * Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
 * @this   Ajv
 * @param  {String|Object|RegExp} schemaKeyRef key, ref, pattern to match key/ref or schema object
 * @return {Ajv} this for method chaining
 */
function removeSchema(schemaKeyRef) {
  if (schemaKeyRef instanceof RegExp) {
    _removeAllSchemas(this, this._schemas, schemaKeyRef);
    _removeAllSchemas(this, this._refs, schemaKeyRef);
    return this;
  }
  switch (typeof schemaKeyRef) {
    case 'undefined':
      _removeAllSchemas(this, this._schemas);
      _removeAllSchemas(this, this._refs);
      this._cache.clear();
      return this;
    case 'string':
      var schemaObj = _getSchemaObj(this, schemaKeyRef);
      if (schemaObj) this._cache.del(schemaObj.cacheKey);
      delete this._schemas[schemaKeyRef];
      delete this._refs[schemaKeyRef];
      return this;
    case 'object':
      var serialize = this._opts.serialize;
      var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef;
      this._cache.del(cacheKey);
      var id = this._getId(schemaKeyRef);
      if (id) {
        id = resolve.normalizeId(id);
        delete this._schemas[id];
        delete this._refs[id];
      }
  }
  return this;
}


function _removeAllSchemas(self, schemas, regex) {
  for (var keyRef in schemas) {
    var schemaObj = schemas[keyRef];
    if (!schemaObj.meta && (!regex || regex.test(keyRef))) {
      self._cache.del(schemaObj.cacheKey);
      delete schemas[keyRef];
    }
  }
}


/* @this   Ajv */
function _addSchema(schema, skipValidation, meta, shouldAddSchema) {
  if (typeof schema != 'object' && typeof schema != 'boolean')
    throw new Error('schema should be object or boolean');
  var serialize = this._opts.serialize;
  var cacheKey = serialize ? serialize(schema) : schema;
  var cached = this._cache.get(cacheKey);
  if (cached) return cached;

  shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false;

  var id = resolve.normalizeId(this._getId(schema));
  if (id && shouldAddSchema) checkUnique(this, id);

  var willValidate = this._opts.validateSchema !== false && !skipValidation;
  var recursiveMeta;
  if (willValidate && !(recursiveMeta = id && id == resolve.normalizeId(schema.$schema)))
    this.validateSchema(schema, true);

  var localRefs = resolve.ids.call(this, schema);

  var schemaObj = new SchemaObject({
    id: id,
    schema: schema,
    localRefs: localRefs,
    cacheKey: cacheKey,
    meta: meta
  });

  if (id[0] != '#' && shouldAddSchema) this._refs[id] = schemaObj;
  this._cache.put(cacheKey, schemaObj);

  if (willValidate && recursiveMeta) this.validateSchema(schema, true);

  return schemaObj;
}


/* @this   Ajv */
function _compile(schemaObj, root) {
  if (schemaObj.compiling) {
    schemaObj.validate = callValidate;
    callValidate.schema = schemaObj.schema;
    callValidate.errors = null;
    callValidate.root = root ? root : callValidate;
    if (schemaObj.schema.$async === true)
      callValidate.$async = true;
    return callValidate;
  }
  schemaObj.compiling = true;

  var currentOpts;
  if (schemaObj.meta) {
    currentOpts = this._opts;
    this._opts = this._metaOpts;
  }

  var v;
  try { v = compileSchema.call(this, schemaObj.schema, root, schemaObj.localRefs); }
  catch(e) {
    delete schemaObj.validate;
    throw e;
  }
  finally {
    schemaObj.compiling = false;
    if (schemaObj.meta) this._opts = currentOpts;
  }

  schemaObj.validate = v;
  schemaObj.refs = v.refs;
  schemaObj.refVal = v.refVal;
  schemaObj.root = v.root;
  return v;


  /* @this   {*} - custom context, see passContext option */
  function callValidate() {
    /* jshint validthis: true */
    var _validate = schemaObj.validate;
    var result = _validate.apply(this, arguments);
    callValidate.errors = _validate.errors;
    return result;
  }
}


function chooseGetId(opts) {
  switch (opts.schemaId) {
    case 'auto': return _get$IdOrId;
    case 'id': return _getId;
    default: return _get$Id;
  }
}

/* @this   Ajv */
function _getId(schema) {
  if (schema.$id) this.logger.warn('schema $id ignored', schema.$id);
  return schema.id;
}

/* @this   Ajv */
function _get$Id(schema) {
  if (schema.id) this.logger.warn('schema id ignored', schema.id);
  return schema.$id;
}


function _get$IdOrId(schema) {
  if (schema.$id && schema.id && schema.$id != schema.id)
    throw new Error('schema $id is different from id');
  return schema.$id || schema.id;
}


/**
 * Convert array of error message objects to string
 * @this   Ajv
 * @param  {Array<Object>} errors optional array of validation errors, if not passed errors from the instance are used.
 * @param  {Object} options optional options with properties `separator` and `dataVar`.
 * @return {String} human readable string with all errors descriptions
 */
function errorsText(errors, options) {
  errors = errors || this.errors;
  if (!errors) return 'No errors';
  options = options || {};
  var separator = options.separator === undefined ? ', ' : options.separator;
  var dataVar = options.dataVar === undefined ? 'data' : options.dataVar;

  var text = '';
  for (var i=0; i<errors.length; i++) {
    var e = errors[i];
    if (e) text += dataVar + e.dataPath + ' ' + e.message + separator;
  }
  return text.slice(0, -separator.length);
}


/**
 * Add custom format
 * @this   Ajv
 * @param {String} name format name
 * @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
 * @return {Ajv} this for method chaining
 */
function addFormat(name, format) {
  if (typeof format == 'string') format = new RegExp(format);
  this._formats[name] = format;
  return this;
}


function addDefaultMetaSchema(self) {
  var $dataSchema;
  if (self._opts.$data) {
    $dataSchema = __webpack_require__(/*! ./refs/data.json */ "./node_modules/ajv/lib/refs/data.json");
    self.addMetaSchema($dataSchema, $dataSchema.$id, true);
  }
  if (self._opts.meta === false) return;
  var metaSchema = __webpack_require__(/*! ./refs/json-schema-draft-07.json */ "./node_modules/ajv/lib/refs/json-schema-draft-07.json");
  if (self._opts.$data) metaSchema = $dataMetaSchema(metaSchema, META_SUPPORT_DATA);
  self.addMetaSchema(metaSchema, META_SCHEMA_ID, true);
  self._refs['http://json-schema.org/schema'] = META_SCHEMA_ID;
}


function addInitialSchemas(self) {
  var optsSchemas = self._opts.schemas;
  if (!optsSchemas) return;
  if (Array.isArray(optsSchemas)) self.addSchema(optsSchemas);
  else for (var key in optsSchemas) self.addSchema(optsSchemas[key], key);
}


function addInitialFormats(self) {
  for (var name in self._opts.formats) {
    var format = self._opts.formats[name];
    self.addFormat(name, format);
  }
}


function addInitialKeywords(self) {
  for (var name in self._opts.keywords) {
    var keyword = self._opts.keywords[name];
    self.addKeyword(name, keyword);
  }
}


function checkUnique(self, id) {
  if (self._schemas[id] || self._refs[id])
    throw new Error('schema with key or id "' + id + '" already exists');
}


function getMetaSchemaOptions(self) {
  var metaOpts = util.copy(self._opts);
  for (var i=0; i<META_IGNORE_OPTIONS.length; i++)
    delete metaOpts[META_IGNORE_OPTIONS[i]];
  return metaOpts;
}


function setLogger(self) {
  var logger = self._opts.logger;
  if (logger === false) {
    self.logger = {log: noop, warn: noop, error: noop};
  } else {
    if (logger === undefined) logger = console;
    if (!(typeof logger == 'object' && logger.log && logger.warn && logger.error))
      throw new Error('logger must implement log, warn and error methods');
    self.logger = logger;
  }
}


function noop() {}


/***/ }),

/***/ "./node_modules/ajv/lib/cache.js":
/*!***************************************!*\
  !*** ./node_modules/ajv/lib/cache.js ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";



var Cache = module.exports = function Cache() {
  this._cache = {};
};


Cache.prototype.put = function Cache_put(key, value) {
  this._cache[key] = value;
};


Cache.prototype.get = function Cache_get(key) {
  return this._cache[key];
};


Cache.prototype.del = function Cache_del(key) {
  delete this._cache[key];
};


Cache.prototype.clear = function Cache_clear() {
  this._cache = {};
};


/***/ }),

/***/ "./node_modules/ajv/lib/compile/async.js":
/*!***********************************************!*\
  !*** ./node_modules/ajv/lib/compile/async.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var MissingRefError = __webpack_require__(/*! ./error_classes */ "./node_modules/ajv/lib/compile/error_classes.js").MissingRef;

module.exports = compileAsync;


/**
 * Creates validating function for passed schema with asynchronous loading of missing schemas.
 * `loadSchema` option should be a function that accepts schema uri and returns promise that resolves with the schema.
 * @this  Ajv
 * @param {Object}   schema schema object
 * @param {Boolean}  meta optional true to compile meta-schema; this parameter can be skipped
 * @param {Function} callback an optional node-style callback, it is called with 2 parameters: error (or null) and validating function.
 * @return {Promise} promise that resolves with a validating function.
 */
function compileAsync(schema, meta, callback) {
  /* eslint no-shadow: 0 */
  /* global Promise */
  /* jshint validthis: true */
  var self = this;
  if (typeof this._opts.loadSchema != 'function')
    throw new Error('options.loadSchema should be a function');

  if (typeof meta == 'function') {
    callback = meta;
    meta = undefined;
  }

  var p = loadMetaSchemaOf(schema).then(function () {
    var schemaObj = self._addSchema(schema, undefined, meta);
    return schemaObj.validate || _compileAsync(schemaObj);
  });

  if (callback) {
    p.then(
      function(v) { callback(null, v); },
      callback
    );
  }

  return p;


  function loadMetaSchemaOf(sch) {
    var $schema = sch.$schema;
    return $schema && !self.getSchema($schema)
            ? compileAsync.call(self, { $ref: $schema }, true)
            : Promise.resolve();
  }


  function _compileAsync(schemaObj) {
    try { return self._compile(schemaObj); }
    catch(e) {
      if (e instanceof MissingRefError) return loadMissingSchema(e);
      throw e;
    }


    function loadMissingSchema(e) {
      var ref = e.missingSchema;
      if (added(ref)) throw new Error('Schema ' + ref + ' is loaded but ' + e.missingRef + ' cannot be resolved');

      var schemaPromise = self._loadingSchemas[ref];
      if (!schemaPromise) {
        schemaPromise = self._loadingSchemas[ref] = self._opts.loadSchema(ref);
        schemaPromise.then(removePromise, removePromise);
      }

      return schemaPromise.then(function (sch) {
        if (!added(ref)) {
          return loadMetaSchemaOf(sch).then(function () {
            if (!added(ref)) self.addSchema(sch, ref, undefined, meta);
          });
        }
      }).then(function() {
        return _compileAsync(schemaObj);
      });

      function removePromise() {
        delete self._loadingSchemas[ref];
      }

      function added(ref) {
        return self._refs[ref] || self._schemas[ref];
      }
    }
  }
}


/***/ }),

/***/ "./node_modules/ajv/lib/compile/error_classes.js":
/*!*******************************************************!*\
  !*** ./node_modules/ajv/lib/compile/error_classes.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var resolve = __webpack_require__(/*! ./resolve */ "./node_modules/ajv/lib/compile/resolve.js");

module.exports = {
  Validation: errorSubclass(ValidationError),
  MissingRef: errorSubclass(MissingRefError)
};


function ValidationError(errors) {
  this.message = 'validation failed';
  this.errors = errors;
  this.ajv = this.validation = true;
}


MissingRefError.message = function (baseId, ref) {
  return 'can\'t resolve reference ' + ref + ' from id ' + baseId;
};


function MissingRefError(baseId, ref, message) {
  this.message = message || MissingRefError.message(baseId, ref);
  this.missingRef = resolve.url(baseId, ref);
  this.missingSchema = resolve.normalizeId(resolve.fullPath(this.missingRef));
}


function errorSubclass(Subclass) {
  Subclass.prototype = Object.create(Error.prototype);
  Subclass.prototype.constructor = Subclass;
  return Subclass;
}


/***/ }),

/***/ "./node_modules/ajv/lib/compile/formats.js":
/*!*************************************************!*\
  !*** ./node_modules/ajv/lib/compile/formats.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var util = __webpack_require__(/*! ./util */ "./node_modules/ajv/lib/compile/util.js");

var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
var DAYS = [0,31,28,31,30,31,30,31,31,30,31,30,31];
var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
var HOSTNAME = /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i;
var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
// uri-template: https://tools.ietf.org/html/rfc6570
var URITEMPLATE = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
// For the source: https://gist.github.com/dperini/729294
// For test cases: https://mathiasbynens.be/demo/url-regex
// @todo Delete current URL in favour of the commented out URL rule when this issue is fixed https://github.com/eslint/eslint/issues/7983.
// var URL = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu;
var URL = /^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i;
var UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
var JSON_POINTER_URI_FRAGMENT = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
var RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;


module.exports = formats;

function formats(mode) {
  mode = mode == 'full' ? 'full' : 'fast';
  return util.copy(formats[mode]);
}


formats.fast = {
  // date: http://tools.ietf.org/html/rfc3339#section-5.6
  date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/,
  // date-time: http://tools.ietf.org/html/rfc3339#section-5.6
  time: /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,
  'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,
  // uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js
  uri: /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i,
  'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,
  'uri-template': URITEMPLATE,
  url: URL,
  // email (sources from jsen validator):
  // http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address#answer-8829363
  // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address (search for 'willful violation')
  email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,
  hostname: HOSTNAME,
  // optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html
  ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
  // optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
  ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
  regex: regex,
  // uuid: http://tools.ietf.org/html/rfc4122
  uuid: UUID,
  // JSON-pointer: https://tools.ietf.org/html/rfc6901
  // uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A
  'json-pointer': JSON_POINTER,
  'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT,
  // relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00
  'relative-json-pointer': RELATIVE_JSON_POINTER
};


formats.full = {
  date: date,
  time: time,
  'date-time': date_time,
  uri: uri,
  'uri-reference': URIREF,
  'uri-template': URITEMPLATE,
  url: URL,
  email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,
  hostname: HOSTNAME,
  ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
  ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
  regex: regex,
  uuid: UUID,
  'json-pointer': JSON_POINTER,
  'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT,
  'relative-json-pointer': RELATIVE_JSON_POINTER
};


function isLeapYear(year) {
  // https://tools.ietf.org/html/rfc3339#appendix-C
  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}


function date(str) {
  // full-date from http://tools.ietf.org/html/rfc3339#section-5.6
  var matches = str.match(DATE);
  if (!matches) return false;

  var year = +matches[1];
  var month = +matches[2];
  var day = +matches[3];

  return month >= 1 && month <= 12 && day >= 1 &&
          day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]);
}


function time(str, full) {
  var matches = str.match(TIME);
  if (!matches) return false;

  var hour = matches[1];
  var minute = matches[2];
  var second = matches[3];
  var timeZone = matches[5];
  return ((hour <= 23 && minute <= 59 && second <= 59) ||
          (hour == 23 && minute == 59 && second == 60)) &&
         (!full || timeZone);
}


var DATE_TIME_SEPARATOR = /t|\s/i;
function date_time(str) {
  // http://tools.ietf.org/html/rfc3339#section-5.6
  var dateTime = str.split(DATE_TIME_SEPARATOR);
  return dateTime.length == 2 && date(dateTime[0]) && time(dateTime[1], true);
}


var NOT_URI_FRAGMENT = /\/|:/;
function uri(str) {
  // http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "."
  return NOT_URI_FRAGMENT.test(str) && URI.test(str);
}


var Z_ANCHOR = /[^\\]\\Z/;
function regex(str) {
  if (Z_ANCHOR.test(str)) return false;
  try {
    new RegExp(str);
    return true;
  } catch(e) {
    return false;
  }
}


/***/ }),

/***/ "./node_modules/ajv/lib/compile/index.js":
/*!***********************************************!*\
  !*** ./node_modules/ajv/lib/compile/index.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var resolve = __webpack_require__(/*! ./resolve */ "./node_modules/ajv/lib/compile/resolve.js")
  , util = __webpack_require__(/*! ./util */ "./node_modules/ajv/lib/compile/util.js")
  , errorClasses = __webpack_require__(/*! ./error_classes */ "./node_modules/ajv/lib/compile/error_classes.js")
  , stableStringify = __webpack_require__(/*! fast-json-stable-stringify */ "./node_modules/fast-json-stable-stringify/index.js");

var validateGenerator = __webpack_require__(/*! ../dotjs/validate */ "./node_modules/ajv/lib/dotjs/validate.js");

/**
 * Functions below are used inside compiled validations function
 */

var ucs2length = util.ucs2length;
var equal = __webpack_require__(/*! fast-deep-equal */ "./node_modules/fast-deep-equal/index.js");

// this error is thrown by async schemas to return validation errors via exception
var ValidationError = errorClasses.Validation;

module.exports = compile;


/**
 * Compiles schema to validation function
 * @this   Ajv
 * @param  {Object} schema schema object
 * @param  {Object} root object with information about the root schema for this schema
 * @param  {Object} localRefs the hash of local references inside the schema (created by resolve.id), used for inline resolution
 * @param  {String} baseId base ID for IDs in the schema
 * @return {Function} validation function
 */
function compile(schema, root, localRefs, baseId) {
  /* jshint validthis: true, evil: true */
  /* eslint no-shadow: 0 */
  var self = this
    , opts = this._opts
    , refVal = [ undefined ]
    , refs = {}
    , patterns = []
    , patternsHash = {}
    , defaults = []
    , defaultsHash = {}
    , customRules = [];

  root = root || { schema: schema, refVal: refVal, refs: refs };

  var c = checkCompiling.call(this, schema, root, baseId);
  var compilation = this._compilations[c.index];
  if (c.compiling) return (compilation.callValidate = callValidate);

  var formats = this._formats;
  var RULES = this.RULES;

  try {
    var v = localCompile(schema, root, localRefs, baseId);
    compilation.validate = v;
    var cv = compilation.callValidate;
    if (cv) {
      cv.schema = v.schema;
      cv.errors = null;
      cv.refs = v.refs;
      cv.refVal = v.refVal;
      cv.root = v.root;
      cv.$async = v.$async;
      if (opts.sourceCode) cv.source = v.source;
    }
    return v;
  } finally {
    endCompiling.call(this, schema, root, baseId);
  }

  /* @this   {*} - custom context, see passContext option */
  function callValidate() {
    /* jshint validthis: true */
    var validate = compilation.validate;
    var result = validate.apply(this, arguments);
    callValidate.errors = validate.errors;
    return result;
  }

  function localCompile(_schema, _root, localRefs, baseId) {
    var isRoot = !_root || (_root && _root.schema == _schema);
    if (_root.schema != root.schema)
      return compile.call(self, _schema, _root, localRefs, baseId);

    var $async = _schema.$async === true;

    var sourceCode = validateGenerator({
      isTop: true,
      schema: _schema,
      isRoot: isRoot,
      baseId: baseId,
      root: _root,
      schemaPath: '',
      errSchemaPath: '#',
      errorPath: '""',
      MissingRefError: errorClasses.MissingRef,
      RULES: RULES,
      validate: validateGenerator,
      util: util,
      resolve: resolve,
      resolveRef: resolveRef,
      usePattern: usePattern,
      useDefault: useDefault,
      useCustomRule: useCustomRule,
      opts: opts,
      formats: formats,
      logger: self.logger,
      self: self
    });

    sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode)
                   + vars(defaults, defaultCode) + vars(customRules, customRuleCode)
                   + sourceCode;

    if (opts.processCode) sourceCode = opts.processCode(sourceCode);
    // console.log('\n\n\n *** \n', JSON.stringify(sourceCode));
    var validate;
    try {
      var makeValidate = new Function(
        'self',
        'RULES',
        'formats',
        'root',
        'refVal',
        'defaults',
        'customRules',
        'equal',
        'ucs2length',
        'ValidationError',
        sourceCode
      );

      validate = makeValidate(
        self,
        RULES,
        formats,
        root,
        refVal,
        defaults,
        customRules,
        equal,
        ucs2length,
        ValidationError
      );

      refVal[0] = validate;
    } catch(e) {
      self.logger.error('Error compiling schema, function code:', sourceCode);
      throw e;
    }

    validate.schema = _schema;
    validate.errors = null;
    validate.refs = refs;
    validate.refVal = refVal;
    validate.root = isRoot ? validate : _root;
    if ($async) validate.$async = true;
    if (opts.sourceCode === true) {
      validate.source = {
        code: sourceCode,
        patterns: patterns,
        defaults: defaults
      };
    }

    return validate;
  }

  function resolveRef(baseId, ref, isRoot) {
    ref = resolve.url(baseId, ref);
    var refIndex = refs[ref];
    var _refVal, refCode;
    if (refIndex !== undefined) {
      _refVal = refVal[refIndex];
      refCode = 'refVal[' + refIndex + ']';
      return resolvedRef(_refVal, refCode);
    }
    if (!isRoot && root.refs) {
      var rootRefId = root.refs[ref];
      if (rootRefId !== undefined) {
        _refVal = root.refVal[rootRefId];
        refCode = addLocalRef(ref, _refVal);
        return resolvedRef(_refVal, refCode);
      }
    }

    refCode = addLocalRef(ref);
    var v = resolve.call(self, localCompile, root, ref);
    if (v === undefined) {
      var localSchema = localRefs && localRefs[ref];
      if (localSchema) {
        v = resolve.inlineRef(localSchema, opts.inlineRefs)
            ? localSchema
            : compile.call(self, localSchema, root, localRefs, baseId);
      }
    }

    if (v === undefined) {
      removeLocalRef(ref);
    } else {
      replaceLocalRef(ref, v);
      return resolvedRef(v, refCode);
    }
  }

  function addLocalRef(ref, v) {
    var refId = refVal.length;
    refVal[refId] = v;
    refs[ref] = refId;
    return 'refVal' + refId;
  }

  function removeLocalRef(ref) {
    delete refs[ref];
  }

  function replaceLocalRef(ref, v) {
    var refId = refs[ref];
    refVal[refId] = v;
  }

  function resolvedRef(refVal, code) {
    return typeof refVal == 'object' || typeof refVal == 'boolean'
            ? { code: code, schema: refVal, inline: true }
            : { code: code, $async: refVal && !!refVal.$async };
  }

  function usePattern(regexStr) {
    var index = patternsHash[regexStr];
    if (index === undefined) {
      index = patternsHash[regexStr] = patterns.length;
      patterns[index] = regexStr;
    }
    return 'pattern' + index;
  }

  function useDefault(value) {
    switch (typeof value) {
      case 'boolean':
      case 'number':
        return '' + value;
      case 'string':
        return util.toQuotedString(value);
      case 'object':
        if (value === null) return 'null';
        var valueStr = stableStringify(value);
        var index = defaultsHash[valueStr];
        if (index === undefined) {
          index = defaultsHash[valueStr] = defaults.length;
          defaults[index] = value;
        }
        return 'default' + index;
    }
  }

  function useCustomRule(rule, schema, parentSchema, it) {
    if (self._opts.validateSchema !== false) {
      var deps = rule.definition.dependencies;
      if (deps && !deps.every(function(keyword) {
        return Object.prototype.hasOwnProperty.call(parentSchema, keyword);
      }))
        throw new Error('parent schema must have all required keywords: ' + deps.join(','));

      var validateSchema = rule.definition.validateSchema;
      if (validateSchema) {
        var valid = validateSchema(schema);
        if (!valid) {
          var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
          if (self._opts.validateSchema == 'log') self.logger.error(message);
          else throw new Error(message);
        }
      }
    }

    var compile = rule.definition.compile
      , inline = rule.definition.inline
      , macro = rule.definition.macro;

    var validate;
    if (compile) {
      validate = compile.call(self, schema, parentSchema, it);
    } else if (macro) {
      validate = macro.call(self, schema, parentSchema, it);
      if (opts.validateSchema !== false) self.validateSchema(validate, true);
    } else if (inline) {
      validate = inline.call(self, it, rule.keyword, schema, parentSchema);
    } else {
      validate = rule.definition.validate;
      if (!validate) return;
    }

    if (validate === undefined)
      throw new Error('custom keyword "' + rule.keyword + '"failed to compile');

    var index = customRules.length;
    customRules[index] = validate;

    return {
      code: 'customRule' + index,
      validate: validate
    };
  }
}


/**
 * Checks if the schema is currently compiled
 * @this   Ajv
 * @param  {Object} schema schema to compile
 * @param  {Object} root root object
 * @param  {String} baseId base schema ID
 * @return {Object} object with properties "index" (compilation index) and "compiling" (boolean)
 */
function checkCompiling(schema, root, baseId) {
  /* jshint validthis: true */
  var index = compIndex.call(this, schema, root, baseId);
  if (index >= 0) return { index: index, compiling: true };
  index = this._compilations.length;
  this._compilations[index] = {
    schema: schema,
    root: root,
    baseId: baseId
  };
  return { index: index, compiling: false };
}


/**
 * Removes the schema from the currently compiled list
 * @this   Ajv
 * @param  {Object} schema schema to compile
 * @param  {Object} root root object
 * @param  {String} baseId base schema ID
 */
function endCompiling(schema, root, baseId) {
  /* jshint validthis: true */
  var i = compIndex.call(this, schema, root, baseId);
  if (i >= 0) this._compilations.splice(i, 1);
}


/**
 * Index of schema compilation in the currently compiled list
 * @this   Ajv
 * @param  {Object} schema schema to compile
 * @param  {Object} root root object
 * @param  {String} baseId base schema ID
 * @return {Integer} compilation index
 */
function compIndex(schema, root, baseId) {
  /* jshint validthis: true */
  for (var i=0; i<this._compilations.length; i++) {
    var c = this._compilations[i];
    if (c.schema == schema && c.root == root && c.baseId == baseId) return i;
  }
  return -1;
}


function patternCode(i, patterns) {
  return 'var pattern' + i + ' = new RegExp(' + util.toQuotedString(patterns[i]) + ');';
}


function defaultCode(i) {
  return 'var default' + i + ' = defaults[' + i + '];';
}


function refValCode(i, refVal) {
  return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];';
}


function customRuleCode(i) {
  return 'var customRule' + i + ' = customRules[' + i + '];';
}


function vars(arr, statement) {
  if (!arr.length) return '';
  var code = '';
  for (var i=0; i<arr.length; i++)
    code += statement(i, arr);
  return code;
}


/***/ }),

/***/ "./node_modules/ajv/lib/compile/resolve.js":
/*!*************************************************!*\
  !*** ./node_modules/ajv/lib/compile/resolve.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var URI = __webpack_require__(/*! uri-js */ "./node_modules/uri-js/dist/es5/uri.all.js")
  , equal = __webpack_require__(/*! fast-deep-equal */ "./node_modules/fast-deep-equal/index.js")
  , util = __webpack_require__(/*! ./util */ "./node_modules/ajv/lib/compile/util.js")
  , SchemaObject = __webpack_require__(/*! ./schema_obj */ "./node_modules/ajv/lib/compile/schema_obj.js")
  , traverse = __webpack_require__(/*! json-schema-traverse */ "./node_modules/json-schema-traverse/index.js");

module.exports = resolve;

resolve.normalizeId = normalizeId;
resolve.fullPath = getFullPath;
resolve.url = resolveUrl;
resolve.ids = resolveIds;
resolve.inlineRef = inlineRef;
resolve.schema = resolveSchema;

/**
 * [resolve and compile the references ($ref)]
 * @this   Ajv
 * @param  {Function} compile reference to schema compilation funciton (localCompile)
 * @param  {Object} root object with information about the root schema for the current schema
 * @param  {String} ref reference to resolve
 * @return {Object|Function} schema object (if the schema can be inlined) or validation function
 */
function resolve(compile, root, ref) {
  /* jshint validthis: true */
  var refVal = this._refs[ref];
  if (typeof refVal == 'string') {
    if (this._refs[refVal]) refVal = this._refs[refVal];
    else return resolve.call(this, compile, root, refVal);
  }

  refVal = refVal || this._schemas[ref];
  if (refVal instanceof SchemaObject) {
    return inlineRef(refVal.schema, this._opts.inlineRefs)
            ? refVal.schema
            : refVal.validate || this._compile(refVal);
  }

  var res = resolveSchema.call(this, root, ref);
  var schema, v, baseId;
  if (res) {
    schema = res.schema;
    root = res.root;
    baseId = res.baseId;
  }

  if (schema instanceof SchemaObject) {
    v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId);
  } else if (schema !== undefined) {
    v = inlineRef(schema, this._opts.inlineRefs)
        ? schema
        : compile.call(this, schema, root, undefined, baseId);
  }

  return v;
}


/**
 * Resolve schema, its root and baseId
 * @this Ajv
 * @param  {Object} root root object with properties schema, refVal, refs
 * @param  {String} ref  reference to resolve
 * @return {Object} object with properties schema, root, baseId
 */
function resolveSchema(root, ref) {
  /* jshint validthis: true */
  var p = URI.parse(ref)
    , refPath = _getFullPath(p)
    , baseId = getFullPath(this._getId(root.schema));
  if (Object.keys(root.schema).length === 0 || refPath !== baseId) {
    var id = normalizeId(refPath);
    var refVal = this._refs[id];
    if (typeof refVal == 'string') {
      return resolveRecursive.call(this, root, refVal, p);
    } else if (refVal instanceof SchemaObject) {
      if (!refVal.validate) this._compile(refVal);
      root = refVal;
    } else {
      refVal = this._schemas[id];
      if (refVal instanceof SchemaObject) {
        if (!refVal.validate) this._compile(refVal);
        if (id == normalizeId(ref))
          return { schema: refVal, root: root, baseId: baseId };
        root = refVal;
      } else {
        return;
      }
    }
    if (!root.schema) return;
    baseId = getFullPath(this._getId(root.schema));
  }
  return getJsonPointer.call(this, p, baseId, root.schema, root);
}


/* @this Ajv */
function resolveRecursive(root, ref, parsedRef) {
  /* jshint validthis: true */
  var res = resolveSchema.call(this, root, ref);
  if (res) {
    var schema = res.schema;
    var baseId = res.baseId;
    root = res.root;
    var id = this._getId(schema);
    if (id) baseId = resolveUrl(baseId, id);
    return getJsonPointer.call(this, parsedRef, baseId, schema, root);
  }
}


var PREVENT_SCOPE_CHANGE = util.toHash(['properties', 'patternProperties', 'enum', 'dependencies', 'definitions']);
/* @this Ajv */
function getJsonPointer(parsedRef, baseId, schema, root) {
  /* jshint validthis: true */
  parsedRef.fragment = parsedRef.fragment || '';
  if (parsedRef.fragment.slice(0,1) != '/') return;
  var parts = parsedRef.fragment.split('/');

  for (var i = 1; i < parts.length; i++) {
    var part = parts[i];
    if (part) {
      part = util.unescapeFragment(part);
      schema = schema[part];
      if (schema === undefined) break;
      var id;
      if (!PREVENT_SCOPE_CHANGE[part]) {
        id = this._getId(schema);
        if (id) baseId = resolveUrl(baseId, id);
        if (schema.$ref) {
          var $ref = resolveUrl(baseId, schema.$ref);
          var res = resolveSchema.call(this, root, $ref);
          if (res) {
            schema = res.schema;
            root = res.root;
            baseId = res.baseId;
          }
        }
      }
    }
  }
  if (schema !== undefined && schema !== root.schema)
    return { schema: schema, root: root, baseId: baseId };
}


var SIMPLE_INLINED = util.toHash([
  'type', 'format', 'pattern',
  'maxLength', 'minLength',
  'maxProperties', 'minProperties',
  'maxItems', 'minItems',
  'maximum', 'minimum',
  'uniqueItems', 'multipleOf',
  'required', 'enum'
]);
function inlineRef(schema, limit) {
  if (limit === false) return false;
  if (limit === undefined || limit === true) return checkNoRef(schema);
  else if (limit) return countKeys(schema) <= limit;
}


function checkNoRef(schema) {
  var item;
  if (Array.isArray(schema)) {
    for (var i=0; i<schema.length; i++) {
      item = schema[i];
      if (typeof item == 'object' && !checkNoRef(item)) return false;
    }
  } else {
    for (var key in schema) {
      if (key == '$ref') return false;
      item = schema[key];
      if (typeof item == 'object' && !checkNoRef(item)) return false;
    }
  }
  return true;
}


function countKeys(schema) {
  var count = 0, item;
  if (Array.isArray(schema)) {
    for (var i=0; i<schema.length; i++) {
      item = schema[i];
      if (typeof item == 'object') count += countKeys(item);
      if (count == Infinity) return Infinity;
    }
  } else {
    for (var key in schema) {
      if (key == '$ref') return Infinity;
      if (SIMPLE_INLINED[key]) {
        count++;
      } else {
        item = schema[key];
        if (typeof item == 'object') count += countKeys(item) + 1;
        if (count == Infinity) return Infinity;
      }
    }
  }
  return count;
}


function getFullPath(id, normalize) {
  if (normalize !== false) id = normalizeId(id);
  var p = URI.parse(id);
  return _getFullPath(p);
}


function _getFullPath(p) {
  return URI.serialize(p).split('#')[0] + '#';
}


var TRAILING_SLASH_HASH = /#\/?$/;
function normalizeId(id) {
  return id ? id.replace(TRAILING_SLASH_HASH, '') : '';
}


function resolveUrl(baseId, id) {
  id = normalizeId(id);
  return URI.resolve(baseId, id);
}


/* @this Ajv */
function resolveIds(schema) {
  var schemaId = normalizeId(this._getId(schema));
  var baseIds = {'': schemaId};
  var fullPaths = {'': getFullPath(schemaId, false)};
  var localRefs = {};
  var self = this;

  traverse(schema, {allKeys: true}, function(sch, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
    if (jsonPtr === '') return;
    var id = self._getId(sch);
    var baseId = baseIds[parentJsonPtr];
    var fullPath = fullPaths[parentJsonPtr] + '/' + parentKeyword;
    if (keyIndex !== undefined)
      fullPath += '/' + (typeof keyIndex == 'number' ? keyIndex : util.escapeFragment(keyIndex));

    if (typeof id == 'string') {
      id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id);

      var refVal = self._refs[id];
      if (typeof refVal == 'string') refVal = self._refs[refVal];
      if (refVal && refVal.schema) {
        if (!equal(sch, refVal.schema))
          throw new Error('id "' + id + '" resolves to more than one schema');
      } else if (id != normalizeId(fullPath)) {
        if (id[0] == '#') {
          if (localRefs[id] && !equal(sch, localRefs[id]))
            throw new Error('id "' + id + '" resolves to more than one schema');
          localRefs[id] = sch;
        } else {
          self._refs[id] = fullPath;
        }
      }
    }
    baseIds[jsonPtr] = baseId;
    fullPaths[jsonPtr] = fullPath;
  });

  return localRefs;
}


/***/ }),

/***/ "./node_modules/ajv/lib/compile/rules.js":
/*!***********************************************!*\
  !*** ./node_modules/ajv/lib/compile/rules.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var ruleModules = __webpack_require__(/*! ../dotjs */ "./node_modules/ajv/lib/dotjs/index.js")
  , toHash = __webpack_require__(/*! ./util */ "./node_modules/ajv/lib/compile/util.js").toHash;

module.exports = function rules() {
  var RULES = [
    { type: 'number',
      rules: [ { 'maximum': ['exclusiveMaximum'] },
               { 'minimum': ['exclusiveMinimum'] }, 'multipleOf', 'format'] },
    { type: 'string',
      rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] },
    { type: 'array',
      rules: [ 'maxItems', 'minItems', 'items', 'contains', 'uniqueItems' ] },
    { type: 'object',
      rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames',
               { 'properties': ['additionalProperties', 'patternProperties'] } ] },
    { rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf', 'if' ] }
  ];

  var ALL = [ 'type', '$comment' ];
  var KEYWORDS = [
    '$schema', '$id', 'id', '$data', '$async', 'title',
    'description', 'default', 'definitions',
    'examples', 'readOnly', 'writeOnly',
    'contentMediaType', 'contentEncoding',
    'additionalItems', 'then', 'else'
  ];
  var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ];
  RULES.all = toHash(ALL);
  RULES.types = toHash(TYPES);

  RULES.forEach(function (group) {
    group.rules = group.rules.map(function (keyword) {
      var implKeywords;
      if (typeof keyword == 'object') {
        var key = Object.keys(keyword)[0];
        implKeywords = keyword[key];
        keyword = key;
        implKeywords.forEach(function (k) {
          ALL.push(k);
          RULES.all[k] = true;
        });
      }
      ALL.push(keyword);
      var rule = RULES.all[keyword] = {
        keyword: keyword,
        code: ruleModules[keyword],
        implements: implKeywords
      };
      return rule;
    });

    RULES.all.$comment = {
      keyword: '$comment',
      code: ruleModules.$comment
    };

    if (group.type) RULES.types[group.type] = group;
  });

  RULES.keywords = toHash(ALL.concat(KEYWORDS));
  RULES.custom = {};

  return RULES;
};


/***/ }),

/***/ "./node_modules/ajv/lib/compile/schema_obj.js":
/*!****************************************************!*\
  !*** ./node_modules/ajv/lib/compile/schema_obj.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var util = __webpack_require__(/*! ./util */ "./node_modules/ajv/lib/compile/util.js");

module.exports = SchemaObject;

function SchemaObject(obj) {
  util.copy(obj, this);
}


/***/ }),

/***/ "./node_modules/ajv/lib/compile/ucs2length.js":
/*!****************************************************!*\
  !*** ./node_modules/ajv/lib/compile/ucs2length.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// https://mathiasbynens.be/notes/javascript-encoding
// https://github.com/bestiejs/punycode.js - punycode.ucs2.decode
module.exports = function ucs2length(str) {
  var length = 0
    , len = str.length
    , pos = 0
    , value;
  while (pos < len) {
    length++;
    value = str.charCodeAt(pos++);
    if (value >= 0xD800 && value <= 0xDBFF && pos < len) {
      // high surrogate, and there is a next character
      value = str.charCodeAt(pos);
      if ((value & 0xFC00) == 0xDC00) pos++; // low surrogate
    }
  }
  return length;
};


/***/ }),

/***/ "./node_modules/ajv/lib/compile/util.js":
/*!**********************************************!*\
  !*** ./node_modules/ajv/lib/compile/util.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";



module.exports = {
  copy: copy,
  checkDataType: checkDataType,
  checkDataTypes: checkDataTypes,
  coerceToTypes: coerceToTypes,
  toHash: toHash,
  getProperty: getProperty,
  escapeQuotes: escapeQuotes,
  equal: __webpack_require__(/*! fast-deep-equal */ "./node_modules/fast-deep-equal/index.js"),
  ucs2length: __webpack_require__(/*! ./ucs2length */ "./node_modules/ajv/lib/compile/ucs2length.js"),
  varOccurences: varOccurences,
  varReplace: varReplace,
  cleanUpCode: cleanUpCode,
  finalCleanUpCode: finalCleanUpCode,
  schemaHasRules: schemaHasRules,
  schemaHasRulesExcept: schemaHasRulesExcept,
  schemaUnknownRules: schemaUnknownRules,
  toQuotedString: toQuotedString,
  getPathExpr: getPathExpr,
  getPath: getPath,
  getData: getData,
  unescapeFragment: unescapeFragment,
  unescapeJsonPointer: unescapeJsonPointer,
  escapeFragment: escapeFragment,
  escapeJsonPointer: escapeJsonPointer
};


function copy(o, to) {
  to = to || {};
  for (var key in o) to[key] = o[key];
  return to;
}


function checkDataType(dataType, data, negate) {
  var EQUAL = negate ? ' !== ' : ' === '
    , AND = negate ? ' || ' : ' && '
    , OK = negate ? '!' : ''
    , NOT = negate ? '' : '!';
  switch (dataType) {
    case 'null': return data + EQUAL + 'null';
    case 'array': return OK + 'Array.isArray(' + data + ')';
    case 'object': return '(' + OK + data + AND +
                          'typeof ' + data + EQUAL + '"object"' + AND +
                          NOT + 'Array.isArray(' + data + '))';
    case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
                           NOT + '(' + data + ' % 1)' +
                           AND + data + EQUAL + data + ')';
    default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
  }
}


function checkDataTypes(dataTypes, data) {
  switch (dataTypes.length) {
    case 1: return checkDataType(dataTypes[0], data, true);
    default:
      var code = '';
      var types = toHash(dataTypes);
      if (types.array && types.object) {
        code = types.null ? '(': '(!' + data + ' || ';
        code += 'typeof ' + data + ' !== "object")';
        delete types.null;
        delete types.array;
        delete types.object;
      }
      if (types.number) delete types.integer;
      for (var t in types)
        code += (code ? ' && ' : '' ) + checkDataType(t, data, true);

      return code;
  }
}


var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]);
function coerceToTypes(optionCoerceTypes, dataTypes) {
  if (Array.isArray(dataTypes)) {
    var types = [];
    for (var i=0; i<dataTypes.length; i++) {
      var t = dataTypes[i];
      if (COERCE_TO_TYPES[t]) types[types.length] = t;
      else if (optionCoerceTypes === 'array' && t === 'array') types[types.length] = t;
    }
    if (types.length) return types;
  } else if (COERCE_TO_TYPES[dataTypes]) {
    return [dataTypes];
  } else if (optionCoerceTypes === 'array' && dataTypes === 'array') {
    return ['array'];
  }
}


function toHash(arr) {
  var hash = {};
  for (var i=0; i<arr.length; i++) hash[arr[i]] = true;
  return hash;
}


var IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
var SINGLE_QUOTE = /'|\\/g;
function getProperty(key) {
  return typeof key == 'number'
          ? '[' + key + ']'
          : IDENTIFIER.test(key)
            ? '.' + key
            : "['" + escapeQuotes(key) + "']";
}


function escapeQuotes(str) {
  return str.replace(SINGLE_QUOTE, '\\$&')
            .replace(/\n/g, '\\n')
            .replace(/\r/g, '\\r')
            .replace(/\f/g, '\\f')
            .replace(/\t/g, '\\t');
}


function varOccurences(str, dataVar) {
  dataVar += '[^0-9]';
  var matches = str.match(new RegExp(dataVar, 'g'));
  return matches ? matches.length : 0;
}


function varReplace(str, dataVar, expr) {
  dataVar += '([^0-9])';
  expr = expr.replace(/\$/g, '$$$$');
  return str.replace(new RegExp(dataVar, 'g'), expr + '$1');
}


var EMPTY_ELSE = /else\s*{\s*}/g
  , EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
  , EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
function cleanUpCode(out) {
  return out.replace(EMPTY_ELSE, '')
            .replace(EMPTY_IF_NO_ELSE, '')
            .replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
}


var ERRORS_REGEXP = /[^v.]errors/g
  , REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
  , REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
  , RETURN_VALID = 'return errors === 0;'
  , RETURN_TRUE = 'validate.errors = null; return true;'
  , RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/
  , RETURN_DATA_ASYNC = 'return data;'
  , ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g
  , REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/;

function finalCleanUpCode(out, async) {
  var matches = out.match(ERRORS_REGEXP);
  if (matches && matches.length == 2) {
    out = async
          ? out.replace(REMOVE_ERRORS_ASYNC, '')
               .replace(RETURN_ASYNC, RETURN_DATA_ASYNC)
          : out.replace(REMOVE_ERRORS, '')
               .replace(RETURN_VALID, RETURN_TRUE);
  }

  matches = out.match(ROOTDATA_REGEXP);
  if (!matches || matches.length !== 3) return out;
  return out.replace(REMOVE_ROOTDATA, '');
}


function schemaHasRules(schema, rules) {
  if (typeof schema == 'boolean') return !schema;
  for (var key in schema) if (rules[key]) return true;
}


function schemaHasRulesExcept(schema, rules, exceptKeyword) {
  if (typeof schema == 'boolean') return !schema && exceptKeyword != 'not';
  for (var key in schema) if (key != exceptKeyword && rules[key]) return true;
}


function schemaUnknownRules(schema, rules) {
  if (typeof schema == 'boolean') return;
  for (var key in schema) if (!rules[key]) return key;
}


function toQuotedString(str) {
  return '\'' + escapeQuotes(str) + '\'';
}


function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
  var path = jsonPointers // false by default
              ? '\'/\' + ' + expr + (isNumber ? '' : '.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')')
              : (isNumber ? '\'[\' + ' + expr + ' + \']\'' : '\'[\\\'\' + ' + expr + ' + \'\\\']\'');
  return joinPaths(currentPath, path);
}


function getPath(currentPath, prop, jsonPointers) {
  var path = jsonPointers // false by default
              ? toQuotedString('/' + escapeJsonPointer(prop))
              : toQuotedString(getProperty(prop));
  return joinPaths(currentPath, path);
}


var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/;
var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
function getData($data, lvl, paths) {
  var up, jsonPointer, data, matches;
  if ($data === '') return 'rootData';
  if ($data[0] == '/') {
    if (!JSON_POINTER.test($data)) throw new Error('Invalid JSON-pointer: ' + $data);
    jsonPointer = $data;
    data = 'rootData';
  } else {
    matches = $data.match(RELATIVE_JSON_POINTER);
    if (!matches) throw new Error('Invalid JSON-pointer: ' + $data);
    up = +matches[1];
    jsonPointer = matches[2];
    if (jsonPointer == '#') {
      if (up >= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl);
      return paths[lvl - up];
    }

    if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl);
    data = 'data' + ((lvl - up) || '');
    if (!jsonPointer) return data;
  }

  var expr = data;
  var segments = jsonPointer.split('/');
  for (var i=0; i<segments.length; i++) {
    var segment = segments[i];
    if (segment) {
      data += getProperty(unescapeJsonPointer(segment));
      expr += ' && ' + data;
    }
  }
  return expr;
}


function joinPaths (a, b) {
  if (a == '""') return b;
  return (a + ' + ' + b).replace(/' \+ '/g, '');
}


function unescapeFragment(str) {
  return unescapeJsonPointer(decodeURIComponent(str));
}


function escapeFragment(str) {
  return encodeURIComponent(escapeJsonPointer(str));
}


function escapeJsonPointer(str) {
  return str.replace(/~/g, '~0').replace(/\//g, '~1');
}


function unescapeJsonPointer(str) {
  return str.replace(/~1/g, '/').replace(/~0/g, '~');
}


/***/ }),

/***/ "./node_modules/ajv/lib/data.js":
/*!**************************************!*\
  !*** ./node_modules/ajv/lib/data.js ***!
  \**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var KEYWORDS = [
  'multipleOf',
  'maximum',
  'exclusiveMaximum',
  'minimum',
  'exclusiveMinimum',
  'maxLength',
  'minLength',
  'pattern',
  'additionalItems',
  'maxItems',
  'minItems',
  'uniqueItems',
  'maxProperties',
  'minProperties',
  'required',
  'additionalProperties',
  'enum',
  'format',
  'const'
];

module.exports = function (metaSchema, keywordsJsonPointers) {
  for (var i=0; i<keywordsJsonPointers.length; i++) {
    metaSchema = JSON.parse(JSON.stringify(metaSchema));
    var segments = keywordsJsonPointers[i].split('/');
    var keywords = metaSchema;
    var j;
    for (j=1; j<segments.length; j++)
      keywords = keywords[segments[j]];

    for (j=0; j<KEYWORDS.length; j++) {
      var key = KEYWORDS[j];
      var schema = keywords[key];
      if (schema) {
        keywords[key] = {
          anyOf: [
            schema,
            { $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#' }
          ]
        };
      }
    }
  }

  return metaSchema;
};


/***/ }),

/***/ "./node_modules/ajv/lib/definition_schema.js":
/*!***************************************************!*\
  !*** ./node_modules/ajv/lib/definition_schema.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var metaSchema = __webpack_require__(/*! ./refs/json-schema-draft-07.json */ "./node_modules/ajv/lib/refs/json-schema-draft-07.json");

module.exports = {
  $id: 'https://github.com/epoberezkin/ajv/blob/master/lib/definition_schema.js',
  definitions: {
    simpleTypes: metaSchema.definitions.simpleTypes
  },
  type: 'object',
  dependencies: {
    schema: ['validate'],
    $data: ['validate'],
    statements: ['inline'],
    valid: {not: {required: ['macro']}}
  },
  properties: {
    type: metaSchema.properties.type,
    schema: {type: 'boolean'},
    statements: {type: 'boolean'},
    dependencies: {
      type: 'array',
      items: {type: 'string'}
    },
    metaSchema: {type: 'object'},
    modifying: {type: 'boolean'},
    valid: {type: 'boolean'},
    $data: {type: 'boolean'},
    async: {type: 'boolean'},
    errors: {
      anyOf: [
        {type: 'boolean'},
        {const: 'full'}
      ]
    }
  }
};


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/_limit.js":
/*!**********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/_limit.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate__limit(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $errorKeyword;
  var $data = 'data' + ($dataLvl || '');
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $isMax = $keyword == 'maximum',
    $exclusiveKeyword = $isMax ? 'exclusiveMaximum' : 'exclusiveMinimum',
    $schemaExcl = it.schema[$exclusiveKeyword],
    $isDataExcl = it.opts.$data && $schemaExcl && $schemaExcl.$data,
    $op = $isMax ? '<' : '>',
    $notOp = $isMax ? '>' : '<',
    $errorKeyword = undefined;
  if ($isDataExcl) {
    var $schemaValueExcl = it.util.getData($schemaExcl.$data, $dataLvl, it.dataPathArr),
      $exclusive = 'exclusive' + $lvl,
      $exclType = 'exclType' + $lvl,
      $exclIsNumber = 'exclIsNumber' + $lvl,
      $opExpr = 'op' + $lvl,
      $opStr = '\' + ' + $opExpr + ' + \'';
    out += ' var schemaExcl' + ($lvl) + ' = ' + ($schemaValueExcl) + '; ';
    $schemaValueExcl = 'schemaExcl' + $lvl;
    out += ' var ' + ($exclusive) + '; var ' + ($exclType) + ' = typeof ' + ($schemaValueExcl) + '; if (' + ($exclType) + ' != \'boolean\' && ' + ($exclType) + ' != \'undefined\' && ' + ($exclType) + ' != \'number\') { ';
    var $errorKeyword = $exclusiveKeyword;
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = ''; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ($errorKeyword || '_exclusiveLimit') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
      if (it.opts.messages !== false) {
        out += ' , message: \'' + ($exclusiveKeyword) + ' should be boolean\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    var __err = out;
    out = $$outStack.pop();
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError([' + (__err) + ']); ';
      } else {
        out += ' validate.errors = [' + (__err) + ']; return false; ';
      }
    } else {
      out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    }
    out += ' } else if ( ';
    if ($isData) {
      out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || ';
    }
    out += ' ' + ($exclType) + ' == \'number\' ? ( (' + ($exclusive) + ' = ' + ($schemaValue) + ' === undefined || ' + ($schemaValueExcl) + ' ' + ($op) + '= ' + ($schemaValue) + ') ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaValueExcl) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) : ( (' + ($exclusive) + ' = ' + ($schemaValueExcl) + ' === true) ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaValue) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) || ' + ($data) + ' !== ' + ($data) + ') { var op' + ($lvl) + ' = ' + ($exclusive) + ' ? \'' + ($op) + '\' : \'' + ($op) + '=\'; ';
    if ($schema === undefined) {
      $errorKeyword = $exclusiveKeyword;
      $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword;
      $schemaValue = $schemaValueExcl;
      $isData = $isDataExcl;
    }
  } else {
    var $exclIsNumber = typeof $schemaExcl == 'number',
      $opStr = $op;
    if ($exclIsNumber && $isData) {
      var $opExpr = '\'' + $opStr + '\'';
      out += ' if ( ';
      if ($isData) {
        out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || ';
      }
      out += ' ( ' + ($schemaValue) + ' === undefined || ' + ($schemaExcl) + ' ' + ($op) + '= ' + ($schemaValue) + ' ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaExcl) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) || ' + ($data) + ' !== ' + ($data) + ') { ';
    } else {
      if ($exclIsNumber && $schema === undefined) {
        $exclusive = true;
        $errorKeyword = $exclusiveKeyword;
        $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword;
        $schemaValue = $schemaExcl;
        $notOp += '=';
      } else {
        if ($exclIsNumber) $schemaValue = Math[$isMax ? 'min' : 'max']($schemaExcl, $schema);
        if ($schemaExcl === ($exclIsNumber ? $schemaValue : true)) {
          $exclusive = true;
          $errorKeyword = $exclusiveKeyword;
          $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword;
          $notOp += '=';
        } else {
          $exclusive = false;
          $opStr += '=';
        }
      }
      var $opExpr = '\'' + $opStr + '\'';
      out += ' if ( ';
      if ($isData) {
        out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || ';
      }
      out += ' ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' || ' + ($data) + ' !== ' + ($data) + ') { ';
    }
  }
  $errorKeyword = $errorKeyword || $keyword;
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ($errorKeyword || '_limit') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { comparison: ' + ($opExpr) + ', limit: ' + ($schemaValue) + ', exclusive: ' + ($exclusive) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should be ' + ($opStr) + ' ';
      if ($isData) {
        out += '\' + ' + ($schemaValue);
      } else {
        out += '' + ($schemaValue) + '\'';
      }
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + ($schema);
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += ' } ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/_limitItems.js":
/*!***************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/_limitItems.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate__limitItems(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $errorKeyword;
  var $data = 'data' + ($dataLvl || '');
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $op = $keyword == 'maxItems' ? '>' : '<';
  out += 'if ( ';
  if ($isData) {
    out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || ';
  }
  out += ' ' + ($data) + '.length ' + ($op) + ' ' + ($schemaValue) + ') { ';
  var $errorKeyword = $keyword;
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ($errorKeyword || '_limitItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should NOT have ';
      if ($keyword == 'maxItems') {
        out += 'more';
      } else {
        out += 'fewer';
      }
      out += ' than ';
      if ($isData) {
        out += '\' + ' + ($schemaValue) + ' + \'';
      } else {
        out += '' + ($schema);
      }
      out += ' items\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + ($schema);
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += '} ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/_limitLength.js":
/*!****************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/_limitLength.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate__limitLength(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $errorKeyword;
  var $data = 'data' + ($dataLvl || '');
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $op = $keyword == 'maxLength' ? '>' : '<';
  out += 'if ( ';
  if ($isData) {
    out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || ';
  }
  if (it.opts.unicode === false) {
    out += ' ' + ($data) + '.length ';
  } else {
    out += ' ucs2length(' + ($data) + ') ';
  }
  out += ' ' + ($op) + ' ' + ($schemaValue) + ') { ';
  var $errorKeyword = $keyword;
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ($errorKeyword || '_limitLength') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should NOT be ';
      if ($keyword == 'maxLength') {
        out += 'longer';
      } else {
        out += 'shorter';
      }
      out += ' than ';
      if ($isData) {
        out += '\' + ' + ($schemaValue) + ' + \'';
      } else {
        out += '' + ($schema);
      }
      out += ' characters\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + ($schema);
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += '} ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/_limitProperties.js":
/*!********************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/_limitProperties.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate__limitProperties(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $errorKeyword;
  var $data = 'data' + ($dataLvl || '');
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $op = $keyword == 'maxProperties' ? '>' : '<';
  out += 'if ( ';
  if ($isData) {
    out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || ';
  }
  out += ' Object.keys(' + ($data) + ').length ' + ($op) + ' ' + ($schemaValue) + ') { ';
  var $errorKeyword = $keyword;
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ($errorKeyword || '_limitProperties') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should NOT have ';
      if ($keyword == 'maxProperties') {
        out += 'more';
      } else {
        out += 'fewer';
      }
      out += ' than ';
      if ($isData) {
        out += '\' + ' + ($schemaValue) + ' + \'';
      } else {
        out += '' + ($schema);
      }
      out += ' properties\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + ($schema);
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += '} ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/allOf.js":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/allOf.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_allOf(it, $keyword, $ruleType) {
  var out = ' ';
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $currentBaseId = $it.baseId,
    $allSchemasEmpty = true;
  var arr1 = $schema;
  if (arr1) {
    var $sch, $i = -1,
      l1 = arr1.length - 1;
    while ($i < l1) {
      $sch = arr1[$i += 1];
      if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) {
        $allSchemasEmpty = false;
        $it.schema = $sch;
        $it.schemaPath = $schemaPath + '[' + $i + ']';
        $it.errSchemaPath = $errSchemaPath + '/' + $i;
        out += '  ' + (it.validate($it)) + ' ';
        $it.baseId = $currentBaseId;
        if ($breakOnError) {
          out += ' if (' + ($nextValid) + ') { ';
          $closingBraces += '}';
        }
      }
    }
  }
  if ($breakOnError) {
    if ($allSchemasEmpty) {
      out += ' if (true) { ';
    } else {
      out += ' ' + ($closingBraces.slice(0, -1)) + ' ';
    }
  }
  out = it.util.cleanUpCode(out);
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/anyOf.js":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/anyOf.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_anyOf(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $noEmptySchema = $schema.every(function($sch) {
    return (it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all));
  });
  if ($noEmptySchema) {
    var $currentBaseId = $it.baseId;
    out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = false;  ';
    var $wasComposite = it.compositeRule;
    it.compositeRule = $it.compositeRule = true;
    var arr1 = $schema;
    if (arr1) {
      var $sch, $i = -1,
        l1 = arr1.length - 1;
      while ($i < l1) {
        $sch = arr1[$i += 1];
        $it.schema = $sch;
        $it.schemaPath = $schemaPath + '[' + $i + ']';
        $it.errSchemaPath = $errSchemaPath + '/' + $i;
        out += '  ' + (it.validate($it)) + ' ';
        $it.baseId = $currentBaseId;
        out += ' ' + ($valid) + ' = ' + ($valid) + ' || ' + ($nextValid) + '; if (!' + ($valid) + ') { ';
        $closingBraces += '}';
      }
    }
    it.compositeRule = $it.compositeRule = $wasComposite;
    out += ' ' + ($closingBraces) + ' if (!' + ($valid) + ') {   var err =   '; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ('anyOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
      if (it.opts.messages !== false) {
        out += ' , message: \'should match some schema in anyOf\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError(vErrors); ';
      } else {
        out += ' validate.errors = vErrors; return false; ';
      }
    }
    out += ' } else {  errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } ';
    if (it.opts.allErrors) {
      out += ' } ';
    }
    out = it.util.cleanUpCode(out);
  } else {
    if ($breakOnError) {
      out += ' if (true) { ';
    }
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/comment.js":
/*!***********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/comment.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_comment(it, $keyword, $ruleType) {
  var out = ' ';
  var $schema = it.schema[$keyword];
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $comment = it.util.toQuotedString($schema);
  if (it.opts.$comment === true) {
    out += ' console.log(' + ($comment) + ');';
  } else if (typeof it.opts.$comment == 'function') {
    out += ' self._opts.$comment(' + ($comment) + ', ' + (it.util.toQuotedString($errSchemaPath)) + ', validate.root.schema);';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/const.js":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/const.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_const(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  if (!$isData) {
    out += ' var schema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ';';
  }
  out += 'var ' + ($valid) + ' = equal(' + ($data) + ', schema' + ($lvl) + '); if (!' + ($valid) + ') {   ';
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('const') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { allowedValue: schema' + ($lvl) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should be equal to constant\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += ' }';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/contains.js":
/*!************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/contains.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_contains(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $idx = 'i' + $lvl,
    $dataNxt = $it.dataLevel = it.dataLevel + 1,
    $nextData = 'data' + $dataNxt,
    $currentBaseId = it.baseId,
    $nonEmptySchema = (it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all));
  out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';';
  if ($nonEmptySchema) {
    var $wasComposite = it.compositeRule;
    it.compositeRule = $it.compositeRule = true;
    $it.schema = $schema;
    $it.schemaPath = $schemaPath;
    $it.errSchemaPath = $errSchemaPath;
    out += ' var ' + ($nextValid) + ' = false; for (var ' + ($idx) + ' = 0; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { ';
    $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true);
    var $passData = $data + '[' + $idx + ']';
    $it.dataPathArr[$dataNxt] = $idx;
    var $code = it.validate($it);
    $it.baseId = $currentBaseId;
    if (it.util.varOccurences($code, $nextData) < 2) {
      out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
    } else {
      out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
    }
    out += ' if (' + ($nextValid) + ') break; }  ';
    it.compositeRule = $it.compositeRule = $wasComposite;
    out += ' ' + ($closingBraces) + ' if (!' + ($nextValid) + ') {';
  } else {
    out += ' if (' + ($data) + '.length == 0) {';
  }
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('contains') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should contain a valid item\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += ' } else { ';
  if ($nonEmptySchema) {
    out += '  errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } ';
  }
  if (it.opts.allErrors) {
    out += ' } ';
  }
  out = it.util.cleanUpCode(out);
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/custom.js":
/*!**********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/custom.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_custom(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $errorKeyword;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $errs = 'errs__' + $lvl;
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $rule = this,
    $definition = 'definition' + $lvl,
    $rDef = $rule.definition,
    $closingBraces = '';
  var $compile, $inline, $macro, $ruleValidate, $validateCode;
  if ($isData && $rDef.$data) {
    $validateCode = 'keywordValidate' + $lvl;
    var $validateSchema = $rDef.validateSchema;
    out += ' var ' + ($definition) + ' = RULES.custom[\'' + ($keyword) + '\'].definition; var ' + ($validateCode) + ' = ' + ($definition) + '.validate;';
  } else {
    $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it);
    if (!$ruleValidate) return;
    $schemaValue = 'validate.schema' + $schemaPath;
    $validateCode = $ruleValidate.code;
    $compile = $rDef.compile;
    $inline = $rDef.inline;
    $macro = $rDef.macro;
  }
  var $ruleErrs = $validateCode + '.errors',
    $i = 'i' + $lvl,
    $ruleErr = 'ruleErr' + $lvl,
    $asyncKeyword = $rDef.async;
  if ($asyncKeyword && !it.async) throw new Error('async keyword in sync schema');
  if (!($inline || $macro)) {
    out += '' + ($ruleErrs) + ' = null;';
  }
  out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';';
  if ($isData && $rDef.$data) {
    $closingBraces += '}';
    out += ' if (' + ($schemaValue) + ' === undefined) { ' + ($valid) + ' = true; } else { ';
    if ($validateSchema) {
      $closingBraces += '}';
      out += ' ' + ($valid) + ' = ' + ($definition) + '.validateSchema(' + ($schemaValue) + '); if (' + ($valid) + ') { ';
    }
  }
  if ($inline) {
    if ($rDef.statements) {
      out += ' ' + ($ruleValidate.validate) + ' ';
    } else {
      out += ' ' + ($valid) + ' = ' + ($ruleValidate.validate) + '; ';
    }
  } else if ($macro) {
    var $it = it.util.copy(it);
    var $closingBraces = '';
    $it.level++;
    var $nextValid = 'valid' + $it.level;
    $it.schema = $ruleValidate.validate;
    $it.schemaPath = '';
    var $wasComposite = it.compositeRule;
    it.compositeRule = $it.compositeRule = true;
    var $code = it.validate($it).replace(/validate\.schema/g, $validateCode);
    it.compositeRule = $it.compositeRule = $wasComposite;
    out += ' ' + ($code);
  } else {
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = '';
    out += '  ' + ($validateCode) + '.call( ';
    if (it.opts.passContext) {
      out += 'this';
    } else {
      out += 'self';
    }
    if ($compile || $rDef.schema === false) {
      out += ' , ' + ($data) + ' ';
    } else {
      out += ' , ' + ($schemaValue) + ' , ' + ($data) + ' , validate.schema' + (it.schemaPath) + ' ';
    }
    out += ' , (dataPath || \'\')';
    if (it.errorPath != '""') {
      out += ' + ' + (it.errorPath);
    }
    var $parentData = $dataLvl ? 'data' + (($dataLvl - 1) || '') : 'parentData',
      $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty';
    out += ' , ' + ($parentData) + ' , ' + ($parentDataProperty) + ' , rootData )  ';
    var def_callRuleValidate = out;
    out = $$outStack.pop();
    if ($rDef.errors === false) {
      out += ' ' + ($valid) + ' = ';
      if ($asyncKeyword) {
        out += 'await ';
      }
      out += '' + (def_callRuleValidate) + '; ';
    } else {
      if ($asyncKeyword) {
        $ruleErrs = 'customErrors' + $lvl;
        out += ' var ' + ($ruleErrs) + ' = null; try { ' + ($valid) + ' = await ' + (def_callRuleValidate) + '; } catch (e) { ' + ($valid) + ' = false; if (e instanceof ValidationError) ' + ($ruleErrs) + ' = e.errors; else throw e; } ';
      } else {
        out += ' ' + ($ruleErrs) + ' = null; ' + ($valid) + ' = ' + (def_callRuleValidate) + '; ';
      }
    }
  }
  if ($rDef.modifying) {
    out += ' if (' + ($parentData) + ') ' + ($data) + ' = ' + ($parentData) + '[' + ($parentDataProperty) + '];';
  }
  out += '' + ($closingBraces);
  if ($rDef.valid) {
    if ($breakOnError) {
      out += ' if (true) { ';
    }
  } else {
    out += ' if ( ';
    if ($rDef.valid === undefined) {
      out += ' !';
      if ($macro) {
        out += '' + ($nextValid);
      } else {
        out += '' + ($valid);
      }
    } else {
      out += ' ' + (!$rDef.valid) + ' ';
    }
    out += ') { ';
    $errorKeyword = $rule.keyword;
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = '';
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = ''; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ($errorKeyword || 'custom') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { keyword: \'' + ($rule.keyword) + '\' } ';
      if (it.opts.messages !== false) {
        out += ' , message: \'should pass "' + ($rule.keyword) + '" keyword validation\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    var __err = out;
    out = $$outStack.pop();
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError([' + (__err) + ']); ';
      } else {
        out += ' validate.errors = [' + (__err) + ']; return false; ';
      }
    } else {
      out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    }
    var def_customError = out;
    out = $$outStack.pop();
    if ($inline) {
      if ($rDef.errors) {
        if ($rDef.errors != 'full') {
          out += '  for (var ' + ($i) + '=' + ($errs) + '; ' + ($i) + '<errors; ' + ($i) + '++) { var ' + ($ruleErr) + ' = vErrors[' + ($i) + ']; if (' + ($ruleErr) + '.dataPath === undefined) ' + ($ruleErr) + '.dataPath = (dataPath || \'\') + ' + (it.errorPath) + '; if (' + ($ruleErr) + '.schemaPath === undefined) { ' + ($ruleErr) + '.schemaPath = "' + ($errSchemaPath) + '"; } ';
          if (it.opts.verbose) {
            out += ' ' + ($ruleErr) + '.schema = ' + ($schemaValue) + '; ' + ($ruleErr) + '.data = ' + ($data) + '; ';
          }
          out += ' } ';
        }
      } else {
        if ($rDef.errors === false) {
          out += ' ' + (def_customError) + ' ';
        } else {
          out += ' if (' + ($errs) + ' == errors) { ' + (def_customError) + ' } else {  for (var ' + ($i) + '=' + ($errs) + '; ' + ($i) + '<errors; ' + ($i) + '++) { var ' + ($ruleErr) + ' = vErrors[' + ($i) + ']; if (' + ($ruleErr) + '.dataPath === undefined) ' + ($ruleErr) + '.dataPath = (dataPath || \'\') + ' + (it.errorPath) + '; if (' + ($ruleErr) + '.schemaPath === undefined) { ' + ($ruleErr) + '.schemaPath = "' + ($errSchemaPath) + '"; } ';
          if (it.opts.verbose) {
            out += ' ' + ($ruleErr) + '.schema = ' + ($schemaValue) + '; ' + ($ruleErr) + '.data = ' + ($data) + '; ';
          }
          out += ' } } ';
        }
      }
    } else if ($macro) {
      out += '   var err =   '; /* istanbul ignore else */
      if (it.createErrors !== false) {
        out += ' { keyword: \'' + ($errorKeyword || 'custom') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { keyword: \'' + ($rule.keyword) + '\' } ';
        if (it.opts.messages !== false) {
          out += ' , message: \'should pass "' + ($rule.keyword) + '" keyword validation\' ';
        }
        if (it.opts.verbose) {
          out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
        }
        out += ' } ';
      } else {
        out += ' {} ';
      }
      out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
      if (!it.compositeRule && $breakOnError) {
        /* istanbul ignore if */
        if (it.async) {
          out += ' throw new ValidationError(vErrors); ';
        } else {
          out += ' validate.errors = vErrors; return false; ';
        }
      }
    } else {
      if ($rDef.errors === false) {
        out += ' ' + (def_customError) + ' ';
      } else {
        out += ' if (Array.isArray(' + ($ruleErrs) + ')) { if (vErrors === null) vErrors = ' + ($ruleErrs) + '; else vErrors = vErrors.concat(' + ($ruleErrs) + '); errors = vErrors.length;  for (var ' + ($i) + '=' + ($errs) + '; ' + ($i) + '<errors; ' + ($i) + '++) { var ' + ($ruleErr) + ' = vErrors[' + ($i) + ']; if (' + ($ruleErr) + '.dataPath === undefined) ' + ($ruleErr) + '.dataPath = (dataPath || \'\') + ' + (it.errorPath) + ';  ' + ($ruleErr) + '.schemaPath = "' + ($errSchemaPath) + '";  ';
        if (it.opts.verbose) {
          out += ' ' + ($ruleErr) + '.schema = ' + ($schemaValue) + '; ' + ($ruleErr) + '.data = ' + ($data) + '; ';
        }
        out += ' } } else { ' + (def_customError) + ' } ';
      }
    }
    out += ' } ';
    if ($breakOnError) {
      out += ' else { ';
    }
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/dependencies.js":
/*!****************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/dependencies.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_dependencies(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $schemaDeps = {},
    $propertyDeps = {},
    $ownProperties = it.opts.ownProperties;
  for ($property in $schema) {
    var $sch = $schema[$property];
    var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps;
    $deps[$property] = $sch;
  }
  out += 'var ' + ($errs) + ' = errors;';
  var $currentErrorPath = it.errorPath;
  out += 'var missing' + ($lvl) + ';';
  for (var $property in $propertyDeps) {
    $deps = $propertyDeps[$property];
    if ($deps.length) {
      out += ' if ( ' + ($data) + (it.util.getProperty($property)) + ' !== undefined ';
      if ($ownProperties) {
        out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($property)) + '\') ';
      }
      if ($breakOnError) {
        out += ' && ( ';
        var arr1 = $deps;
        if (arr1) {
          var $propertyKey, $i = -1,
            l1 = arr1.length - 1;
          while ($i < l1) {
            $propertyKey = arr1[$i += 1];
            if ($i) {
              out += ' || ';
            }
            var $prop = it.util.getProperty($propertyKey),
              $useData = $data + $prop;
            out += ' ( ( ' + ($useData) + ' === undefined ';
            if ($ownProperties) {
              out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
            }
            out += ') && (missing' + ($lvl) + ' = ' + (it.util.toQuotedString(it.opts.jsonPointers ? $propertyKey : $prop)) + ') ) ';
          }
        }
        out += ')) {  ';
        var $propertyPath = 'missing' + $lvl,
          $missingProperty = '\' + ' + $propertyPath + ' + \'';
        if (it.opts._errorDataPathProperty) {
          it.errorPath = it.opts.jsonPointers ? it.util.getPathExpr($currentErrorPath, $propertyPath, true) : $currentErrorPath + ' + ' + $propertyPath;
        }
        var $$outStack = $$outStack || [];
        $$outStack.push(out);
        out = ''; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ('dependencies') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { property: \'' + (it.util.escapeQuotes($property)) + '\', missingProperty: \'' + ($missingProperty) + '\', depsCount: ' + ($deps.length) + ', deps: \'' + (it.util.escapeQuotes($deps.length == 1 ? $deps[0] : $deps.join(", "))) + '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'should have ';
            if ($deps.length == 1) {
              out += 'property ' + (it.util.escapeQuotes($deps[0]));
            } else {
              out += 'properties ' + (it.util.escapeQuotes($deps.join(", ")));
            }
            out += ' when property ' + (it.util.escapeQuotes($property)) + ' is present\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        var __err = out;
        out = $$outStack.pop();
        if (!it.compositeRule && $breakOnError) {
          /* istanbul ignore if */
          if (it.async) {
            out += ' throw new ValidationError([' + (__err) + ']); ';
          } else {
            out += ' validate.errors = [' + (__err) + ']; return false; ';
          }
        } else {
          out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
        }
      } else {
        out += ' ) { ';
        var arr2 = $deps;
        if (arr2) {
          var $propertyKey, i2 = -1,
            l2 = arr2.length - 1;
          while (i2 < l2) {
            $propertyKey = arr2[i2 += 1];
            var $prop = it.util.getProperty($propertyKey),
              $missingProperty = it.util.escapeQuotes($propertyKey),
              $useData = $data + $prop;
            if (it.opts._errorDataPathProperty) {
              it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers);
            }
            out += ' if ( ' + ($useData) + ' === undefined ';
            if ($ownProperties) {
              out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
            }
            out += ') {  var err =   '; /* istanbul ignore else */
            if (it.createErrors !== false) {
              out += ' { keyword: \'' + ('dependencies') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { property: \'' + (it.util.escapeQuotes($property)) + '\', missingProperty: \'' + ($missingProperty) + '\', depsCount: ' + ($deps.length) + ', deps: \'' + (it.util.escapeQuotes($deps.length == 1 ? $deps[0] : $deps.join(", "))) + '\' } ';
              if (it.opts.messages !== false) {
                out += ' , message: \'should have ';
                if ($deps.length == 1) {
                  out += 'property ' + (it.util.escapeQuotes($deps[0]));
                } else {
                  out += 'properties ' + (it.util.escapeQuotes($deps.join(", ")));
                }
                out += ' when property ' + (it.util.escapeQuotes($property)) + ' is present\' ';
              }
              if (it.opts.verbose) {
                out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
              }
              out += ' } ';
            } else {
              out += ' {} ';
            }
            out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } ';
          }
        }
      }
      out += ' }   ';
      if ($breakOnError) {
        $closingBraces += '}';
        out += ' else { ';
      }
    }
  }
  it.errorPath = $currentErrorPath;
  var $currentBaseId = $it.baseId;
  for (var $property in $schemaDeps) {
    var $sch = $schemaDeps[$property];
    if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) {
      out += ' ' + ($nextValid) + ' = true; if ( ' + ($data) + (it.util.getProperty($property)) + ' !== undefined ';
      if ($ownProperties) {
        out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($property)) + '\') ';
      }
      out += ') { ';
      $it.schema = $sch;
      $it.schemaPath = $schemaPath + it.util.getProperty($property);
      $it.errSchemaPath = $errSchemaPath + '/' + it.util.escapeFragment($property);
      out += '  ' + (it.validate($it)) + ' ';
      $it.baseId = $currentBaseId;
      out += ' }  ';
      if ($breakOnError) {
        out += ' if (' + ($nextValid) + ') { ';
        $closingBraces += '}';
      }
    }
  }
  if ($breakOnError) {
    out += '   ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {';
  }
  out = it.util.cleanUpCode(out);
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/enum.js":
/*!********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/enum.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_enum(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $i = 'i' + $lvl,
    $vSchema = 'schema' + $lvl;
  if (!$isData) {
    out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + ';';
  }
  out += 'var ' + ($valid) + ';';
  if ($isData) {
    out += ' if (schema' + ($lvl) + ' === undefined) ' + ($valid) + ' = true; else if (!Array.isArray(schema' + ($lvl) + ')) ' + ($valid) + ' = false; else {';
  }
  out += '' + ($valid) + ' = false;for (var ' + ($i) + '=0; ' + ($i) + '<' + ($vSchema) + '.length; ' + ($i) + '++) if (equal(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + '])) { ' + ($valid) + ' = true; break; }';
  if ($isData) {
    out += '  }  ';
  }
  out += ' if (!' + ($valid) + ') {   ';
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('enum') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { allowedValues: schema' + ($lvl) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should be equal to one of the allowed values\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += ' }';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/format.js":
/*!**********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/format.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_format(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  if (it.opts.format === false) {
    if ($breakOnError) {
      out += ' if (true) { ';
    }
    return out;
  }
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $unknownFormats = it.opts.unknownFormats,
    $allowUnknown = Array.isArray($unknownFormats);
  if ($isData) {
    var $format = 'format' + $lvl,
      $isObject = 'isObject' + $lvl,
      $formatType = 'formatType' + $lvl;
    out += ' var ' + ($format) + ' = formats[' + ($schemaValue) + ']; var ' + ($isObject) + ' = typeof ' + ($format) + ' == \'object\' && !(' + ($format) + ' instanceof RegExp) && ' + ($format) + '.validate; var ' + ($formatType) + ' = ' + ($isObject) + ' && ' + ($format) + '.type || \'string\'; if (' + ($isObject) + ') { ';
    if (it.async) {
      out += ' var async' + ($lvl) + ' = ' + ($format) + '.async; ';
    }
    out += ' ' + ($format) + ' = ' + ($format) + '.validate; } if (  ';
    if ($isData) {
      out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || ';
    }
    out += ' (';
    if ($unknownFormats != 'ignore') {
      out += ' (' + ($schemaValue) + ' && !' + ($format) + ' ';
      if ($allowUnknown) {
        out += ' && self._opts.unknownFormats.indexOf(' + ($schemaValue) + ') == -1 ';
      }
      out += ') || ';
    }
    out += ' (' + ($format) + ' && ' + ($formatType) + ' == \'' + ($ruleType) + '\' && !(typeof ' + ($format) + ' == \'function\' ? ';
    if (it.async) {
      out += ' (async' + ($lvl) + ' ? await ' + ($format) + '(' + ($data) + ') : ' + ($format) + '(' + ($data) + ')) ';
    } else {
      out += ' ' + ($format) + '(' + ($data) + ') ';
    }
    out += ' : ' + ($format) + '.test(' + ($data) + '))))) {';
  } else {
    var $format = it.formats[$schema];
    if (!$format) {
      if ($unknownFormats == 'ignore') {
        it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"');
        if ($breakOnError) {
          out += ' if (true) { ';
        }
        return out;
      } else if ($allowUnknown && $unknownFormats.indexOf($schema) >= 0) {
        if ($breakOnError) {
          out += ' if (true) { ';
        }
        return out;
      } else {
        throw new Error('unknown format "' + $schema + '" is used in schema at path "' + it.errSchemaPath + '"');
      }
    }
    var $isObject = typeof $format == 'object' && !($format instanceof RegExp) && $format.validate;
    var $formatType = $isObject && $format.type || 'string';
    if ($isObject) {
      var $async = $format.async === true;
      $format = $format.validate;
    }
    if ($formatType != $ruleType) {
      if ($breakOnError) {
        out += ' if (true) { ';
      }
      return out;
    }
    if ($async) {
      if (!it.async) throw new Error('async format in sync schema');
      var $formatRef = 'formats' + it.util.getProperty($schema) + '.validate';
      out += ' if (!(await ' + ($formatRef) + '(' + ($data) + '))) { ';
    } else {
      out += ' if (! ';
      var $formatRef = 'formats' + it.util.getProperty($schema);
      if ($isObject) $formatRef += '.validate';
      if (typeof $format == 'function') {
        out += ' ' + ($formatRef) + '(' + ($data) + ') ';
      } else {
        out += ' ' + ($formatRef) + '.test(' + ($data) + ') ';
      }
      out += ') { ';
    }
  }
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('format') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { format:  ';
    if ($isData) {
      out += '' + ($schemaValue);
    } else {
      out += '' + (it.util.toQuotedString($schema));
    }
    out += '  } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should match format "';
      if ($isData) {
        out += '\' + ' + ($schemaValue) + ' + \'';
      } else {
        out += '' + (it.util.escapeQuotes($schema));
      }
      out += '"\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + (it.util.toQuotedString($schema));
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += ' } ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/if.js":
/*!******************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/if.js ***!
  \******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_if(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $thenSch = it.schema['then'],
    $elseSch = it.schema['else'],
    $thenPresent = $thenSch !== undefined && (it.opts.strictKeywords ? typeof $thenSch == 'object' && Object.keys($thenSch).length > 0 : it.util.schemaHasRules($thenSch, it.RULES.all)),
    $elsePresent = $elseSch !== undefined && (it.opts.strictKeywords ? typeof $elseSch == 'object' && Object.keys($elseSch).length > 0 : it.util.schemaHasRules($elseSch, it.RULES.all)),
    $currentBaseId = $it.baseId;
  if ($thenPresent || $elsePresent) {
    var $ifClause;
    $it.createErrors = false;
    $it.schema = $schema;
    $it.schemaPath = $schemaPath;
    $it.errSchemaPath = $errSchemaPath;
    out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = true;  ';
    var $wasComposite = it.compositeRule;
    it.compositeRule = $it.compositeRule = true;
    out += '  ' + (it.validate($it)) + ' ';
    $it.baseId = $currentBaseId;
    $it.createErrors = true;
    out += '  errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; }  ';
    it.compositeRule = $it.compositeRule = $wasComposite;
    if ($thenPresent) {
      out += ' if (' + ($nextValid) + ') {  ';
      $it.schema = it.schema['then'];
      $it.schemaPath = it.schemaPath + '.then';
      $it.errSchemaPath = it.errSchemaPath + '/then';
      out += '  ' + (it.validate($it)) + ' ';
      $it.baseId = $currentBaseId;
      out += ' ' + ($valid) + ' = ' + ($nextValid) + '; ';
      if ($thenPresent && $elsePresent) {
        $ifClause = 'ifClause' + $lvl;
        out += ' var ' + ($ifClause) + ' = \'then\'; ';
      } else {
        $ifClause = '\'then\'';
      }
      out += ' } ';
      if ($elsePresent) {
        out += ' else { ';
      }
    } else {
      out += ' if (!' + ($nextValid) + ') { ';
    }
    if ($elsePresent) {
      $it.schema = it.schema['else'];
      $it.schemaPath = it.schemaPath + '.else';
      $it.errSchemaPath = it.errSchemaPath + '/else';
      out += '  ' + (it.validate($it)) + ' ';
      $it.baseId = $currentBaseId;
      out += ' ' + ($valid) + ' = ' + ($nextValid) + '; ';
      if ($thenPresent && $elsePresent) {
        $ifClause = 'ifClause' + $lvl;
        out += ' var ' + ($ifClause) + ' = \'else\'; ';
      } else {
        $ifClause = '\'else\'';
      }
      out += ' } ';
    }
    out += ' if (!' + ($valid) + ') {   var err =   '; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ('if') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { failingKeyword: ' + ($ifClause) + ' } ';
      if (it.opts.messages !== false) {
        out += ' , message: \'should match "\' + ' + ($ifClause) + ' + \'" schema\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError(vErrors); ';
      } else {
        out += ' validate.errors = vErrors; return false; ';
      }
    }
    out += ' }   ';
    if ($breakOnError) {
      out += ' else { ';
    }
    out = it.util.cleanUpCode(out);
  } else {
    if ($breakOnError) {
      out += ' if (true) { ';
    }
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/index.js":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/index.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


//all requires must be explicit because browserify won't work with dynamic requires
module.exports = {
  '$ref': __webpack_require__(/*! ./ref */ "./node_modules/ajv/lib/dotjs/ref.js"),
  allOf: __webpack_require__(/*! ./allOf */ "./node_modules/ajv/lib/dotjs/allOf.js"),
  anyOf: __webpack_require__(/*! ./anyOf */ "./node_modules/ajv/lib/dotjs/anyOf.js"),
  '$comment': __webpack_require__(/*! ./comment */ "./node_modules/ajv/lib/dotjs/comment.js"),
  const: __webpack_require__(/*! ./const */ "./node_modules/ajv/lib/dotjs/const.js"),
  contains: __webpack_require__(/*! ./contains */ "./node_modules/ajv/lib/dotjs/contains.js"),
  dependencies: __webpack_require__(/*! ./dependencies */ "./node_modules/ajv/lib/dotjs/dependencies.js"),
  'enum': __webpack_require__(/*! ./enum */ "./node_modules/ajv/lib/dotjs/enum.js"),
  format: __webpack_require__(/*! ./format */ "./node_modules/ajv/lib/dotjs/format.js"),
  'if': __webpack_require__(/*! ./if */ "./node_modules/ajv/lib/dotjs/if.js"),
  items: __webpack_require__(/*! ./items */ "./node_modules/ajv/lib/dotjs/items.js"),
  maximum: __webpack_require__(/*! ./_limit */ "./node_modules/ajv/lib/dotjs/_limit.js"),
  minimum: __webpack_require__(/*! ./_limit */ "./node_modules/ajv/lib/dotjs/_limit.js"),
  maxItems: __webpack_require__(/*! ./_limitItems */ "./node_modules/ajv/lib/dotjs/_limitItems.js"),
  minItems: __webpack_require__(/*! ./_limitItems */ "./node_modules/ajv/lib/dotjs/_limitItems.js"),
  maxLength: __webpack_require__(/*! ./_limitLength */ "./node_modules/ajv/lib/dotjs/_limitLength.js"),
  minLength: __webpack_require__(/*! ./_limitLength */ "./node_modules/ajv/lib/dotjs/_limitLength.js"),
  maxProperties: __webpack_require__(/*! ./_limitProperties */ "./node_modules/ajv/lib/dotjs/_limitProperties.js"),
  minProperties: __webpack_require__(/*! ./_limitProperties */ "./node_modules/ajv/lib/dotjs/_limitProperties.js"),
  multipleOf: __webpack_require__(/*! ./multipleOf */ "./node_modules/ajv/lib/dotjs/multipleOf.js"),
  not: __webpack_require__(/*! ./not */ "./node_modules/ajv/lib/dotjs/not.js"),
  oneOf: __webpack_require__(/*! ./oneOf */ "./node_modules/ajv/lib/dotjs/oneOf.js"),
  pattern: __webpack_require__(/*! ./pattern */ "./node_modules/ajv/lib/dotjs/pattern.js"),
  properties: __webpack_require__(/*! ./properties */ "./node_modules/ajv/lib/dotjs/properties.js"),
  propertyNames: __webpack_require__(/*! ./propertyNames */ "./node_modules/ajv/lib/dotjs/propertyNames.js"),
  required: __webpack_require__(/*! ./required */ "./node_modules/ajv/lib/dotjs/required.js"),
  uniqueItems: __webpack_require__(/*! ./uniqueItems */ "./node_modules/ajv/lib/dotjs/uniqueItems.js"),
  validate: __webpack_require__(/*! ./validate */ "./node_modules/ajv/lib/dotjs/validate.js")
};


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/items.js":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/items.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_items(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $idx = 'i' + $lvl,
    $dataNxt = $it.dataLevel = it.dataLevel + 1,
    $nextData = 'data' + $dataNxt,
    $currentBaseId = it.baseId;
  out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';';
  if (Array.isArray($schema)) {
    var $additionalItems = it.schema.additionalItems;
    if ($additionalItems === false) {
      out += ' ' + ($valid) + ' = ' + ($data) + '.length <= ' + ($schema.length) + '; ';
      var $currErrSchemaPath = $errSchemaPath;
      $errSchemaPath = it.errSchemaPath + '/additionalItems';
      out += '  if (!' + ($valid) + ') {   ';
      var $$outStack = $$outStack || [];
      $$outStack.push(out);
      out = ''; /* istanbul ignore else */
      if (it.createErrors !== false) {
        out += ' { keyword: \'' + ('additionalItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schema.length) + ' } ';
        if (it.opts.messages !== false) {
          out += ' , message: \'should NOT have more than ' + ($schema.length) + ' items\' ';
        }
        if (it.opts.verbose) {
          out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
        }
        out += ' } ';
      } else {
        out += ' {} ';
      }
      var __err = out;
      out = $$outStack.pop();
      if (!it.compositeRule && $breakOnError) {
        /* istanbul ignore if */
        if (it.async) {
          out += ' throw new ValidationError([' + (__err) + ']); ';
        } else {
          out += ' validate.errors = [' + (__err) + ']; return false; ';
        }
      } else {
        out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
      }
      out += ' } ';
      $errSchemaPath = $currErrSchemaPath;
      if ($breakOnError) {
        $closingBraces += '}';
        out += ' else { ';
      }
    }
    var arr1 = $schema;
    if (arr1) {
      var $sch, $i = -1,
        l1 = arr1.length - 1;
      while ($i < l1) {
        $sch = arr1[$i += 1];
        if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) {
          out += ' ' + ($nextValid) + ' = true; if (' + ($data) + '.length > ' + ($i) + ') { ';
          var $passData = $data + '[' + $i + ']';
          $it.schema = $sch;
          $it.schemaPath = $schemaPath + '[' + $i + ']';
          $it.errSchemaPath = $errSchemaPath + '/' + $i;
          $it.errorPath = it.util.getPathExpr(it.errorPath, $i, it.opts.jsonPointers, true);
          $it.dataPathArr[$dataNxt] = $i;
          var $code = it.validate($it);
          $it.baseId = $currentBaseId;
          if (it.util.varOccurences($code, $nextData) < 2) {
            out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
          } else {
            out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
          }
          out += ' }  ';
          if ($breakOnError) {
            out += ' if (' + ($nextValid) + ') { ';
            $closingBraces += '}';
          }
        }
      }
    }
    if (typeof $additionalItems == 'object' && (it.opts.strictKeywords ? typeof $additionalItems == 'object' && Object.keys($additionalItems).length > 0 : it.util.schemaHasRules($additionalItems, it.RULES.all))) {
      $it.schema = $additionalItems;
      $it.schemaPath = it.schemaPath + '.additionalItems';
      $it.errSchemaPath = it.errSchemaPath + '/additionalItems';
      out += ' ' + ($nextValid) + ' = true; if (' + ($data) + '.length > ' + ($schema.length) + ') {  for (var ' + ($idx) + ' = ' + ($schema.length) + '; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { ';
      $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true);
      var $passData = $data + '[' + $idx + ']';
      $it.dataPathArr[$dataNxt] = $idx;
      var $code = it.validate($it);
      $it.baseId = $currentBaseId;
      if (it.util.varOccurences($code, $nextData) < 2) {
        out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
      } else {
        out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
      }
      if ($breakOnError) {
        out += ' if (!' + ($nextValid) + ') break; ';
      }
      out += ' } }  ';
      if ($breakOnError) {
        out += ' if (' + ($nextValid) + ') { ';
        $closingBraces += '}';
      }
    }
  } else if ((it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all))) {
    $it.schema = $schema;
    $it.schemaPath = $schemaPath;
    $it.errSchemaPath = $errSchemaPath;
    out += '  for (var ' + ($idx) + ' = ' + (0) + '; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { ';
    $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true);
    var $passData = $data + '[' + $idx + ']';
    $it.dataPathArr[$dataNxt] = $idx;
    var $code = it.validate($it);
    $it.baseId = $currentBaseId;
    if (it.util.varOccurences($code, $nextData) < 2) {
      out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
    } else {
      out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
    }
    if ($breakOnError) {
      out += ' if (!' + ($nextValid) + ') break; ';
    }
    out += ' }';
  }
  if ($breakOnError) {
    out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {';
  }
  out = it.util.cleanUpCode(out);
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/multipleOf.js":
/*!**************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/multipleOf.js ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_multipleOf(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  out += 'var division' + ($lvl) + ';if (';
  if ($isData) {
    out += ' ' + ($schemaValue) + ' !== undefined && ( typeof ' + ($schemaValue) + ' != \'number\' || ';
  }
  out += ' (division' + ($lvl) + ' = ' + ($data) + ' / ' + ($schemaValue) + ', ';
  if (it.opts.multipleOfPrecision) {
    out += ' Math.abs(Math.round(division' + ($lvl) + ') - division' + ($lvl) + ') > 1e-' + (it.opts.multipleOfPrecision) + ' ';
  } else {
    out += ' division' + ($lvl) + ' !== parseInt(division' + ($lvl) + ') ';
  }
  out += ' ) ';
  if ($isData) {
    out += '  )  ';
  }
  out += ' ) {   ';
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('multipleOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { multipleOf: ' + ($schemaValue) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should be multiple of ';
      if ($isData) {
        out += '\' + ' + ($schemaValue);
      } else {
        out += '' + ($schemaValue) + '\'';
      }
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + ($schema);
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += '} ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/not.js":
/*!*******************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/not.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_not(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  if ((it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all))) {
    $it.schema = $schema;
    $it.schemaPath = $schemaPath;
    $it.errSchemaPath = $errSchemaPath;
    out += ' var ' + ($errs) + ' = errors;  ';
    var $wasComposite = it.compositeRule;
    it.compositeRule = $it.compositeRule = true;
    $it.createErrors = false;
    var $allErrorsOption;
    if ($it.opts.allErrors) {
      $allErrorsOption = $it.opts.allErrors;
      $it.opts.allErrors = false;
    }
    out += ' ' + (it.validate($it)) + ' ';
    $it.createErrors = true;
    if ($allErrorsOption) $it.opts.allErrors = $allErrorsOption;
    it.compositeRule = $it.compositeRule = $wasComposite;
    out += ' if (' + ($nextValid) + ') {   ';
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = ''; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ('not') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
      if (it.opts.messages !== false) {
        out += ' , message: \'should NOT be valid\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    var __err = out;
    out = $$outStack.pop();
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError([' + (__err) + ']); ';
      } else {
        out += ' validate.errors = [' + (__err) + ']; return false; ';
      }
    } else {
      out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    }
    out += ' } else {  errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } ';
    if (it.opts.allErrors) {
      out += ' } ';
    }
  } else {
    out += '  var err =   '; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ('not') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
      if (it.opts.messages !== false) {
        out += ' , message: \'should NOT be valid\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    if ($breakOnError) {
      out += ' if (false) { ';
    }
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/oneOf.js":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/oneOf.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_oneOf(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $currentBaseId = $it.baseId,
    $prevValid = 'prevValid' + $lvl,
    $passingSchemas = 'passingSchemas' + $lvl;
  out += 'var ' + ($errs) + ' = errors , ' + ($prevValid) + ' = false , ' + ($valid) + ' = false , ' + ($passingSchemas) + ' = null; ';
  var $wasComposite = it.compositeRule;
  it.compositeRule = $it.compositeRule = true;
  var arr1 = $schema;
  if (arr1) {
    var $sch, $i = -1,
      l1 = arr1.length - 1;
    while ($i < l1) {
      $sch = arr1[$i += 1];
      if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) {
        $it.schema = $sch;
        $it.schemaPath = $schemaPath + '[' + $i + ']';
        $it.errSchemaPath = $errSchemaPath + '/' + $i;
        out += '  ' + (it.validate($it)) + ' ';
        $it.baseId = $currentBaseId;
      } else {
        out += ' var ' + ($nextValid) + ' = true; ';
      }
      if ($i) {
        out += ' if (' + ($nextValid) + ' && ' + ($prevValid) + ') { ' + ($valid) + ' = false; ' + ($passingSchemas) + ' = [' + ($passingSchemas) + ', ' + ($i) + ']; } else { ';
        $closingBraces += '}';
      }
      out += ' if (' + ($nextValid) + ') { ' + ($valid) + ' = ' + ($prevValid) + ' = true; ' + ($passingSchemas) + ' = ' + ($i) + '; }';
    }
  }
  it.compositeRule = $it.compositeRule = $wasComposite;
  out += '' + ($closingBraces) + 'if (!' + ($valid) + ') {   var err =   '; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('oneOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { passingSchemas: ' + ($passingSchemas) + ' } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should match exactly one schema in oneOf\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError(vErrors); ';
    } else {
      out += ' validate.errors = vErrors; return false; ';
    }
  }
  out += '} else {  errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; }';
  if (it.opts.allErrors) {
    out += ' } ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/pattern.js":
/*!***********************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/pattern.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_pattern(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $regexp = $isData ? '(new RegExp(' + $schemaValue + '))' : it.usePattern($schema);
  out += 'if ( ';
  if ($isData) {
    out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || ';
  }
  out += ' !' + ($regexp) + '.test(' + ($data) + ') ) {   ';
  var $$outStack = $$outStack || [];
  $$outStack.push(out);
  out = ''; /* istanbul ignore else */
  if (it.createErrors !== false) {
    out += ' { keyword: \'' + ('pattern') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { pattern:  ';
    if ($isData) {
      out += '' + ($schemaValue);
    } else {
      out += '' + (it.util.toQuotedString($schema));
    }
    out += '  } ';
    if (it.opts.messages !== false) {
      out += ' , message: \'should match pattern "';
      if ($isData) {
        out += '\' + ' + ($schemaValue) + ' + \'';
      } else {
        out += '' + (it.util.escapeQuotes($schema));
      }
      out += '"\' ';
    }
    if (it.opts.verbose) {
      out += ' , schema:  ';
      if ($isData) {
        out += 'validate.schema' + ($schemaPath);
      } else {
        out += '' + (it.util.toQuotedString($schema));
      }
      out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
    }
    out += ' } ';
  } else {
    out += ' {} ';
  }
  var __err = out;
  out = $$outStack.pop();
  if (!it.compositeRule && $breakOnError) {
    /* istanbul ignore if */
    if (it.async) {
      out += ' throw new ValidationError([' + (__err) + ']); ';
    } else {
      out += ' validate.errors = [' + (__err) + ']; return false; ';
    }
  } else {
    out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
  }
  out += '} ';
  if ($breakOnError) {
    out += ' else { ';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/properties.js":
/*!**************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/properties.js ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_properties(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  var $key = 'key' + $lvl,
    $idx = 'idx' + $lvl,
    $dataNxt = $it.dataLevel = it.dataLevel + 1,
    $nextData = 'data' + $dataNxt,
    $dataProperties = 'dataProperties' + $lvl;
  var $schemaKeys = Object.keys($schema || {}),
    $pProperties = it.schema.patternProperties || {},
    $pPropertyKeys = Object.keys($pProperties),
    $aProperties = it.schema.additionalProperties,
    $someProperties = $schemaKeys.length || $pPropertyKeys.length,
    $noAdditional = $aProperties === false,
    $additionalIsSchema = typeof $aProperties == 'object' && Object.keys($aProperties).length,
    $removeAdditional = it.opts.removeAdditional,
    $checkAdditional = $noAdditional || $additionalIsSchema || $removeAdditional,
    $ownProperties = it.opts.ownProperties,
    $currentBaseId = it.baseId;
  var $required = it.schema.required;
  if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) var $requiredHash = it.util.toHash($required);
  out += 'var ' + ($errs) + ' = errors;var ' + ($nextValid) + ' = true;';
  if ($ownProperties) {
    out += ' var ' + ($dataProperties) + ' = undefined;';
  }
  if ($checkAdditional) {
    if ($ownProperties) {
      out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; ';
    } else {
      out += ' for (var ' + ($key) + ' in ' + ($data) + ') { ';
    }
    if ($someProperties) {
      out += ' var isAdditional' + ($lvl) + ' = !(false ';
      if ($schemaKeys.length) {
        if ($schemaKeys.length > 8) {
          out += ' || validate.schema' + ($schemaPath) + '.hasOwnProperty(' + ($key) + ') ';
        } else {
          var arr1 = $schemaKeys;
          if (arr1) {
            var $propertyKey, i1 = -1,
              l1 = arr1.length - 1;
            while (i1 < l1) {
              $propertyKey = arr1[i1 += 1];
              out += ' || ' + ($key) + ' == ' + (it.util.toQuotedString($propertyKey)) + ' ';
            }
          }
        }
      }
      if ($pPropertyKeys.length) {
        var arr2 = $pPropertyKeys;
        if (arr2) {
          var $pProperty, $i = -1,
            l2 = arr2.length - 1;
          while ($i < l2) {
            $pProperty = arr2[$i += 1];
            out += ' || ' + (it.usePattern($pProperty)) + '.test(' + ($key) + ') ';
          }
        }
      }
      out += ' ); if (isAdditional' + ($lvl) + ') { ';
    }
    if ($removeAdditional == 'all') {
      out += ' delete ' + ($data) + '[' + ($key) + ']; ';
    } else {
      var $currentErrorPath = it.errorPath;
      var $additionalProperty = '\' + ' + $key + ' + \'';
      if (it.opts._errorDataPathProperty) {
        it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers);
      }
      if ($noAdditional) {
        if ($removeAdditional) {
          out += ' delete ' + ($data) + '[' + ($key) + ']; ';
        } else {
          out += ' ' + ($nextValid) + ' = false; ';
          var $currErrSchemaPath = $errSchemaPath;
          $errSchemaPath = it.errSchemaPath + '/additionalProperties';
          var $$outStack = $$outStack || [];
          $$outStack.push(out);
          out = ''; /* istanbul ignore else */
          if (it.createErrors !== false) {
            out += ' { keyword: \'' + ('additionalProperties') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { additionalProperty: \'' + ($additionalProperty) + '\' } ';
            if (it.opts.messages !== false) {
              out += ' , message: \'';
              if (it.opts._errorDataPathProperty) {
                out += 'is an invalid additional property';
              } else {
                out += 'should NOT have additional properties';
              }
              out += '\' ';
            }
            if (it.opts.verbose) {
              out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
            }
            out += ' } ';
          } else {
            out += ' {} ';
          }
          var __err = out;
          out = $$outStack.pop();
          if (!it.compositeRule && $breakOnError) {
            /* istanbul ignore if */
            if (it.async) {
              out += ' throw new ValidationError([' + (__err) + ']); ';
            } else {
              out += ' validate.errors = [' + (__err) + ']; return false; ';
            }
          } else {
            out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
          }
          $errSchemaPath = $currErrSchemaPath;
          if ($breakOnError) {
            out += ' break; ';
          }
        }
      } else if ($additionalIsSchema) {
        if ($removeAdditional == 'failing') {
          out += ' var ' + ($errs) + ' = errors;  ';
          var $wasComposite = it.compositeRule;
          it.compositeRule = $it.compositeRule = true;
          $it.schema = $aProperties;
          $it.schemaPath = it.schemaPath + '.additionalProperties';
          $it.errSchemaPath = it.errSchemaPath + '/additionalProperties';
          $it.errorPath = it.opts._errorDataPathProperty ? it.errorPath : it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers);
          var $passData = $data + '[' + $key + ']';
          $it.dataPathArr[$dataNxt] = $key;
          var $code = it.validate($it);
          $it.baseId = $currentBaseId;
          if (it.util.varOccurences($code, $nextData) < 2) {
            out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
          } else {
            out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
          }
          out += ' if (!' + ($nextValid) + ') { errors = ' + ($errs) + '; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete ' + ($data) + '[' + ($key) + ']; }  ';
          it.compositeRule = $it.compositeRule = $wasComposite;
        } else {
          $it.schema = $aProperties;
          $it.schemaPath = it.schemaPath + '.additionalProperties';
          $it.errSchemaPath = it.errSchemaPath + '/additionalProperties';
          $it.errorPath = it.opts._errorDataPathProperty ? it.errorPath : it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers);
          var $passData = $data + '[' + $key + ']';
          $it.dataPathArr[$dataNxt] = $key;
          var $code = it.validate($it);
          $it.baseId = $currentBaseId;
          if (it.util.varOccurences($code, $nextData) < 2) {
            out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
          } else {
            out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
          }
          if ($breakOnError) {
            out += ' if (!' + ($nextValid) + ') break; ';
          }
        }
      }
      it.errorPath = $currentErrorPath;
    }
    if ($someProperties) {
      out += ' } ';
    }
    out += ' }  ';
    if ($breakOnError) {
      out += ' if (' + ($nextValid) + ') { ';
      $closingBraces += '}';
    }
  }
  var $useDefaults = it.opts.useDefaults && !it.compositeRule;
  if ($schemaKeys.length) {
    var arr3 = $schemaKeys;
    if (arr3) {
      var $propertyKey, i3 = -1,
        l3 = arr3.length - 1;
      while (i3 < l3) {
        $propertyKey = arr3[i3 += 1];
        var $sch = $schema[$propertyKey];
        if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) {
          var $prop = it.util.getProperty($propertyKey),
            $passData = $data + $prop,
            $hasDefault = $useDefaults && $sch.default !== undefined;
          $it.schema = $sch;
          $it.schemaPath = $schemaPath + $prop;
          $it.errSchemaPath = $errSchemaPath + '/' + it.util.escapeFragment($propertyKey);
          $it.errorPath = it.util.getPath(it.errorPath, $propertyKey, it.opts.jsonPointers);
          $it.dataPathArr[$dataNxt] = it.util.toQuotedString($propertyKey);
          var $code = it.validate($it);
          $it.baseId = $currentBaseId;
          if (it.util.varOccurences($code, $nextData) < 2) {
            $code = it.util.varReplace($code, $nextData, $passData);
            var $useData = $passData;
          } else {
            var $useData = $nextData;
            out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ';
          }
          if ($hasDefault) {
            out += ' ' + ($code) + ' ';
          } else {
            if ($requiredHash && $requiredHash[$propertyKey]) {
              out += ' if ( ' + ($useData) + ' === undefined ';
              if ($ownProperties) {
                out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
              }
              out += ') { ' + ($nextValid) + ' = false; ';
              var $currentErrorPath = it.errorPath,
                $currErrSchemaPath = $errSchemaPath,
                $missingProperty = it.util.escapeQuotes($propertyKey);
              if (it.opts._errorDataPathProperty) {
                it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers);
              }
              $errSchemaPath = it.errSchemaPath + '/required';
              var $$outStack = $$outStack || [];
              $$outStack.push(out);
              out = ''; /* istanbul ignore else */
              if (it.createErrors !== false) {
                out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } ';
                if (it.opts.messages !== false) {
                  out += ' , message: \'';
                  if (it.opts._errorDataPathProperty) {
                    out += 'is a required property';
                  } else {
                    out += 'should have required property \\\'' + ($missingProperty) + '\\\'';
                  }
                  out += '\' ';
                }
                if (it.opts.verbose) {
                  out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
                }
                out += ' } ';
              } else {
                out += ' {} ';
              }
              var __err = out;
              out = $$outStack.pop();
              if (!it.compositeRule && $breakOnError) {
                /* istanbul ignore if */
                if (it.async) {
                  out += ' throw new ValidationError([' + (__err) + ']); ';
                } else {
                  out += ' validate.errors = [' + (__err) + ']; return false; ';
                }
              } else {
                out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
              }
              $errSchemaPath = $currErrSchemaPath;
              it.errorPath = $currentErrorPath;
              out += ' } else { ';
            } else {
              if ($breakOnError) {
                out += ' if ( ' + ($useData) + ' === undefined ';
                if ($ownProperties) {
                  out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
                }
                out += ') { ' + ($nextValid) + ' = true; } else { ';
              } else {
                out += ' if (' + ($useData) + ' !== undefined ';
                if ($ownProperties) {
                  out += ' &&   Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
                }
                out += ' ) { ';
              }
            }
            out += ' ' + ($code) + ' } ';
          }
        }
        if ($breakOnError) {
          out += ' if (' + ($nextValid) + ') { ';
          $closingBraces += '}';
        }
      }
    }
  }
  if ($pPropertyKeys.length) {
    var arr4 = $pPropertyKeys;
    if (arr4) {
      var $pProperty, i4 = -1,
        l4 = arr4.length - 1;
      while (i4 < l4) {
        $pProperty = arr4[i4 += 1];
        var $sch = $pProperties[$pProperty];
        if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) {
          $it.schema = $sch;
          $it.schemaPath = it.schemaPath + '.patternProperties' + it.util.getProperty($pProperty);
          $it.errSchemaPath = it.errSchemaPath + '/patternProperties/' + it.util.escapeFragment($pProperty);
          if ($ownProperties) {
            out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; ';
          } else {
            out += ' for (var ' + ($key) + ' in ' + ($data) + ') { ';
          }
          out += ' if (' + (it.usePattern($pProperty)) + '.test(' + ($key) + ')) { ';
          $it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers);
          var $passData = $data + '[' + $key + ']';
          $it.dataPathArr[$dataNxt] = $key;
          var $code = it.validate($it);
          $it.baseId = $currentBaseId;
          if (it.util.varOccurences($code, $nextData) < 2) {
            out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
          } else {
            out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
          }
          if ($breakOnError) {
            out += ' if (!' + ($nextValid) + ') break; ';
          }
          out += ' } ';
          if ($breakOnError) {
            out += ' else ' + ($nextValid) + ' = true; ';
          }
          out += ' }  ';
          if ($breakOnError) {
            out += ' if (' + ($nextValid) + ') { ';
            $closingBraces += '}';
          }
        }
      }
    }
  }
  if ($breakOnError) {
    out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {';
  }
  out = it.util.cleanUpCode(out);
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/propertyNames.js":
/*!*****************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/propertyNames.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_propertyNames(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $errs = 'errs__' + $lvl;
  var $it = it.util.copy(it);
  var $closingBraces = '';
  $it.level++;
  var $nextValid = 'valid' + $it.level;
  out += 'var ' + ($errs) + ' = errors;';
  if ((it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all))) {
    $it.schema = $schema;
    $it.schemaPath = $schemaPath;
    $it.errSchemaPath = $errSchemaPath;
    var $key = 'key' + $lvl,
      $idx = 'idx' + $lvl,
      $i = 'i' + $lvl,
      $invalidName = '\' + ' + $key + ' + \'',
      $dataNxt = $it.dataLevel = it.dataLevel + 1,
      $nextData = 'data' + $dataNxt,
      $dataProperties = 'dataProperties' + $lvl,
      $ownProperties = it.opts.ownProperties,
      $currentBaseId = it.baseId;
    if ($ownProperties) {
      out += ' var ' + ($dataProperties) + ' = undefined; ';
    }
    if ($ownProperties) {
      out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; ';
    } else {
      out += ' for (var ' + ($key) + ' in ' + ($data) + ') { ';
    }
    out += ' var startErrs' + ($lvl) + ' = errors; ';
    var $passData = $key;
    var $wasComposite = it.compositeRule;
    it.compositeRule = $it.compositeRule = true;
    var $code = it.validate($it);
    $it.baseId = $currentBaseId;
    if (it.util.varOccurences($code, $nextData) < 2) {
      out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' ';
    } else {
      out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' ';
    }
    it.compositeRule = $it.compositeRule = $wasComposite;
    out += ' if (!' + ($nextValid) + ') { for (var ' + ($i) + '=startErrs' + ($lvl) + '; ' + ($i) + '<errors; ' + ($i) + '++) { vErrors[' + ($i) + '].propertyName = ' + ($key) + '; }   var err =   '; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ('propertyNames') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { propertyName: \'' + ($invalidName) + '\' } ';
      if (it.opts.messages !== false) {
        out += ' , message: \'property name \\\'' + ($invalidName) + '\\\' is invalid\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError(vErrors); ';
      } else {
        out += ' validate.errors = vErrors; return false; ';
      }
    }
    if ($breakOnError) {
      out += ' break; ';
    }
    out += ' } }';
  }
  if ($breakOnError) {
    out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {';
  }
  out = it.util.cleanUpCode(out);
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/ref.js":
/*!*******************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/ref.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_ref(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $async, $refCode;
  if ($schema == '#' || $schema == '#/') {
    if (it.isRoot) {
      $async = it.async;
      $refCode = 'validate';
    } else {
      $async = it.root.schema.$async === true;
      $refCode = 'root.refVal[0]';
    }
  } else {
    var $refVal = it.resolveRef(it.baseId, $schema, it.isRoot);
    if ($refVal === undefined) {
      var $message = it.MissingRefError.message(it.baseId, $schema);
      if (it.opts.missingRefs == 'fail') {
        it.logger.error($message);
        var $$outStack = $$outStack || [];
        $$outStack.push(out);
        out = ''; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ('$ref') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { ref: \'' + (it.util.escapeQuotes($schema)) + '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'can\\\'t resolve reference ' + (it.util.escapeQuotes($schema)) + '\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: ' + (it.util.toQuotedString($schema)) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        var __err = out;
        out = $$outStack.pop();
        if (!it.compositeRule && $breakOnError) {
          /* istanbul ignore if */
          if (it.async) {
            out += ' throw new ValidationError([' + (__err) + ']); ';
          } else {
            out += ' validate.errors = [' + (__err) + ']; return false; ';
          }
        } else {
          out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
        }
        if ($breakOnError) {
          out += ' if (false) { ';
        }
      } else if (it.opts.missingRefs == 'ignore') {
        it.logger.warn($message);
        if ($breakOnError) {
          out += ' if (true) { ';
        }
      } else {
        throw new it.MissingRefError(it.baseId, $schema, $message);
      }
    } else if ($refVal.inline) {
      var $it = it.util.copy(it);
      $it.level++;
      var $nextValid = 'valid' + $it.level;
      $it.schema = $refVal.schema;
      $it.schemaPath = '';
      $it.errSchemaPath = $schema;
      var $code = it.validate($it).replace(/validate\.schema/g, $refVal.code);
      out += ' ' + ($code) + ' ';
      if ($breakOnError) {
        out += ' if (' + ($nextValid) + ') { ';
      }
    } else {
      $async = $refVal.$async === true || (it.async && $refVal.$async !== false);
      $refCode = $refVal.code;
    }
  }
  if ($refCode) {
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = '';
    if (it.opts.passContext) {
      out += ' ' + ($refCode) + '.call(this, ';
    } else {
      out += ' ' + ($refCode) + '( ';
    }
    out += ' ' + ($data) + ', (dataPath || \'\')';
    if (it.errorPath != '""') {
      out += ' + ' + (it.errorPath);
    }
    var $parentData = $dataLvl ? 'data' + (($dataLvl - 1) || '') : 'parentData',
      $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty';
    out += ' , ' + ($parentData) + ' , ' + ($parentDataProperty) + ', rootData)  ';
    var __callValidate = out;
    out = $$outStack.pop();
    if ($async) {
      if (!it.async) throw new Error('async schema referenced by sync schema');
      if ($breakOnError) {
        out += ' var ' + ($valid) + '; ';
      }
      out += ' try { await ' + (__callValidate) + '; ';
      if ($breakOnError) {
        out += ' ' + ($valid) + ' = true; ';
      }
      out += ' } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; ';
      if ($breakOnError) {
        out += ' ' + ($valid) + ' = false; ';
      }
      out += ' } ';
      if ($breakOnError) {
        out += ' if (' + ($valid) + ') { ';
      }
    } else {
      out += ' if (!' + (__callValidate) + ') { if (vErrors === null) vErrors = ' + ($refCode) + '.errors; else vErrors = vErrors.concat(' + ($refCode) + '.errors); errors = vErrors.length; } ';
      if ($breakOnError) {
        out += ' else { ';
      }
    }
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/required.js":
/*!************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/required.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_required(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  var $vSchema = 'schema' + $lvl;
  if (!$isData) {
    if ($schema.length < it.opts.loopRequired && it.schema.properties && Object.keys(it.schema.properties).length) {
      var $required = [];
      var arr1 = $schema;
      if (arr1) {
        var $property, i1 = -1,
          l1 = arr1.length - 1;
        while (i1 < l1) {
          $property = arr1[i1 += 1];
          var $propertySch = it.schema.properties[$property];
          if (!($propertySch && (it.opts.strictKeywords ? typeof $propertySch == 'object' && Object.keys($propertySch).length > 0 : it.util.schemaHasRules($propertySch, it.RULES.all)))) {
            $required[$required.length] = $property;
          }
        }
      }
    } else {
      var $required = $schema;
    }
  }
  if ($isData || $required.length) {
    var $currentErrorPath = it.errorPath,
      $loopRequired = $isData || $required.length >= it.opts.loopRequired,
      $ownProperties = it.opts.ownProperties;
    if ($breakOnError) {
      out += ' var missing' + ($lvl) + '; ';
      if ($loopRequired) {
        if (!$isData) {
          out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + '; ';
        }
        var $i = 'i' + $lvl,
          $propertyPath = 'schema' + $lvl + '[' + $i + ']',
          $missingProperty = '\' + ' + $propertyPath + ' + \'';
        if (it.opts._errorDataPathProperty) {
          it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers);
        }
        out += ' var ' + ($valid) + ' = true; ';
        if ($isData) {
          out += ' if (schema' + ($lvl) + ' === undefined) ' + ($valid) + ' = true; else if (!Array.isArray(schema' + ($lvl) + ')) ' + ($valid) + ' = false; else {';
        }
        out += ' for (var ' + ($i) + ' = 0; ' + ($i) + ' < ' + ($vSchema) + '.length; ' + ($i) + '++) { ' + ($valid) + ' = ' + ($data) + '[' + ($vSchema) + '[' + ($i) + ']] !== undefined ';
        if ($ownProperties) {
          out += ' &&   Object.prototype.hasOwnProperty.call(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + ']) ';
        }
        out += '; if (!' + ($valid) + ') break; } ';
        if ($isData) {
          out += '  }  ';
        }
        out += '  if (!' + ($valid) + ') {   ';
        var $$outStack = $$outStack || [];
        $$outStack.push(out);
        out = ''; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'';
            if (it.opts._errorDataPathProperty) {
              out += 'is a required property';
            } else {
              out += 'should have required property \\\'' + ($missingProperty) + '\\\'';
            }
            out += '\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        var __err = out;
        out = $$outStack.pop();
        if (!it.compositeRule && $breakOnError) {
          /* istanbul ignore if */
          if (it.async) {
            out += ' throw new ValidationError([' + (__err) + ']); ';
          } else {
            out += ' validate.errors = [' + (__err) + ']; return false; ';
          }
        } else {
          out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
        }
        out += ' } else { ';
      } else {
        out += ' if ( ';
        var arr2 = $required;
        if (arr2) {
          var $propertyKey, $i = -1,
            l2 = arr2.length - 1;
          while ($i < l2) {
            $propertyKey = arr2[$i += 1];
            if ($i) {
              out += ' || ';
            }
            var $prop = it.util.getProperty($propertyKey),
              $useData = $data + $prop;
            out += ' ( ( ' + ($useData) + ' === undefined ';
            if ($ownProperties) {
              out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
            }
            out += ') && (missing' + ($lvl) + ' = ' + (it.util.toQuotedString(it.opts.jsonPointers ? $propertyKey : $prop)) + ') ) ';
          }
        }
        out += ') {  ';
        var $propertyPath = 'missing' + $lvl,
          $missingProperty = '\' + ' + $propertyPath + ' + \'';
        if (it.opts._errorDataPathProperty) {
          it.errorPath = it.opts.jsonPointers ? it.util.getPathExpr($currentErrorPath, $propertyPath, true) : $currentErrorPath + ' + ' + $propertyPath;
        }
        var $$outStack = $$outStack || [];
        $$outStack.push(out);
        out = ''; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'';
            if (it.opts._errorDataPathProperty) {
              out += 'is a required property';
            } else {
              out += 'should have required property \\\'' + ($missingProperty) + '\\\'';
            }
            out += '\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        var __err = out;
        out = $$outStack.pop();
        if (!it.compositeRule && $breakOnError) {
          /* istanbul ignore if */
          if (it.async) {
            out += ' throw new ValidationError([' + (__err) + ']); ';
          } else {
            out += ' validate.errors = [' + (__err) + ']; return false; ';
          }
        } else {
          out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
        }
        out += ' } else { ';
      }
    } else {
      if ($loopRequired) {
        if (!$isData) {
          out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + '; ';
        }
        var $i = 'i' + $lvl,
          $propertyPath = 'schema' + $lvl + '[' + $i + ']',
          $missingProperty = '\' + ' + $propertyPath + ' + \'';
        if (it.opts._errorDataPathProperty) {
          it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers);
        }
        if ($isData) {
          out += ' if (' + ($vSchema) + ' && !Array.isArray(' + ($vSchema) + ')) {  var err =   '; /* istanbul ignore else */
          if (it.createErrors !== false) {
            out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } ';
            if (it.opts.messages !== false) {
              out += ' , message: \'';
              if (it.opts._errorDataPathProperty) {
                out += 'is a required property';
              } else {
                out += 'should have required property \\\'' + ($missingProperty) + '\\\'';
              }
              out += '\' ';
            }
            if (it.opts.verbose) {
              out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
            }
            out += ' } ';
          } else {
            out += ' {} ';
          }
          out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else if (' + ($vSchema) + ' !== undefined) { ';
        }
        out += ' for (var ' + ($i) + ' = 0; ' + ($i) + ' < ' + ($vSchema) + '.length; ' + ($i) + '++) { if (' + ($data) + '[' + ($vSchema) + '[' + ($i) + ']] === undefined ';
        if ($ownProperties) {
          out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + ']) ';
        }
        out += ') {  var err =   '; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'';
            if (it.opts._errorDataPathProperty) {
              out += 'is a required property';
            } else {
              out += 'should have required property \\\'' + ($missingProperty) + '\\\'';
            }
            out += '\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } ';
        if ($isData) {
          out += '  }  ';
        }
      } else {
        var arr3 = $required;
        if (arr3) {
          var $propertyKey, i3 = -1,
            l3 = arr3.length - 1;
          while (i3 < l3) {
            $propertyKey = arr3[i3 += 1];
            var $prop = it.util.getProperty($propertyKey),
              $missingProperty = it.util.escapeQuotes($propertyKey),
              $useData = $data + $prop;
            if (it.opts._errorDataPathProperty) {
              it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers);
            }
            out += ' if ( ' + ($useData) + ' === undefined ';
            if ($ownProperties) {
              out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') ';
            }
            out += ') {  var err =   '; /* istanbul ignore else */
            if (it.createErrors !== false) {
              out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } ';
              if (it.opts.messages !== false) {
                out += ' , message: \'';
                if (it.opts._errorDataPathProperty) {
                  out += 'is a required property';
                } else {
                  out += 'should have required property \\\'' + ($missingProperty) + '\\\'';
                }
                out += '\' ';
              }
              if (it.opts.verbose) {
                out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
              }
              out += ' } ';
            } else {
              out += ' {} ';
            }
            out += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } ';
          }
        }
      }
    }
    it.errorPath = $currentErrorPath;
  } else if ($breakOnError) {
    out += ' if (true) {';
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/uniqueItems.js":
/*!***************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/uniqueItems.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_uniqueItems(it, $keyword, $ruleType) {
  var out = ' ';
  var $lvl = it.level;
  var $dataLvl = it.dataLevel;
  var $schema = it.schema[$keyword];
  var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
  var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
  var $breakOnError = !it.opts.allErrors;
  var $data = 'data' + ($dataLvl || '');
  var $valid = 'valid' + $lvl;
  var $isData = it.opts.$data && $schema && $schema.$data,
    $schemaValue;
  if ($isData) {
    out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; ';
    $schemaValue = 'schema' + $lvl;
  } else {
    $schemaValue = $schema;
  }
  if (($schema || $isData) && it.opts.uniqueItems !== false) {
    if ($isData) {
      out += ' var ' + ($valid) + '; if (' + ($schemaValue) + ' === false || ' + ($schemaValue) + ' === undefined) ' + ($valid) + ' = true; else if (typeof ' + ($schemaValue) + ' != \'boolean\') ' + ($valid) + ' = false; else { ';
    }
    out += ' var i = ' + ($data) + '.length , ' + ($valid) + ' = true , j; if (i > 1) { ';
    var $itemType = it.schema.items && it.schema.items.type,
      $typeIsArray = Array.isArray($itemType);
    if (!$itemType || $itemType == 'object' || $itemType == 'array' || ($typeIsArray && ($itemType.indexOf('object') >= 0 || $itemType.indexOf('array') >= 0))) {
      out += ' outer: for (;i--;) { for (j = i; j--;) { if (equal(' + ($data) + '[i], ' + ($data) + '[j])) { ' + ($valid) + ' = false; break outer; } } } ';
    } else {
      out += ' var itemIndices = {}, item; for (;i--;) { var item = ' + ($data) + '[i]; ';
      var $method = 'checkDataType' + ($typeIsArray ? 's' : '');
      out += ' if (' + (it.util[$method]($itemType, 'item', true)) + ') continue; ';
      if ($typeIsArray) {
        out += ' if (typeof item == \'string\') item = \'"\' + item; ';
      }
      out += ' if (typeof itemIndices[item] == \'number\') { ' + ($valid) + ' = false; j = itemIndices[item]; break; } itemIndices[item] = i; } ';
    }
    out += ' } ';
    if ($isData) {
      out += '  }  ';
    }
    out += ' if (!' + ($valid) + ') {   ';
    var $$outStack = $$outStack || [];
    $$outStack.push(out);
    out = ''; /* istanbul ignore else */
    if (it.createErrors !== false) {
      out += ' { keyword: \'' + ('uniqueItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { i: i, j: j } ';
      if (it.opts.messages !== false) {
        out += ' , message: \'should NOT have duplicate items (items ## \' + j + \' and \' + i + \' are identical)\' ';
      }
      if (it.opts.verbose) {
        out += ' , schema:  ';
        if ($isData) {
          out += 'validate.schema' + ($schemaPath);
        } else {
          out += '' + ($schema);
        }
        out += '         , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
      }
      out += ' } ';
    } else {
      out += ' {} ';
    }
    var __err = out;
    out = $$outStack.pop();
    if (!it.compositeRule && $breakOnError) {
      /* istanbul ignore if */
      if (it.async) {
        out += ' throw new ValidationError([' + (__err) + ']); ';
      } else {
        out += ' validate.errors = [' + (__err) + ']; return false; ';
      }
    } else {
      out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
    }
    out += ' } ';
    if ($breakOnError) {
      out += ' else { ';
    }
  } else {
    if ($breakOnError) {
      out += ' if (true) { ';
    }
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/dotjs/validate.js":
/*!************************************************!*\
  !*** ./node_modules/ajv/lib/dotjs/validate.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = function generate_validate(it, $keyword, $ruleType) {
  var out = '';
  var $async = it.schema.$async === true,
    $refKeywords = it.util.schemaHasRulesExcept(it.schema, it.RULES.all, '$ref'),
    $id = it.self._getId(it.schema);
  if (it.opts.strictKeywords) {
    var $unknownKwd = it.util.schemaUnknownRules(it.schema, it.RULES.keywords);
    if ($unknownKwd) {
      var $keywordsMsg = 'unknown keyword: ' + $unknownKwd;
      if (it.opts.strictKeywords === 'log') it.logger.warn($keywordsMsg);
      else throw new Error($keywordsMsg);
    }
  }
  if (it.isTop) {
    out += ' var validate = ';
    if ($async) {
      it.async = true;
      out += 'async ';
    }
    out += 'function(data, dataPath, parentData, parentDataProperty, rootData) { \'use strict\'; ';
    if ($id && (it.opts.sourceCode || it.opts.processCode)) {
      out += ' ' + ('/\*# sourceURL=' + $id + ' */') + ' ';
    }
  }
  if (typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref)) {
    var $keyword = 'false schema';
    var $lvl = it.level;
    var $dataLvl = it.dataLevel;
    var $schema = it.schema[$keyword];
    var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
    var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
    var $breakOnError = !it.opts.allErrors;
    var $errorKeyword;
    var $data = 'data' + ($dataLvl || '');
    var $valid = 'valid' + $lvl;
    if (it.schema === false) {
      if (it.isTop) {
        $breakOnError = true;
      } else {
        out += ' var ' + ($valid) + ' = false; ';
      }
      var $$outStack = $$outStack || [];
      $$outStack.push(out);
      out = ''; /* istanbul ignore else */
      if (it.createErrors !== false) {
        out += ' { keyword: \'' + ($errorKeyword || 'false schema') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
        if (it.opts.messages !== false) {
          out += ' , message: \'boolean schema is false\' ';
        }
        if (it.opts.verbose) {
          out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
        }
        out += ' } ';
      } else {
        out += ' {} ';
      }
      var __err = out;
      out = $$outStack.pop();
      if (!it.compositeRule && $breakOnError) {
        /* istanbul ignore if */
        if (it.async) {
          out += ' throw new ValidationError([' + (__err) + ']); ';
        } else {
          out += ' validate.errors = [' + (__err) + ']; return false; ';
        }
      } else {
        out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
      }
    } else {
      if (it.isTop) {
        if ($async) {
          out += ' return data; ';
        } else {
          out += ' validate.errors = null; return true; ';
        }
      } else {
        out += ' var ' + ($valid) + ' = true; ';
      }
    }
    if (it.isTop) {
      out += ' }; return validate; ';
    }
    return out;
  }
  if (it.isTop) {
    var $top = it.isTop,
      $lvl = it.level = 0,
      $dataLvl = it.dataLevel = 0,
      $data = 'data';
    it.rootId = it.resolve.fullPath(it.self._getId(it.root.schema));
    it.baseId = it.baseId || it.rootId;
    delete it.isTop;
    it.dataPathArr = [undefined];
    if (it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults) {
      var $defaultMsg = 'default is ignored in the schema root';
      if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg);
      else throw new Error($defaultMsg);
    }
    out += ' var vErrors = null; ';
    out += ' var errors = 0;     ';
    out += ' if (rootData === undefined) rootData = data; ';
  } else {
    var $lvl = it.level,
      $dataLvl = it.dataLevel,
      $data = 'data' + ($dataLvl || '');
    if ($id) it.baseId = it.resolve.url(it.baseId, $id);
    if ($async && !it.async) throw new Error('async schema in sync schema');
    out += ' var errs_' + ($lvl) + ' = errors;';
  }
  var $valid = 'valid' + $lvl,
    $breakOnError = !it.opts.allErrors,
    $closingBraces1 = '',
    $closingBraces2 = '';
  var $errorKeyword;
  var $typeSchema = it.schema.type,
    $typeIsArray = Array.isArray($typeSchema);
  if ($typeSchema && it.opts.nullable && it.schema.nullable === true) {
    if ($typeIsArray) {
      if ($typeSchema.indexOf('null') == -1) $typeSchema = $typeSchema.concat('null');
    } else if ($typeSchema != 'null') {
      $typeSchema = [$typeSchema, 'null'];
      $typeIsArray = true;
    }
  }
  if ($typeIsArray && $typeSchema.length == 1) {
    $typeSchema = $typeSchema[0];
    $typeIsArray = false;
  }
  if (it.schema.$ref && $refKeywords) {
    if (it.opts.extendRefs == 'fail') {
      throw new Error('$ref: validation keywords used in schema at path "' + it.errSchemaPath + '" (see option extendRefs)');
    } else if (it.opts.extendRefs !== true) {
      $refKeywords = false;
      it.logger.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"');
    }
  }
  if (it.schema.$comment && it.opts.$comment) {
    out += ' ' + (it.RULES.all.$comment.code(it, '$comment'));
  }
  if ($typeSchema) {
    if (it.opts.coerceTypes) {
      var $coerceToTypes = it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema);
    }
    var $rulesGroup = it.RULES.types[$typeSchema];
    if ($coerceToTypes || $typeIsArray || $rulesGroup === true || ($rulesGroup && !$shouldUseGroup($rulesGroup))) {
      var $schemaPath = it.schemaPath + '.type',
        $errSchemaPath = it.errSchemaPath + '/type';
      var $schemaPath = it.schemaPath + '.type',
        $errSchemaPath = it.errSchemaPath + '/type',
        $method = $typeIsArray ? 'checkDataTypes' : 'checkDataType';
      out += ' if (' + (it.util[$method]($typeSchema, $data, true)) + ') { ';
      if ($coerceToTypes) {
        var $dataType = 'dataType' + $lvl,
          $coerced = 'coerced' + $lvl;
        out += ' var ' + ($dataType) + ' = typeof ' + ($data) + '; ';
        if (it.opts.coerceTypes == 'array') {
          out += ' if (' + ($dataType) + ' == \'object\' && Array.isArray(' + ($data) + ')) ' + ($dataType) + ' = \'array\'; ';
        }
        out += ' var ' + ($coerced) + ' = undefined; ';
        var $bracesCoercion = '';
        var arr1 = $coerceToTypes;
        if (arr1) {
          var $type, $i = -1,
            l1 = arr1.length - 1;
          while ($i < l1) {
            $type = arr1[$i += 1];
            if ($i) {
              out += ' if (' + ($coerced) + ' === undefined) { ';
              $bracesCoercion += '}';
            }
            if (it.opts.coerceTypes == 'array' && $type != 'array') {
              out += ' if (' + ($dataType) + ' == \'array\' && ' + ($data) + '.length == 1) { ' + ($coerced) + ' = ' + ($data) + ' = ' + ($data) + '[0]; ' + ($dataType) + ' = typeof ' + ($data) + ';  } ';
            }
            if ($type == 'string') {
              out += ' if (' + ($dataType) + ' == \'number\' || ' + ($dataType) + ' == \'boolean\') ' + ($coerced) + ' = \'\' + ' + ($data) + '; else if (' + ($data) + ' === null) ' + ($coerced) + ' = \'\'; ';
            } else if ($type == 'number' || $type == 'integer') {
              out += ' if (' + ($dataType) + ' == \'boolean\' || ' + ($data) + ' === null || (' + ($dataType) + ' == \'string\' && ' + ($data) + ' && ' + ($data) + ' == +' + ($data) + ' ';
              if ($type == 'integer') {
                out += ' && !(' + ($data) + ' % 1)';
              }
              out += ')) ' + ($coerced) + ' = +' + ($data) + '; ';
            } else if ($type == 'boolean') {
              out += ' if (' + ($data) + ' === \'false\' || ' + ($data) + ' === 0 || ' + ($data) + ' === null) ' + ($coerced) + ' = false; else if (' + ($data) + ' === \'true\' || ' + ($data) + ' === 1) ' + ($coerced) + ' = true; ';
            } else if ($type == 'null') {
              out += ' if (' + ($data) + ' === \'\' || ' + ($data) + ' === 0 || ' + ($data) + ' === false) ' + ($coerced) + ' = null; ';
            } else if (it.opts.coerceTypes == 'array' && $type == 'array') {
              out += ' if (' + ($dataType) + ' == \'string\' || ' + ($dataType) + ' == \'number\' || ' + ($dataType) + ' == \'boolean\' || ' + ($data) + ' == null) ' + ($coerced) + ' = [' + ($data) + ']; ';
            }
          }
        }
        out += ' ' + ($bracesCoercion) + ' if (' + ($coerced) + ' === undefined) {   ';
        var $$outStack = $$outStack || [];
        $$outStack.push(out);
        out = ''; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ($errorKeyword || 'type') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { type: \'';
          if ($typeIsArray) {
            out += '' + ($typeSchema.join(","));
          } else {
            out += '' + ($typeSchema);
          }
          out += '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'should be ';
            if ($typeIsArray) {
              out += '' + ($typeSchema.join(","));
            } else {
              out += '' + ($typeSchema);
            }
            out += '\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        var __err = out;
        out = $$outStack.pop();
        if (!it.compositeRule && $breakOnError) {
          /* istanbul ignore if */
          if (it.async) {
            out += ' throw new ValidationError([' + (__err) + ']); ';
          } else {
            out += ' validate.errors = [' + (__err) + ']; return false; ';
          }
        } else {
          out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
        }
        out += ' } else {  ';
        var $parentData = $dataLvl ? 'data' + (($dataLvl - 1) || '') : 'parentData',
          $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty';
        out += ' ' + ($data) + ' = ' + ($coerced) + '; ';
        if (!$dataLvl) {
          out += 'if (' + ($parentData) + ' !== undefined)';
        }
        out += ' ' + ($parentData) + '[' + ($parentDataProperty) + '] = ' + ($coerced) + '; } ';
      } else {
        var $$outStack = $$outStack || [];
        $$outStack.push(out);
        out = ''; /* istanbul ignore else */
        if (it.createErrors !== false) {
          out += ' { keyword: \'' + ($errorKeyword || 'type') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { type: \'';
          if ($typeIsArray) {
            out += '' + ($typeSchema.join(","));
          } else {
            out += '' + ($typeSchema);
          }
          out += '\' } ';
          if (it.opts.messages !== false) {
            out += ' , message: \'should be ';
            if ($typeIsArray) {
              out += '' + ($typeSchema.join(","));
            } else {
              out += '' + ($typeSchema);
            }
            out += '\' ';
          }
          if (it.opts.verbose) {
            out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
          }
          out += ' } ';
        } else {
          out += ' {} ';
        }
        var __err = out;
        out = $$outStack.pop();
        if (!it.compositeRule && $breakOnError) {
          /* istanbul ignore if */
          if (it.async) {
            out += ' throw new ValidationError([' + (__err) + ']); ';
          } else {
            out += ' validate.errors = [' + (__err) + ']; return false; ';
          }
        } else {
          out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
        }
      }
      out += ' } ';
    }
  }
  if (it.schema.$ref && !$refKeywords) {
    out += ' ' + (it.RULES.all.$ref.code(it, '$ref')) + ' ';
    if ($breakOnError) {
      out += ' } if (errors === ';
      if ($top) {
        out += '0';
      } else {
        out += 'errs_' + ($lvl);
      }
      out += ') { ';
      $closingBraces2 += '}';
    }
  } else {
    var arr2 = it.RULES;
    if (arr2) {
      var $rulesGroup, i2 = -1,
        l2 = arr2.length - 1;
      while (i2 < l2) {
        $rulesGroup = arr2[i2 += 1];
        if ($shouldUseGroup($rulesGroup)) {
          if ($rulesGroup.type) {
            out += ' if (' + (it.util.checkDataType($rulesGroup.type, $data)) + ') { ';
          }
          if (it.opts.useDefaults) {
            if ($rulesGroup.type == 'object' && it.schema.properties) {
              var $schema = it.schema.properties,
                $schemaKeys = Object.keys($schema);
              var arr3 = $schemaKeys;
              if (arr3) {
                var $propertyKey, i3 = -1,
                  l3 = arr3.length - 1;
                while (i3 < l3) {
                  $propertyKey = arr3[i3 += 1];
                  var $sch = $schema[$propertyKey];
                  if ($sch.default !== undefined) {
                    var $passData = $data + it.util.getProperty($propertyKey);
                    if (it.compositeRule) {
                      if (it.opts.strictDefaults) {
                        var $defaultMsg = 'default is ignored for: ' + $passData;
                        if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg);
                        else throw new Error($defaultMsg);
                      }
                    } else {
                      out += ' if (' + ($passData) + ' === undefined ';
                      if (it.opts.useDefaults == 'empty') {
                        out += ' || ' + ($passData) + ' === null || ' + ($passData) + ' === \'\' ';
                      }
                      out += ' ) ' + ($passData) + ' = ';
                      if (it.opts.useDefaults == 'shared') {
                        out += ' ' + (it.useDefault($sch.default)) + ' ';
                      } else {
                        out += ' ' + (JSON.stringify($sch.default)) + ' ';
                      }
                      out += '; ';
                    }
                  }
                }
              }
            } else if ($rulesGroup.type == 'array' && Array.isArray(it.schema.items)) {
              var arr4 = it.schema.items;
              if (arr4) {
                var $sch, $i = -1,
                  l4 = arr4.length - 1;
                while ($i < l4) {
                  $sch = arr4[$i += 1];
                  if ($sch.default !== undefined) {
                    var $passData = $data + '[' + $i + ']';
                    if (it.compositeRule) {
                      if (it.opts.strictDefaults) {
                        var $defaultMsg = 'default is ignored for: ' + $passData;
                        if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg);
                        else throw new Error($defaultMsg);
                      }
                    } else {
                      out += ' if (' + ($passData) + ' === undefined ';
                      if (it.opts.useDefaults == 'empty') {
                        out += ' || ' + ($passData) + ' === null || ' + ($passData) + ' === \'\' ';
                      }
                      out += ' ) ' + ($passData) + ' = ';
                      if (it.opts.useDefaults == 'shared') {
                        out += ' ' + (it.useDefault($sch.default)) + ' ';
                      } else {
                        out += ' ' + (JSON.stringify($sch.default)) + ' ';
                      }
                      out += '; ';
                    }
                  }
                }
              }
            }
          }
          var arr5 = $rulesGroup.rules;
          if (arr5) {
            var $rule, i5 = -1,
              l5 = arr5.length - 1;
            while (i5 < l5) {
              $rule = arr5[i5 += 1];
              if ($shouldUseRule($rule)) {
                var $code = $rule.code(it, $rule.keyword, $rulesGroup.type);
                if ($code) {
                  out += ' ' + ($code) + ' ';
                  if ($breakOnError) {
                    $closingBraces1 += '}';
                  }
                }
              }
            }
          }
          if ($breakOnError) {
            out += ' ' + ($closingBraces1) + ' ';
            $closingBraces1 = '';
          }
          if ($rulesGroup.type) {
            out += ' } ';
            if ($typeSchema && $typeSchema === $rulesGroup.type && !$coerceToTypes) {
              out += ' else { ';
              var $schemaPath = it.schemaPath + '.type',
                $errSchemaPath = it.errSchemaPath + '/type';
              var $$outStack = $$outStack || [];
              $$outStack.push(out);
              out = ''; /* istanbul ignore else */
              if (it.createErrors !== false) {
                out += ' { keyword: \'' + ($errorKeyword || 'type') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { type: \'';
                if ($typeIsArray) {
                  out += '' + ($typeSchema.join(","));
                } else {
                  out += '' + ($typeSchema);
                }
                out += '\' } ';
                if (it.opts.messages !== false) {
                  out += ' , message: \'should be ';
                  if ($typeIsArray) {
                    out += '' + ($typeSchema.join(","));
                  } else {
                    out += '' + ($typeSchema);
                  }
                  out += '\' ';
                }
                if (it.opts.verbose) {
                  out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
                }
                out += ' } ';
              } else {
                out += ' {} ';
              }
              var __err = out;
              out = $$outStack.pop();
              if (!it.compositeRule && $breakOnError) {
                /* istanbul ignore if */
                if (it.async) {
                  out += ' throw new ValidationError([' + (__err) + ']); ';
                } else {
                  out += ' validate.errors = [' + (__err) + ']; return false; ';
                }
              } else {
                out += ' var err = ' + (__err) + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
              }
              out += ' } ';
            }
          }
          if ($breakOnError) {
            out += ' if (errors === ';
            if ($top) {
              out += '0';
            } else {
              out += 'errs_' + ($lvl);
            }
            out += ') { ';
            $closingBraces2 += '}';
          }
        }
      }
    }
  }
  if ($breakOnError) {
    out += ' ' + ($closingBraces2) + ' ';
  }
  if ($top) {
    if ($async) {
      out += ' if (errors === 0) return data;           ';
      out += ' else throw new ValidationError(vErrors); ';
    } else {
      out += ' validate.errors = vErrors; ';
      out += ' return errors === 0;       ';
    }
    out += ' }; return validate;';
  } else {
    out += ' var ' + ($valid) + ' = errors === errs_' + ($lvl) + ';';
  }
  out = it.util.cleanUpCode(out);
  if ($top) {
    out = it.util.finalCleanUpCode(out, $async);
  }

  function $shouldUseGroup($rulesGroup) {
    var rules = $rulesGroup.rules;
    for (var i = 0; i < rules.length; i++)
      if ($shouldUseRule(rules[i])) return true;
  }

  function $shouldUseRule($rule) {
    return it.schema[$rule.keyword] !== undefined || ($rule.implements && $ruleImplementsSomeKeyword($rule));
  }

  function $ruleImplementsSomeKeyword($rule) {
    var impl = $rule.implements;
    for (var i = 0; i < impl.length; i++)
      if (it.schema[impl[i]] !== undefined) return true;
  }
  return out;
}


/***/ }),

/***/ "./node_modules/ajv/lib/keyword.js":
/*!*****************************************!*\
  !*** ./node_modules/ajv/lib/keyword.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var IDENTIFIER = /^[a-z_$][a-z0-9_$-]*$/i;
var customRuleCode = __webpack_require__(/*! ./dotjs/custom */ "./node_modules/ajv/lib/dotjs/custom.js");
var definitionSchema = __webpack_require__(/*! ./definition_schema */ "./node_modules/ajv/lib/definition_schema.js");

module.exports = {
  add: addKeyword,
  get: getKeyword,
  remove: removeKeyword,
  validate: validateKeyword
};


/**
 * Define custom keyword
 * @this  Ajv
 * @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords).
 * @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
 * @return {Ajv} this for method chaining
 */
function addKeyword(keyword, definition) {
  /* jshint validthis: true */
  /* eslint no-shadow: 0 */
  var RULES = this.RULES;
  if (RULES.keywords[keyword])
    throw new Error('Keyword ' + keyword + ' is already defined');

  if (!IDENTIFIER.test(keyword))
    throw new Error('Keyword ' + keyword + ' is not a valid identifier');

  if (definition) {
    this.validateKeyword(definition, true);

    var dataType = definition.type;
    if (Array.isArray(dataType)) {
      for (var i=0; i<dataType.length; i++)
        _addRule(keyword, dataType[i], definition);
    } else {
      _addRule(keyword, dataType, definition);
    }

    var metaSchema = definition.metaSchema;
    if (metaSchema) {
      if (definition.$data && this._opts.$data) {
        metaSchema = {
          anyOf: [
            metaSchema,
            { '$ref': 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#' }
          ]
        };
      }
      definition.validateSchema = this.compile(metaSchema, true);
    }
  }

  RULES.keywords[keyword] = RULES.all[keyword] = true;


  function _addRule(keyword, dataType, definition) {
    var ruleGroup;
    for (var i=0; i<RULES.length; i++) {
      var rg = RULES[i];
      if (rg.type == dataType) {
        ruleGroup = rg;
        break;
      }
    }

    if (!ruleGroup) {
      ruleGroup = { type: dataType, rules: [] };
      RULES.push(ruleGroup);
    }

    var rule = {
      keyword: keyword,
      definition: definition,
      custom: true,
      code: customRuleCode,
      implements: definition.implements
    };
    ruleGroup.rules.push(rule);
    RULES.custom[keyword] = rule;
  }

  return this;
}


/**
 * Get keyword
 * @this  Ajv
 * @param {String} keyword pre-defined or custom keyword.
 * @return {Object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise.
 */
function getKeyword(keyword) {
  /* jshint validthis: true */
  var rule = this.RULES.custom[keyword];
  return rule ? rule.definition : this.RULES.keywords[keyword] || false;
}


/**
 * Remove keyword
 * @this  Ajv
 * @param {String} keyword pre-defined or custom keyword.
 * @return {Ajv} this for method chaining
 */
function removeKeyword(keyword) {
  /* jshint validthis: true */
  var RULES = this.RULES;
  delete RULES.keywords[keyword];
  delete RULES.all[keyword];
  delete RULES.custom[keyword];
  for (var i=0; i<RULES.length; i++) {
    var rules = RULES[i].rules;
    for (var j=0; j<rules.length; j++) {
      if (rules[j].keyword == keyword) {
        rules.splice(j, 1);
        break;
      }
    }
  }
  return this;
}


/**
 * Validate keyword definition
 * @this  Ajv
 * @param {Object} definition keyword definition object.
 * @param {Boolean} throwError true to throw exception if definition is invalid
 * @return {boolean} validation result
 */
function validateKeyword(definition, throwError) {
  validateKeyword.errors = null;
  var v = this._validateKeyword = this._validateKeyword
                                  || this.compile(definitionSchema, true);

  if (v(definition)) return true;
  validateKeyword.errors = v.errors;
  if (throwError)
    throw new Error('custom keyword definition is invalid: '  + this.errorsText(v.errors));
  else
    return false;
}


/***/ }),

/***/ "./node_modules/ajv/lib/refs/data.json":
/*!*********************************************!*\
  !*** ./node_modules/ajv/lib/refs/data.json ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQCQM4GMAWBTAtgQygLmkgLvgA7YD0pAVnAPYB2AtIqpgHTUBOA5qQCbsYAzfPQAMAdlJN0GAMRQANOEiwAljxx5CROGVL8A7i04r8SAK4AjM3BTsEdfClr4W9tKRRFqF2ygBeANYqtKQYFABupJhwjuykADYqFnooAnC8GPgYLFR0coqQ0DwoiOwqRPgqdBpQALIoWYzI0mACHGAwPJkYYOypvrQIKGAAFABSAMoA8gByYBPNmGAoAB6OtHBVtGBE7NRecBjxAJQKShBQ+ACeRCg13hQoCPhnhVB9AI5mKn3quADa0E63SgYAAugVlLt9rZKiUNKBCm9gVkEeckW9rrcajEyrROK8MUioBhaFcpgINP90USkcBoG12JgXrh3ih4pkVOEUPRcgwvMFYqCAL7yMA02mQelQRnMmp8+gC5y2EUSjFgtVgYXo0Xokk8HgmLZHAAKe1u7DhcA0AiONhA2qAA"));
    })()

/***/ }),

/***/ "./node_modules/ajv/lib/refs/json-schema-draft-07.json":
/*!*************************************************************!*\
  !*** ./node_modules/ajv/lib/refs/json-schema-draft-07.json ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQCQM4GMAWBTAtgQygLmkgLvgA7YD0pAVnAPYB2AtIqpgHTUBOA5qQCbsYAzfPQAMAdlJN0GAMRQANOEiwAljxx5CJclTqNk0tl178hoiVMxzFkaPhX4ANig1QAwhxRhLGMGhT4GPrMWDbKPCgCKrQOKnRwGqC2tlA+AILs-ACeiUrJyVD4WUQuuFAYmRg5YfkpaNEAkvjoCbgAjDW1EFAOLYnQMOyRrnJgAL55yWOdKbR0AHIonBj2AG4oDbTNnCjsuV0pRSWu0du7CpNdUPUxaACuaBoil5DTL91ztIvLaxtbS7sACKRDB3Rz4ET7A7dDCORwAeQEGgA2u8DsABkMkWUZLxItFYvFSJ9visVOtNmc9uMZtCwBioBEBKDwU9xmjbABdNFvK5wFRoIjOAAqxRQrXpHOUKFoDxRUvy5Uq1QVBQARtRqM4MLQLnSCqcAXtadCoLK4Xr9cpZWg1ecTQcoNQ1RQUAh8JardA4Ph2NFOFBVdyurzaqlff6MtkoaajqVoBVow6Ur00BKGXHXD6-bQAzTVVA7jEAI53DbNNMaX1l5PhEFgj24ZHB2oTWyh7qZptOl1uj3yaAarUoHVQTmdKBEdjUErsezimMFGBqRdXLveiO5z2OgQcTCN6B3P30LG7GUIFw8h2wHyrsPr8M5gO17q79j71xHlSBkPXwbDXAkjpQoxSzTdnwLN8PzKL8T0iM9aAvH9Wz-BBqDQfwtjvRUH2zf1kPyDsCnsJx4yA2NQLKPCtyvNFGXFBA-SIew6Gw4jKI3J8CKma8mRZA9qxQa8hgwHh4VoRwckAgsHyHbVdRfaA+IbDRmUcOAUFoq4UAADwwQVnHTGSOKVJMC1TCVBK0sN7nBFQhRQRE2MOEybTtY0C10hBHDufl1gAWQJe5HlwZ5fzozAdIFOVpP1EDjjKNzzmsxUvJ8vyUH8jAouC5zlAfJK9hSgobmikLJTigqHnc7j22vNLfPJTKgpiirgKq21kvCq5IoAGRlTh8CQfpYCxEY8SiGIWNoOBiQWJYyQpf4dmpIi6mifrcyGkb-2xaBcSZAlptmkkFt+SkjWBZkG0hfMriIFZmnYXVYvakzqIguKoJWVwhh2HTatea9RJ4QlaFhJo+kAzEAP2qA7rDCy8phWgsicptVVsBldpGeG1rpbGxpxCajriGbJAMTAoyqPGg0U+jrtZXArO6mzsshytodG2GoAO-EprJk75p+JqLpWvHrxuDn0xhvbeZJgWiVOkWlqpK7+Nu-HuiLFRS3LKG2oohLB01eTtyuZSmbANSNOKlI0K2DBohl7m5dGLXoEigAFadZ3nF2ceJw7FfJ5XFr+KkJYi6IfZnXZ-Z2on9oVsGha+M7ReWoF63BTXhJQUsVCGdQucD5Pg9TyRwOpnIEcVEGwdhWO-ZUBdS6T3mo4t-nU+RuwTOdV13XNsMG+mpvffj1uA47936ctg9gDbFC6KnOO52nvv4vjHsh-7Asx7Jif14T9uebngsF8SZfCOvB7CF2Whm6ntvDcdB9B77Ef654UHx8cZ+G9X6E3Pl3YCa8-ZZHmPpYB0BvoHigH9XSYDTRX0AjfHidEIglFoBERCm9XpGx3p-Ye9ND50GPi3YBmNlA6jRntVEXp8ggLdigphEAWHjQrsdKuT4a603YS2aEGC6qr0nnOKBMCZ6gLrgUB2PoqzsBrHRGUrVyLvxMomGm9MkbMyUUJAsUsKwSg6AWHWetpaKLLHbfKHF1FXDoejMAjCmGcKDj3Hh-IDIoFFCUBIsj9T2KYdvVwWiVTsJTMYxO58U6eIFA5XxC4AnBKMQbUxETlDmLLJYvR1j2EiNqEIzBVx4EjVwuBNh9s6DNC2P5FAoMMCJLKe9CpyTugOxqfgAAoohagoMtxc3KVxNp0AVB7WAK7XGHtCioBevSSZOIoDTJQOpMiCy4bTNhAicZssuEeMFhTEI-CRnlFRk4tx5d9lEnSMqSpyg6COR2es+W3CDk3OjCcuYi9dmLPZKIlIaCwBWQmEAA"));
    })()

/***/ }),

/***/ "./node_modules/circular-json/build/circular-json.node.js":
/*!****************************************************************!*\
  !*** ./node_modules/circular-json/build/circular-json.node.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/*!
Copyright (C) 2013-2017 by Andrea Giammarchi - @WebReflection

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/
var
  // should be a not so common char
  // possibly one JSON does not encode
  // possibly one encodeURIComponent does not encode
  // right now this char is '~' but this might change in the future
  specialChar = '~',
  safeSpecialChar = '\\x' + (
    '0' + specialChar.charCodeAt(0).toString(16)
  ).slice(-2),
  escapedSafeSpecialChar = '\\' + safeSpecialChar,
  specialCharRG = new RegExp(safeSpecialChar, 'g'),
  safeSpecialCharRG = new RegExp(escapedSafeSpecialChar, 'g'),

  safeStartWithSpecialCharRG = new RegExp('(?:^|([^\\\\]))' + escapedSafeSpecialChar),

  indexOf = [].indexOf || function(v){
    for(var i=this.length;i--&&this[i]!==v;);
    return i;
  },
  $String = String  // there's no way to drop warnings in JSHint
                    // about new String ... well, I need that here!
                    // faked, and happy linter!
;

function generateReplacer(value, replacer, resolve) {
  var
    doNotIgnore = false,
    inspect = !!replacer,
    path = [],
    all  = [value],
    seen = [value],
    mapp = [resolve ? specialChar : '[Circular]'],
    last = value,
    lvl  = 1,
    i, fn
  ;
  if (inspect) {
    fn = typeof replacer === 'object' ?
      function (key, value) {
        return key !== '' && replacer.indexOf(key) < 0 ? void 0 : value;
      } :
      replacer;
  }
  return function(key, value) {
    // the replacer has rights to decide
    // if a new object should be returned
    // or if there's some key to drop
    // let's call it here rather than "too late"
    if (inspect) value = fn.call(this, key, value);

    // first pass should be ignored, since it's just the initial object
    if (doNotIgnore) {
      if (last !== this) {
        i = lvl - indexOf.call(all, this) - 1;
        lvl -= i;
        all.splice(lvl, all.length);
        path.splice(lvl - 1, path.length);
        last = this;
      }
      // console.log(lvl, key, path);
      if (typeof value === 'object' && value) {
    	// if object isn't referring to parent object, add to the
        // object path stack. Otherwise it is already there.
        if (indexOf.call(all, value) < 0) {
          all.push(last = value);
        }
        lvl = all.length;
        i = indexOf.call(seen, value);
        if (i < 0) {
          i = seen.push(value) - 1;
          if (resolve) {
            // key cannot contain specialChar but could be not a string
            path.push(('' + key).replace(specialCharRG, safeSpecialChar));
            mapp[i] = specialChar + path.join(specialChar);
          } else {
            mapp[i] = mapp[0];
          }
        } else {
          value = mapp[i];
        }
      } else {
        if (typeof value === 'string' && resolve) {
          // ensure no special char involved on deserialization
          // in this case only first char is important
          // no need to replace all value (better performance)
          value = value .replace(safeSpecialChar, escapedSafeSpecialChar)
                        .replace(specialChar, safeSpecialChar);
        }
      }
    } else {
      doNotIgnore = true;
    }
    return value;
  };
}

function retrieveFromPath(current, keys) {
  for(var i = 0, length = keys.length; i < length; current = current[
    // keys should be normalized back here
    keys[i++].replace(safeSpecialCharRG, specialChar)
  ]);
  return current;
}

function generateReviver(reviver) {
  return function(key, value) {
    var isString = typeof value === 'string';
    if (isString && value.charAt(0) === specialChar) {
      return new $String(value.slice(1));
    }
    if (key === '') value = regenerate(value, value, {});
    // again, only one needed, do not use the RegExp for this replacement
    // only keys need the RegExp
    if (isString) value = value .replace(safeStartWithSpecialCharRG, '$1' + specialChar)
                                .replace(escapedSafeSpecialChar, safeSpecialChar);
    return reviver ? reviver.call(this, key, value) : value;
  };
}

function regenerateArray(root, current, retrieve) {
  for (var i = 0, length = current.length; i < length; i++) {
    current[i] = regenerate(root, current[i], retrieve);
  }
  return current;
}

function regenerateObject(root, current, retrieve) {
  for (var key in current) {
    if (current.hasOwnProperty(key)) {
      current[key] = regenerate(root, current[key], retrieve);
    }
  }
  return current;
}

function regenerate(root, current, retrieve) {
  return current instanceof Array ?
    // fast Array reconstruction
    regenerateArray(root, current, retrieve) :
    (
      current instanceof $String ?
        (
          // root is an empty string
          current.length ?
            (
              retrieve.hasOwnProperty(current) ?
                retrieve[current] :
                retrieve[current] = retrieveFromPath(
                  root, current.split(specialChar)
                )
            ) :
            root
        ) :
        (
          current instanceof Object ?
            // dedicated Object parser
            regenerateObject(root, current, retrieve) :
            // value as it is
            current
        )
    )
  ;
}

var CircularJSON = {
  stringify: function stringify(value, replacer, space, doNotResolve) {
    return CircularJSON.parser.stringify(
      value,
      generateReplacer(value, replacer, !doNotResolve),
      space
    );
  },
  parse: function parse(text, reviver) {
    return CircularJSON.parser.parse(
      text,
      generateReviver(reviver)
    );
  },
  // A parser should be an API 1:1 compatible with JSON
  // it should expose stringify and parse methods.
  // The default parser is the native JSON.
  parser: JSON
};

module.exports = CircularJSON;


/***/ }),

/***/ "./node_modules/clipboard-copy/index.js":
/*!**********************************************!*\
  !*** ./node_modules/clipboard-copy/index.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* global DOMException */

module.exports = clipboardCopy

function clipboardCopy (text) {
  // Use the Async Clipboard API when available. Requires a secure browing
  // context (i.e. HTTPS)
  if (navigator.clipboard) {
    return navigator.clipboard.writeText(text).catch(function (err) {
      throw (err !== undefined ? err : new DOMException('The request is not allowed', 'NotAllowedError'))
    })
  }

  // ...Otherwise, use document.execCommand() fallback

  // Put the text to copy into a <span>
  var span = document.createElement('span')
  span.textContent = text

  // Preserve consecutive spaces and newlines
  span.style.whiteSpace = 'pre'

  // Add the <span> to the page
  document.body.appendChild(span)

  // Make a selection object representing the range of text selected by the user
  var selection = window.getSelection()
  var range = window.document.createRange()
  selection.removeAllRanges()
  range.selectNode(span)
  selection.addRange(range)

  // Copy text to the clipboard
  var success = false
  try {
    success = window.document.execCommand('copy')
  } catch (err) {
    console.log('error', err)
  }

  // Cleanup
  selection.removeAllRanges()
  window.document.body.removeChild(span)

  return success
    ? Promise.resolve()
    : Promise.reject(new DOMException('The request is not allowed', 'NotAllowedError'))
}


/***/ }),

/***/ "./node_modules/crc/crc32.js":
/*!***********************************!*\
  !*** ./node_modules/crc/crc32.js ***!
  \***********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! buffer */ "./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js");
/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(buffer__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _create_buffer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./create_buffer */ "./node_modules/crc/create_buffer.js");
/* harmony import */ var _define_crc__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./define_crc */ "./node_modules/crc/define_crc.js");




// Generated by `./pycrc.py --algorithm=table-driven --model=crc-32 --generate=c`
// prettier-ignore
let TABLE = [
  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
  0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
  0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
  0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
  0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
  0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
  0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
  0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
  0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
  0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
  0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
  0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
  0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
  0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
  0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
  0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
  0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
  0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
  0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
  0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
  0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
  0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
  0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
  0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
  0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
  0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
  0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
  0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
  0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
  0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
  0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
  0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
  0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
  0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
  0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
  0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
  0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
  0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
  0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
];

if (typeof Int32Array !== 'undefined') TABLE = new Int32Array(TABLE);

const crc32 = Object(_define_crc__WEBPACK_IMPORTED_MODULE_2__["default"])('crc-32', function(buf, previous) {
  if (!buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"].isBuffer(buf)) buf = Object(_create_buffer__WEBPACK_IMPORTED_MODULE_1__["default"])(buf);

  let crc = previous === 0 ? 0 : ~~previous ^ -1;

  for (let index = 0; index < buf.length; index++) {
    const byte = buf[index];
    crc = TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
  }

  return crc ^ -1;
});

/* harmony default export */ __webpack_exports__["default"] = (crc32);


/***/ }),

/***/ "./node_modules/crc/create_buffer.js":
/*!*******************************************!*\
  !*** ./node_modules/crc/create_buffer.js ***!
  \*******************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! buffer */ "./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js");
/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(buffer__WEBPACK_IMPORTED_MODULE_0__);


const createBuffer =
  buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"].from && buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"].alloc && buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"].allocUnsafe && buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"].allocUnsafeSlow
    ? buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"].from
    : // support for Node < 5.10
      val => new buffer__WEBPACK_IMPORTED_MODULE_0__["Buffer"](val);

/* harmony default export */ __webpack_exports__["default"] = (createBuffer);


/***/ }),

/***/ "./node_modules/crc/define_crc.js":
/*!****************************************!*\
  !*** ./node_modules/crc/define_crc.js ***!
  \****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = (function(model, calc) {
  const fn = (buf, previous) => calc(buf, previous) >>> 0;
  fn.signed = calc;
  fn.unsigned = fn;
  fn.model = model;

  return fn;
});


/***/ }),

/***/ "./node_modules/debounce-promise/dist/index.js":
/*!*****************************************************!*\
  !*** ./node_modules/debounce-promise/dist/index.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* global setTimeout, clearTimeout */

module.exports = function debounce(fn) {
  var wait = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  var lastCallAt = void 0;
  var deferred = void 0;
  var timer = void 0;
  var pendingArgs = [];
  return function debounced() {
    var currentWait = getWait(wait);
    var currentTime = new Date().getTime();

    var isCold = !lastCallAt || currentTime - lastCallAt > currentWait;

    lastCallAt = currentTime;

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    if (isCold && options.leading) {
      return options.accumulate ? Promise.resolve(fn.call(this, [args])).then(function (result) {
        return result[0];
      }) : Promise.resolve(fn.call.apply(fn, [this].concat(args)));
    }

    if (deferred) {
      clearTimeout(timer);
    } else {
      deferred = defer();
    }

    pendingArgs.push(args);
    timer = setTimeout(flush.bind(this), currentWait);

    if (options.accumulate) {
      var argsIndex = pendingArgs.length - 1;
      return deferred.promise.then(function (results) {
        return results[argsIndex];
      });
    }

    return deferred.promise;
  };

  function flush() {
    var thisDeferred = deferred;
    clearTimeout(timer);

    Promise.resolve(options.accumulate ? fn.call(this, pendingArgs) : fn.apply(this, pendingArgs[pendingArgs.length - 1])).then(thisDeferred.resolve, thisDeferred.reject);

    pendingArgs = [];
    deferred = null;
  }
};

function getWait(wait) {
  return typeof wait === 'function' ? wait() : wait;
}

function defer() {
  var deferred = {};
  deferred.promise = new Promise(function (resolve, reject) {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
}
//# sourceMappingURL=index.js.map

/***/ }),

/***/ "./node_modules/decode-uri-component/index.js":
/*!****************************************************!*\
  !*** ./node_modules/decode-uri-component/index.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

var token = '%[a-f0-9]{2}';
var singleMatcher = new RegExp(token, 'gi');
var multiMatcher = new RegExp('(' + token + ')+', 'gi');

function decodeComponents(components, split) {
	try {
		// Try to decode the entire string first
		return decodeURIComponent(components.join(''));
	} catch (err) {
		// Do nothing
	}

	if (components.length === 1) {
		return components;
	}

	split = split || 1;

	// Split the array in 2 parts
	var left = components.slice(0, split);
	var right = components.slice(split);

	return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
}

function decode(input) {
	try {
		return decodeURIComponent(input);
	} catch (err) {
		var tokens = input.match(singleMatcher);

		for (var i = 1; i < tokens.length; i++) {
			input = decodeComponents(tokens, i).join('');

			tokens = input.match(singleMatcher);
		}

		return input;
	}
}

function customDecodeURIComponent(input) {
	// Keep track of all the replacements and prefill the map with the `BOM`
	var replaceMap = {
		'%FE%FF': '\uFFFD\uFFFD',
		'%FF%FE': '\uFFFD\uFFFD'
	};

	var match = multiMatcher.exec(input);
	while (match) {
		try {
			// Decode as big chunks as possible
			replaceMap[match[0]] = decodeURIComponent(match[0]);
		} catch (err) {
			var result = decode(match[0]);

			if (result !== match[0]) {
				replaceMap[match[0]] = result;
			}
		}

		match = multiMatcher.exec(input);
	}

	// Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else
	replaceMap['%C2'] = '\uFFFD';

	var entries = Object.keys(replaceMap);

	for (var i = 0; i < entries.length; i++) {
		// Replace all decoded components
		var key = entries[i];
		input = input.replace(new RegExp(key, 'g'), replaceMap[key]);
	}

	return input;
}

module.exports = function (encodedURI) {
	if (typeof encodedURI !== 'string') {
		throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`');
	}

	try {
		encodedURI = encodedURI.replace(/\+/g, ' ');

		// Try the built in decoder first
		return decodeURIComponent(encodedURI);
	} catch (err) {
		// Fallback to a more advanced decoder
		return customDecodeURIComponent(encodedURI);
	}
};


/***/ }),

/***/ "./node_modules/fast-deep-equal/index.js":
/*!***********************************************!*\
  !*** ./node_modules/fast-deep-equal/index.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// do not edit .js files directly - edit src/index.jst



module.exports = function equal(a, b) {
  if (a === b) return true;

  if (a && b && typeof a == 'object' && typeof b == 'object') {
    if (a.constructor !== b.constructor) return false;

    var length, i, keys;
    if (Array.isArray(a)) {
      length = a.length;
      if (length != b.length) return false;
      for (i = length; i-- !== 0;)
        if (!equal(a[i], b[i])) return false;
      return true;
    }



    if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
    if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
    if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();

    keys = Object.keys(a);
    length = keys.length;
    if (length !== Object.keys(b).length) return false;

    for (i = length; i-- !== 0;)
      if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;

    for (i = length; i-- !== 0;) {
      var key = keys[i];

      if (!equal(a[key], b[key])) return false;
    }

    return true;
  }

  // true if both NaN, false otherwise
  return a!==a && b!==b;
};


/***/ }),

/***/ "./node_modules/fast-json-stable-stringify/index.js":
/*!**********************************************************!*\
  !*** ./node_modules/fast-json-stable-stringify/index.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = function (data, opts) {
    if (!opts) opts = {};
    if (typeof opts === 'function') opts = { cmp: opts };
    var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;

    var cmp = opts.cmp && (function (f) {
        return function (node) {
            return function (a, b) {
                var aobj = { key: a, value: node[a] };
                var bobj = { key: b, value: node[b] };
                return f(aobj, bobj);
            };
        };
    })(opts.cmp);

    var seen = [];
    return (function stringify (node) {
        if (node && node.toJSON && typeof node.toJSON === 'function') {
            node = node.toJSON();
        }

        if (node === undefined) return;
        if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
        if (typeof node !== 'object') return JSON.stringify(node);

        var i, out;
        if (Array.isArray(node)) {
            out = '[';
            for (i = 0; i < node.length; i++) {
                if (i) out += ',';
                out += stringify(node[i]) || 'null';
            }
            return out + ']';
        }

        if (node === null) return 'null';

        if (seen.indexOf(node) !== -1) {
            if (cycles) return JSON.stringify('__cycle__');
            throw new TypeError('Converting circular structure to JSON');
        }

        var seenIndex = seen.push(node) - 1;
        var keys = Object.keys(node).sort(cmp && cmp(node));
        out = '';
        for (i = 0; i < keys.length; i++) {
            var key = keys[i];
            var value = stringify(node[key]);

            if (!value) continue;
            if (out) out += ',';
            out += JSON.stringify(key) + ':' + value;
        }
        seen.splice(seenIndex, 1);
        return '{' + out + '}';
    })(data);
};


/***/ }),

/***/ "./node_modules/howler/dist/howler.js":
/*!********************************************!*\
  !*** ./node_modules/howler/dist/howler.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
 *  howler.js v2.1.3
 *  howlerjs.com
 *
 *  (c) 2013-2019, James Simpson of GoldFire Studios
 *  goldfirestudios.com
 *
 *  MIT License
 */

(function() {

  'use strict';

  /** Global Methods **/
  /***************************************************************************/

  /**
   * Create the global controller. All contained methods and properties apply
   * to all sounds that are currently playing or will be in the future.
   */
  var HowlerGlobal = function() {
    this.init();
  };
  HowlerGlobal.prototype = {
    /**
     * Initialize the global Howler object.
     * @return {Howler}
     */
    init: function() {
      var self = this || Howler;

      // Create a global ID counter.
      self._counter = 1000;

      // Pool of unlocked HTML5 Audio objects.
      self._html5AudioPool = [];
      self.html5PoolSize = 10;

      // Internal properties.
      self._codecs = {};
      self._howls = [];
      self._muted = false;
      self._volume = 1;
      self._canPlayEvent = 'canplaythrough';
      self._navigator = (typeof window !== 'undefined' && window.navigator) ? window.navigator : null;

      // Public properties.
      self.masterGain = null;
      self.noAudio = false;
      self.usingWebAudio = true;
      self.autoSuspend = true;
      self.ctx = null;

      // Set to false to disable the auto audio unlocker.
      self.autoUnlock = true;

      // Setup the various state values for global tracking.
      self._setup();

      return self;
    },

    /**
     * Get/set the global volume for all sounds.
     * @param  {Float} vol Volume from 0.0 to 1.0.
     * @return {Howler/Float}     Returns self or current volume.
     */
    volume: function(vol) {
      var self = this || Howler;
      vol = parseFloat(vol);

      // If we don't have an AudioContext created yet, run the setup.
      if (!self.ctx) {
        setupAudioContext();
      }

      if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {
        self._volume = vol;

        // Don't update any of the nodes if we are muted.
        if (self._muted) {
          return self;
        }

        // When using Web Audio, we just need to adjust the master gain.
        if (self.usingWebAudio) {
          self.masterGain.gain.setValueAtTime(vol, Howler.ctx.currentTime);
        }

        // Loop through and change volume for all HTML5 audio nodes.
        for (var i=0; i<self._howls.length; i++) {
          if (!self._howls[i]._webAudio) {
            // Get all of the sounds in this Howl group.
            var ids = self._howls[i]._getSoundIds();

            // Loop through all sounds and change the volumes.
            for (var j=0; j<ids.length; j++) {
              var sound = self._howls[i]._soundById(ids[j]);

              if (sound && sound._node) {
                sound._node.volume = sound._volume * vol;
              }
            }
          }
        }

        return self;
      }

      return self._volume;
    },

    /**
     * Handle muting and unmuting globally.
     * @param  {Boolean} muted Is muted or not.
     */
    mute: function(muted) {
      var self = this || Howler;

      // If we don't have an AudioContext created yet, run the setup.
      if (!self.ctx) {
        setupAudioContext();
      }

      self._muted = muted;

      // With Web Audio, we just need to mute the master gain.
      if (self.usingWebAudio) {
        self.masterGain.gain.setValueAtTime(muted ? 0 : self._volume, Howler.ctx.currentTime);
      }

      // Loop through and mute all HTML5 Audio nodes.
      for (var i=0; i<self._howls.length; i++) {
        if (!self._howls[i]._webAudio) {
          // Get all of the sounds in this Howl group.
          var ids = self._howls[i]._getSoundIds();

          // Loop through all sounds and mark the audio node as muted.
          for (var j=0; j<ids.length; j++) {
            var sound = self._howls[i]._soundById(ids[j]);

            if (sound && sound._node) {
              sound._node.muted = (muted) ? true : sound._muted;
            }
          }
        }
      }

      return self;
    },

    /**
     * Unload and destroy all currently loaded Howl objects.
     * @return {Howler}
     */
    unload: function() {
      var self = this || Howler;

      for (var i=self._howls.length-1; i>=0; i--) {
        self._howls[i].unload();
      }

      // Create a new AudioContext to make sure it is fully reset.
      if (self.usingWebAudio && self.ctx && typeof self.ctx.close !== 'undefined') {
        self.ctx.close();
        self.ctx = null;
        setupAudioContext();
      }

      return self;
    },

    /**
     * Check for codec support of specific extension.
     * @param  {String} ext Audio file extention.
     * @return {Boolean}
     */
    codecs: function(ext) {
      return (this || Howler)._codecs[ext.replace(/^x-/, '')];
    },

    /**
     * Setup various state values for global tracking.
     * @return {Howler}
     */
    _setup: function() {
      var self = this || Howler;

      // Keeps track of the suspend/resume state of the AudioContext.
      self.state = self.ctx ? self.ctx.state || 'suspended' : 'suspended';

      // Automatically begin the 30-second suspend process
      self._autoSuspend();

      // Check if audio is available.
      if (!self.usingWebAudio) {
        // No audio is available on this system if noAudio is set to true.
        if (typeof Audio !== 'undefined') {
          try {
            var test = new Audio();

            // Check if the canplaythrough event is available.
            if (typeof test.oncanplaythrough === 'undefined') {
              self._canPlayEvent = 'canplay';
            }
          } catch(e) {
            self.noAudio = true;
          }
        } else {
          self.noAudio = true;
        }
      }

      // Test to make sure audio isn't disabled in Internet Explorer.
      try {
        var test = new Audio();
        if (test.muted) {
          self.noAudio = true;
        }
      } catch (e) {}

      // Check for supported codecs.
      if (!self.noAudio) {
        self._setupCodecs();
      }

      return self;
    },

    /**
     * Check for browser support for various codecs and cache the results.
     * @return {Howler}
     */
    _setupCodecs: function() {
      var self = this || Howler;
      var audioTest = null;

      // Must wrap in a try/catch because IE11 in server mode throws an error.
      try {
        audioTest = (typeof Audio !== 'undefined') ? new Audio() : null;
      } catch (err) {
        return self;
      }

      if (!audioTest || typeof audioTest.canPlayType !== 'function') {
        return self;
      }

      var mpegTest = audioTest.canPlayType('audio/mpeg;').replace(/^no$/, '');

      // Opera version <33 has mixed MP3 support, so we need to check for and block it.
      var checkOpera = self._navigator && self._navigator.userAgent.match(/OPR\/([0-6].)/g);
      var isOldOpera = (checkOpera && parseInt(checkOpera[0].split('/')[1], 10) < 33);

      self._codecs = {
        mp3: !!(!isOldOpera && (mpegTest || audioTest.canPlayType('audio/mp3;').replace(/^no$/, ''))),
        mpeg: !!mpegTest,
        opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
        ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
        oga: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
        wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
        aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),
        caf: !!audioTest.canPlayType('audio/x-caf;').replace(/^no$/, ''),
        m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
        mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
        weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''),
        webm: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''),
        dolby: !!audioTest.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/, ''),
        flac: !!(audioTest.canPlayType('audio/x-flac;') || audioTest.canPlayType('audio/flac;')).replace(/^no$/, '')
      };

      return self;
    },

    /**
     * Some browsers/devices will only allow audio to be played after a user interaction.
     * Attempt to automatically unlock audio on the first user interaction.
     * Concept from: http://paulbakaus.com/tutorials/html5/web-audio-on-ios/
     * @return {Howler}
     */
    _unlockAudio: function() {
      var self = this || Howler;

      // Only run this if Web Audio is supported and it hasn't already been unlocked.
      if (self._audioUnlocked || !self.ctx) {
        return;
      }

      self._audioUnlocked = false;
      self.autoUnlock = false;

      // Some mobile devices/platforms have distortion issues when opening/closing tabs and/or web views.
      // Bugs in the browser (especially Mobile Safari) can cause the sampleRate to change from 44100 to 48000.
      // By calling Howler.unload(), we create a new AudioContext with the correct sampleRate.
      if (!self._mobileUnloaded && self.ctx.sampleRate !== 44100) {
        self._mobileUnloaded = true;
        self.unload();
      }

      // Scratch buffer for enabling iOS to dispose of web audio buffers correctly, as per:
      // http://stackoverflow.com/questions/24119684
      self._scratchBuffer = self.ctx.createBuffer(1, 1, 22050);

      // Call this method on touch start to create and play a buffer,
      // then check if the audio actually played to determine if
      // audio has now been unlocked on iOS, Android, etc.
      var unlock = function(e) {
        // Create a pool of unlocked HTML5 Audio objects that can
        // be used for playing sounds without user interaction. HTML5
        // Audio objects must be individually unlocked, as opposed
        // to the WebAudio API which only needs a single activation.
        // This must occur before WebAudio setup or the source.onended
        // event will not fire.
        for (var i=0; i<self.html5PoolSize; i++) {
          try {
            var audioNode = new Audio();

            // Mark this Audio object as unlocked to ensure it can get returned
            // to the unlocked pool when released.
            audioNode._unlocked = true;

            // Add the audio node to the pool.
            self._releaseHtml5Audio(audioNode);
          } catch (e) {
            self.noAudio = true;
          }
        }

        // Loop through any assigned audio nodes and unlock them.
        for (var i=0; i<self._howls.length; i++) {
          if (!self._howls[i]._webAudio) {
            // Get all of the sounds in this Howl group.
            var ids = self._howls[i]._getSoundIds();

            // Loop through all sounds and unlock the audio nodes.
            for (var j=0; j<ids.length; j++) {
              var sound = self._howls[i]._soundById(ids[j]);

              if (sound && sound._node && !sound._node._unlocked) {
                sound._node._unlocked = true;
                sound._node.load();
              }
            }
          }
        }

        // Fix Android can not play in suspend state.
        self._autoResume();

        // Create an empty buffer.
        var source = self.ctx.createBufferSource();
        source.buffer = self._scratchBuffer;
        source.connect(self.ctx.destination);

        // Play the empty buffer.
        if (typeof source.start === 'undefined') {
          source.noteOn(0);
        } else {
          source.start(0);
        }

        // Calling resume() on a stack initiated by user gesture is what actually unlocks the audio on Android Chrome >= 55.
        if (typeof self.ctx.resume === 'function') {
          self.ctx.resume();
        }

        // Setup a timeout to check that we are unlocked on the next event loop.
        source.onended = function() {
          source.disconnect(0);

          // Update the unlocked state and prevent this check from happening again.
          self._audioUnlocked = true;

          // Remove the touch start listener.
          document.removeEventListener('touchstart', unlock, true);
          document.removeEventListener('touchend', unlock, true);
          document.removeEventListener('click', unlock, true);

          // Let all sounds know that audio has been unlocked.
          for (var i=0; i<self._howls.length; i++) {
            self._howls[i]._emit('unlock');
          }
        };
      };

      // Setup a touch start listener to attempt an unlock in.
      document.addEventListener('touchstart', unlock, true);
      document.addEventListener('touchend', unlock, true);
      document.addEventListener('click', unlock, true);

      return self;
    },

    /**
     * Get an unlocked HTML5 Audio object from the pool. If none are left,
     * return a new Audio object and throw a warning.
     * @return {Audio} HTML5 Audio object.
     */
    _obtainHtml5Audio: function() {
      var self = this || Howler;

      // Return the next object from the pool if one exists.
      if (self._html5AudioPool.length) {
        return self._html5AudioPool.pop();
      }

      //.Check if the audio is locked and throw a warning.
      var testPlay = new Audio().play();
      if (testPlay && typeof Promise !== 'undefined' && (testPlay instanceof Promise || typeof testPlay.then === 'function')) {
        testPlay.catch(function() {
          console.warn('HTML5 Audio pool exhausted, returning potentially locked audio object.');
        });
      }

      return new Audio();
    },

    /**
     * Return an activated HTML5 Audio object to the pool.
     * @return {Howler}
     */
    _releaseHtml5Audio: function(audio) {
      var self = this || Howler;

      // Don't add audio to the pool if we don't know if it has been unlocked.
      if (audio._unlocked) {
        self._html5AudioPool.push(audio);
      }

      return self;
    },

    /**
     * Automatically suspend the Web Audio AudioContext after no sound has played for 30 seconds.
     * This saves processing/energy and fixes various browser-specific bugs with audio getting stuck.
     * @return {Howler}
     */
    _autoSuspend: function() {
      var self = this;

      if (!self.autoSuspend || !self.ctx || typeof self.ctx.suspend === 'undefined' || !Howler.usingWebAudio) {
        return;
      }

      // Check if any sounds are playing.
      for (var i=0; i<self._howls.length; i++) {
        if (self._howls[i]._webAudio) {
          for (var j=0; j<self._howls[i]._sounds.length; j++) {
            if (!self._howls[i]._sounds[j]._paused) {
              return self;
            }
          }
        }
      }

      if (self._suspendTimer) {
        clearTimeout(self._suspendTimer);
      }

      // If no sound has played after 30 seconds, suspend the context.
      self._suspendTimer = setTimeout(function() {
        if (!self.autoSuspend) {
          return;
        }

        self._suspendTimer = null;
        self.state = 'suspending';
        self.ctx.suspend().then(function() {
          self.state = 'suspended';

          if (self._resumeAfterSuspend) {
            delete self._resumeAfterSuspend;
            self._autoResume();
          }
        });
      }, 30000);

      return self;
    },

    /**
     * Automatically resume the Web Audio AudioContext when a new sound is played.
     * @return {Howler}
     */
    _autoResume: function() {
      var self = this;

      if (!self.ctx || typeof self.ctx.resume === 'undefined' || !Howler.usingWebAudio) {
        return;
      }

      if (self.state === 'running' && self._suspendTimer) {
        clearTimeout(self._suspendTimer);
        self._suspendTimer = null;
      } else if (self.state === 'suspended') {
        self.ctx.resume().then(function() {
          self.state = 'running';

          // Emit to all Howls that the audio has resumed.
          for (var i=0; i<self._howls.length; i++) {
            self._howls[i]._emit('resume');
          }
        });

        if (self._suspendTimer) {
          clearTimeout(self._suspendTimer);
          self._suspendTimer = null;
        }
      } else if (self.state === 'suspending') {
        self._resumeAfterSuspend = true;
      }

      return self;
    }
  };

  // Setup the global audio controller.
  var Howler = new HowlerGlobal();

  /** Group Methods **/
  /***************************************************************************/

  /**
   * Create an audio group controller.
   * @param {Object} o Passed in properties for this group.
   */
  var Howl = function(o) {
    var self = this;

    // Throw an error if no source is provided.
    if (!o.src || o.src.length === 0) {
      console.error('An array of source files must be passed with any new Howl.');
      return;
    }

    self.init(o);
  };
  Howl.prototype = {
    /**
     * Initialize a new Howl group object.
     * @param  {Object} o Passed in properties for this group.
     * @return {Howl}
     */
    init: function(o) {
      var self = this;

      // If we don't have an AudioContext created yet, run the setup.
      if (!Howler.ctx) {
        setupAudioContext();
      }

      // Setup user-defined default properties.
      self._autoplay = o.autoplay || false;
      self._format = (typeof o.format !== 'string') ? o.format : [o.format];
      self._html5 = o.html5 || false;
      self._muted = o.mute || false;
      self._loop = o.loop || false;
      self._pool = o.pool || 5;
      self._preload = (typeof o.preload === 'boolean') ? o.preload : true;
      self._rate = o.rate || 1;
      self._sprite = o.sprite || {};
      self._src = (typeof o.src !== 'string') ? o.src : [o.src];
      self._volume = o.volume !== undefined ? o.volume : 1;
      self._xhrWithCredentials = o.xhrWithCredentials || false;

      // Setup all other default properties.
      self._duration = 0;
      self._state = 'unloaded';
      self._sounds = [];
      self._endTimers = {};
      self._queue = [];
      self._playLock = false;

      // Setup event listeners.
      self._onend = o.onend ? [{fn: o.onend}] : [];
      self._onfade = o.onfade ? [{fn: o.onfade}] : [];
      self._onload = o.onload ? [{fn: o.onload}] : [];
      self._onloaderror = o.onloaderror ? [{fn: o.onloaderror}] : [];
      self._onplayerror = o.onplayerror ? [{fn: o.onplayerror}] : [];
      self._onpause = o.onpause ? [{fn: o.onpause}] : [];
      self._onplay = o.onplay ? [{fn: o.onplay}] : [];
      self._onstop = o.onstop ? [{fn: o.onstop}] : [];
      self._onmute = o.onmute ? [{fn: o.onmute}] : [];
      self._onvolume = o.onvolume ? [{fn: o.onvolume}] : [];
      self._onrate = o.onrate ? [{fn: o.onrate}] : [];
      self._onseek = o.onseek ? [{fn: o.onseek}] : [];
      self._onunlock = o.onunlock ? [{fn: o.onunlock}] : [];
      self._onresume = [];

      // Web Audio or HTML5 Audio?
      self._webAudio = Howler.usingWebAudio && !self._html5;

      // Automatically try to enable audio.
      if (typeof Howler.ctx !== 'undefined' && Howler.ctx && Howler.autoUnlock) {
        Howler._unlockAudio();
      }

      // Keep track of this Howl group in the global controller.
      Howler._howls.push(self);

      // If they selected autoplay, add a play event to the load queue.
      if (self._autoplay) {
        self._queue.push({
          event: 'play',
          action: function() {
            self.play();
          }
        });
      }

      // Load the source file unless otherwise specified.
      if (self._preload) {
        self.load();
      }

      return self;
    },

    /**
     * Load the audio file.
     * @return {Howler}
     */
    load: function() {
      var self = this;
      var url = null;

      // If no audio is available, quit immediately.
      if (Howler.noAudio) {
        self._emit('loaderror', null, 'No audio support.');
        return;
      }

      // Make sure our source is in an array.
      if (typeof self._src === 'string') {
        self._src = [self._src];
      }

      // Loop through the sources and pick the first one that is compatible.
      for (var i=0; i<self._src.length; i++) {
        var ext, str;

        if (self._format && self._format[i]) {
          // If an extension was specified, use that instead.
          ext = self._format[i];
        } else {
          // Make sure the source is a string.
          str = self._src[i];
          if (typeof str !== 'string') {
            self._emit('loaderror', null, 'Non-string found in selected audio sources - ignoring.');
            continue;
          }

          // Extract the file extension from the URL or base64 data URI.
          ext = /^data:audio\/([^;,]+);/i.exec(str);
          if (!ext) {
            ext = /\.([^.]+)$/.exec(str.split('?', 1)[0]);
          }

          if (ext) {
            ext = ext[1].toLowerCase();
          }
        }

        // Log a warning if no extension was found.
        if (!ext) {
          console.warn('No file extension was found. Consider using the "format" property or specify an extension.');
        }

        // Check if this extension is available.
        if (ext && Howler.codecs(ext)) {
          url = self._src[i];
          break;
        }
      }

      if (!url) {
        self._emit('loaderror', null, 'No codec support for selected audio sources.');
        return;
      }

      self._src = url;
      self._state = 'loading';

      // If the hosting page is HTTPS and the source isn't,
      // drop down to HTML5 Audio to avoid Mixed Content errors.
      if (window.location.protocol === 'https:' && url.slice(0, 5) === 'http:') {
        self._html5 = true;
        self._webAudio = false;
      }

      // Create a new sound object and add it to the pool.
      new Sound(self);

      // Load and decode the audio data for playback.
      if (self._webAudio) {
        loadBuffer(self);
      }

      return self;
    },

    /**
     * Play a sound or resume previous playback.
     * @param  {String/Number} sprite   Sprite name for sprite playback or sound id to continue previous.
     * @param  {Boolean} internal Internal Use: true prevents event firing.
     * @return {Number}          Sound ID.
     */
    play: function(sprite, internal) {
      var self = this;
      var id = null;

      // Determine if a sprite, sound id or nothing was passed
      if (typeof sprite === 'number') {
        id = sprite;
        sprite = null;
      } else if (typeof sprite === 'string' && self._state === 'loaded' && !self._sprite[sprite]) {
        // If the passed sprite doesn't exist, do nothing.
        return null;
      } else if (typeof sprite === 'undefined') {
        // Use the default sound sprite (plays the full audio length).
        sprite = '__default';

        // Check if there is a single paused sound that isn't ended. 
        // If there is, play that sound. If not, continue as usual.  
        if (!self._playLock) {
          var num = 0;
          for (var i=0; i<self._sounds.length; i++) {
            if (self._sounds[i]._paused && !self._sounds[i]._ended) {
              num++;
              id = self._sounds[i]._id;
            }
          }

          if (num === 1) {
            sprite = null;
          } else {
            id = null;
          }
        }
      }

      // Get the selected node, or get one from the pool.
      var sound = id ? self._soundById(id) : self._inactiveSound();

      // If the sound doesn't exist, do nothing.
      if (!sound) {
        return null;
      }

      // Select the sprite definition.
      if (id && !sprite) {
        sprite = sound._sprite || '__default';
      }

      // If the sound hasn't loaded, we must wait to get the audio's duration.
      // We also need to wait to make sure we don't run into race conditions with
      // the order of function calls.
      if (self._state !== 'loaded') {
        // Set the sprite value on this sound.
        sound._sprite = sprite;

        // Mark this sound as not ended in case another sound is played before this one loads.
        sound._ended = false;

        // Add the sound to the queue to be played on load.
        var soundId = sound._id;
        self._queue.push({
          event: 'play',
          action: function() {
            self.play(soundId);
          }
        });

        return soundId;
      }

      // Don't play the sound if an id was passed and it is already playing.
      if (id && !sound._paused) {
        // Trigger the play event, in order to keep iterating through queue.
        if (!internal) {
          self._loadQueue('play');
        }

        return sound._id;
      }

      // Make sure the AudioContext isn't suspended, and resume it if it is.
      if (self._webAudio) {
        Howler._autoResume();
      }

      // Determine how long to play for and where to start playing.
      var seek = Math.max(0, sound._seek > 0 ? sound._seek : self._sprite[sprite][0] / 1000);
      var duration = Math.max(0, ((self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000) - seek);
      var timeout = (duration * 1000) / Math.abs(sound._rate);
      var start = self._sprite[sprite][0] / 1000;
      var stop = (self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000;
      sound._sprite = sprite;

      // Mark the sound as ended instantly so that this async playback
      // doesn't get grabbed by another call to play while this one waits to start.
      sound._ended = false;

      // Update the parameters of the sound.
      var setParams = function() {
        sound._paused = false;
        sound._seek = seek;
        sound._start = start;
        sound._stop = stop;
        sound._loop = !!(sound._loop || self._sprite[sprite][2]);
      };

      // End the sound instantly if seek is at the end.
      if (seek >= stop) {
        self._ended(sound);
        return;
      }

      // Begin the actual playback.
      var node = sound._node;
      if (self._webAudio) {
        // Fire this when the sound is ready to play to begin Web Audio playback.
        var playWebAudio = function() {
          self._playLock = false;
          setParams();
          self._refreshBuffer(sound);

          // Setup the playback params.
          var vol = (sound._muted || self._muted) ? 0 : sound._volume;
          node.gain.setValueAtTime(vol, Howler.ctx.currentTime);
          sound._playStart = Howler.ctx.currentTime;

          // Play the sound using the supported method.
          if (typeof node.bufferSource.start === 'undefined') {
            sound._loop ? node.bufferSource.noteGrainOn(0, seek, 86400) : node.bufferSource.noteGrainOn(0, seek, duration);
          } else {
            sound._loop ? node.bufferSource.start(0, seek, 86400) : node.bufferSource.start(0, seek, duration);
          }

          // Start a new timer if none is present.
          if (timeout !== Infinity) {
            self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);
          }

          if (!internal) {
            setTimeout(function() {
              self._emit('play', sound._id);
              self._loadQueue();
            }, 0);
          }
        };

        if (Howler.state === 'running') {
          playWebAudio();
        } else {
          self._playLock = true;

          // Wait for the audio context to resume before playing.
          self.once('resume', playWebAudio);

          // Cancel the end timer.
          self._clearTimer(sound._id);
        }
      } else {
        // Fire this when the sound is ready to play to begin HTML5 Audio playback.
        var playHtml5 = function() {
          node.currentTime = seek;
          node.muted = sound._muted || self._muted || Howler._muted || node.muted;
          node.volume = sound._volume * Howler.volume();
          node.playbackRate = sound._rate;

          // Some browsers will throw an error if this is called without user interaction.
          try {
            var play = node.play();

            // Support older browsers that don't support promises, and thus don't have this issue.
            if (play && typeof Promise !== 'undefined' && (play instanceof Promise || typeof play.then === 'function')) {
              // Implements a lock to prevent DOMException: The play() request was interrupted by a call to pause().
              self._playLock = true;

              // Set param values immediately.
              setParams();

              // Releases the lock and executes queued actions.
              play
                .then(function() {
                  self._playLock = false;
                  node._unlocked = true;
                  if (!internal) {
                    self._emit('play', sound._id);
                    self._loadQueue();
                  }
                })
                .catch(function() {
                  self._playLock = false;
                  self._emit('playerror', sound._id, 'Playback was unable to start. This is most commonly an issue ' +
                    'on mobile devices and Chrome where playback was not within a user interaction.');

                  // Reset the ended and paused values.
                  sound._ended = true;
                  sound._paused = true;
                });
            } else if (!internal) {
              self._playLock = false;
              setParams();
              self._emit('play', sound._id);
              self._loadQueue();
            }

            // Setting rate before playing won't work in IE, so we set it again here.
            node.playbackRate = sound._rate;

            // If the node is still paused, then we can assume there was a playback issue.
            if (node.paused) {
              self._emit('playerror', sound._id, 'Playback was unable to start. This is most commonly an issue ' +
                'on mobile devices and Chrome where playback was not within a user interaction.');
              return;
            }

            // Setup the end timer on sprites or listen for the ended event.
            if (sprite !== '__default' || sound._loop) {
              self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);
            } else {
              self._endTimers[sound._id] = function() {
                // Fire ended on this audio node.
                self._ended(sound);

                // Clear this listener.
                node.removeEventListener('ended', self._endTimers[sound._id], false);
              };
              node.addEventListener('ended', self._endTimers[sound._id], false);
            }
          } catch (err) {
            self._emit('playerror', sound._id, err);
          }
        };

        // If this is streaming audio, make sure the src is set and load again.
        if (node.src === 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA') {
          node.src = self._src;
          node.load();
        }

        // Play immediately if ready, or wait for the 'canplaythrough'e vent.
        var loadedNoReadyState = (window && window.ejecta) || (!node.readyState && Howler._navigator.isCocoonJS);
        if (node.readyState >= 3 || loadedNoReadyState) {
          playHtml5();
        } else {
          self._playLock = true;

          var listener = function() {
            // Begin playback.
            playHtml5();

            // Clear this listener.
            node.removeEventListener(Howler._canPlayEvent, listener, false);
          };
          node.addEventListener(Howler._canPlayEvent, listener, false);

          // Cancel the end timer.
          self._clearTimer(sound._id);
        }
      }

      return sound._id;
    },

    /**
     * Pause playback and save current position.
     * @param  {Number} id The sound ID (empty to pause all in group).
     * @return {Howl}
     */
    pause: function(id) {
      var self = this;

      // If the sound hasn't loaded or a play() promise is pending, add it to the load queue to pause when capable.
      if (self._state !== 'loaded' || self._playLock) {
        self._queue.push({
          event: 'pause',
          action: function() {
            self.pause(id);
          }
        });

        return self;
      }

      // If no id is passed, get all ID's to be paused.
      var ids = self._getSoundIds(id);

      for (var i=0; i<ids.length; i++) {
        // Clear the end timer.
        self._clearTimer(ids[i]);

        // Get the sound.
        var sound = self._soundById(ids[i]);

        if (sound && !sound._paused) {
          // Reset the seek position.
          sound._seek = self.seek(ids[i]);
          sound._rateSeek = 0;
          sound._paused = true;

          // Stop currently running fades.
          self._stopFade(ids[i]);

          if (sound._node) {
            if (self._webAudio) {
              // Make sure the sound has been created.
              if (!sound._node.bufferSource) {
                continue;
              }

              if (typeof sound._node.bufferSource.stop === 'undefined') {
                sound._node.bufferSource.noteOff(0);
              } else {
                sound._node.bufferSource.stop(0);
              }

              // Clean up the buffer source.
              self._cleanBuffer(sound._node);
            } else if (!isNaN(sound._node.duration) || sound._node.duration === Infinity) {
              sound._node.pause();
            }
          }
        }

        // Fire the pause event, unless `true` is passed as the 2nd argument.
        if (!arguments[1]) {
          self._emit('pause', sound ? sound._id : null);
        }
      }

      return self;
    },

    /**
     * Stop playback and reset to start.
     * @param  {Number} id The sound ID (empty to stop all in group).
     * @param  {Boolean} internal Internal Use: true prevents event firing.
     * @return {Howl}
     */
    stop: function(id, internal) {
      var self = this;

      // If the sound hasn't loaded, add it to the load queue to stop when capable.
      if (self._state !== 'loaded' || self._playLock) {
        self._queue.push({
          event: 'stop',
          action: function() {
            self.stop(id);
          }
        });

        return self;
      }

      // If no id is passed, get all ID's to be stopped.
      var ids = self._getSoundIds(id);

      for (var i=0; i<ids.length; i++) {
        // Clear the end timer.
        self._clearTimer(ids[i]);

        // Get the sound.
        var sound = self._soundById(ids[i]);

        if (sound) {
          // Reset the seek position.
          sound._seek = sound._start || 0;
          sound._rateSeek = 0;
          sound._paused = true;
          sound._ended = true;

          // Stop currently running fades.
          self._stopFade(ids[i]);

          if (sound._node) {
            if (self._webAudio) {
              // Make sure the sound's AudioBufferSourceNode has been created.
              if (sound._node.bufferSource) {
                if (typeof sound._node.bufferSource.stop === 'undefined') {
                  sound._node.bufferSource.noteOff(0);
                } else {
                  sound._node.bufferSource.stop(0);
                }

                // Clean up the buffer source.
                self._cleanBuffer(sound._node);
              }
            } else if (!isNaN(sound._node.duration) || sound._node.duration === Infinity) {
              sound._node.currentTime = sound._start || 0;
              sound._node.pause();

              // If this is a live stream, stop download once the audio is stopped.
              if (sound._node.duration === Infinity) {
                self._clearSound(sound._node);
              }
            }
          }

          if (!internal) {
            self._emit('stop', sound._id);
          }
        }
      }

      return self;
    },

    /**
     * Mute/unmute a single sound or all sounds in this Howl group.
     * @param  {Boolean} muted Set to true to mute and false to unmute.
     * @param  {Number} id    The sound ID to update (omit to mute/unmute all).
     * @return {Howl}
     */
    mute: function(muted, id) {
      var self = this;

      // If the sound hasn't loaded, add it to the load queue to mute when capable.
      if (self._state !== 'loaded'|| self._playLock) {
        self._queue.push({
          event: 'mute',
          action: function() {
            self.mute(muted, id);
          }
        });

        return self;
      }

      // If applying mute/unmute to all sounds, update the group's value.
      if (typeof id === 'undefined') {
        if (typeof muted === 'boolean') {
          self._muted = muted;
        } else {
          return self._muted;
        }
      }

      // If no id is passed, get all ID's to be muted.
      var ids = self._getSoundIds(id);

      for (var i=0; i<ids.length; i++) {
        // Get the sound.
        var sound = self._soundById(ids[i]);

        if (sound) {
          sound._muted = muted;

          // Cancel active fade and set the volume to the end value.
          if (sound._interval) {
            self._stopFade(sound._id);
          }

          if (self._webAudio && sound._node) {
            sound._node.gain.setValueAtTime(muted ? 0 : sound._volume, Howler.ctx.currentTime);
          } else if (sound._node) {
            sound._node.muted = Howler._muted ? true : muted;
          }

          self._emit('mute', sound._id);
        }
      }

      return self;
    },

    /**
     * Get/set the volume of this sound or of the Howl group. This method can optionally take 0, 1 or 2 arguments.
     *   volume() -> Returns the group's volume value.
     *   volume(id) -> Returns the sound id's current volume.
     *   volume(vol) -> Sets the volume of all sounds in this Howl group.
     *   volume(vol, id) -> Sets the volume of passed sound id.
     * @return {Howl/Number} Returns self or current volume.
     */
    volume: function() {
      var self = this;
      var args = arguments;
      var vol, id;

      // Determine the values based on arguments.
      if (args.length === 0) {
        // Return the value of the groups' volume.
        return self._volume;
      } else if (args.length === 1 || args.length === 2 && typeof args[1] === 'undefined') {
        // First check if this is an ID, and if not, assume it is a new volume.
        var ids = self._getSoundIds();
        var index = ids.indexOf(args[0]);
        if (index >= 0) {
          id = parseInt(args[0], 10);
        } else {
          vol = parseFloat(args[0]);
        }
      } else if (args.length >= 2) {
        vol = parseFloat(args[0]);
        id = parseInt(args[1], 10);
      }

      // Update the volume or return the current volume.
      var sound;
      if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {
        // If the sound hasn't loaded, add it to the load queue to change volume when capable.
        if (self._state !== 'loaded'|| self._playLock) {
          self._queue.push({
            event: 'volume',
            action: function() {
              self.volume.apply(self, args);
            }
          });

          return self;
        }

        // Set the group volume.
        if (typeof id === 'undefined') {
          self._volume = vol;
        }

        // Update one or all volumes.
        id = self._getSoundIds(id);
        for (var i=0; i<id.length; i++) {
          // Get the sound.
          sound = self._soundById(id[i]);

          if (sound) {
            sound._volume = vol;

            // Stop currently running fades.
            if (!args[2]) {
              self._stopFade(id[i]);
            }

            if (self._webAudio && sound._node && !sound._muted) {
              sound._node.gain.setValueAtTime(vol, Howler.ctx.currentTime);
            } else if (sound._node && !sound._muted) {
              sound._node.volume = vol * Howler.volume();
            }

            self._emit('volume', sound._id);
          }
        }
      } else {
        sound = id ? self._soundById(id) : self._sounds[0];
        return sound ? sound._volume : 0;
      }

      return self;
    },

    /**
     * Fade a currently playing sound between two volumes (if no id is passed, all sounds will fade).
     * @param  {Number} from The value to fade from (0.0 to 1.0).
     * @param  {Number} to   The volume to fade to (0.0 to 1.0).
     * @param  {Number} len  Time in milliseconds to fade.
     * @param  {Number} id   The sound id (omit to fade all sounds).
     * @return {Howl}
     */
    fade: function(from, to, len, id) {
      var self = this;

      // If the sound hasn't loaded, add it to the load queue to fade when capable.
      if (self._state !== 'loaded' || self._playLock) {
        self._queue.push({
          event: 'fade',
          action: function() {
            self.fade(from, to, len, id);
          }
        });

        return self;
      }

      // Make sure the to/from/len values are numbers.
      from = parseFloat(from);
      to = parseFloat(to);
      len = parseFloat(len);

      // Set the volume to the start position.
      self.volume(from, id);

      // Fade the volume of one or all sounds.
      var ids = self._getSoundIds(id);
      for (var i=0; i<ids.length; i++) {
        // Get the sound.
        var sound = self._soundById(ids[i]);

        // Create a linear fade or fall back to timeouts with HTML5 Audio.
        if (sound) {
          // Stop the previous fade if no sprite is being used (otherwise, volume handles this).
          if (!id) {
            self._stopFade(ids[i]);
          }

          // If we are using Web Audio, let the native methods do the actual fade.
          if (self._webAudio && !sound._muted) {
            var currentTime = Howler.ctx.currentTime;
            var end = currentTime + (len / 1000);
            sound._volume = from;
            sound._node.gain.setValueAtTime(from, currentTime);
            sound._node.gain.linearRampToValueAtTime(to, end);
          }

          self._startFadeInterval(sound, from, to, len, ids[i], typeof id === 'undefined');
        }
      }

      return self;
    },

    /**
     * Starts the internal interval to fade a sound.
     * @param  {Object} sound Reference to sound to fade.
     * @param  {Number} from The value to fade from (0.0 to 1.0).
     * @param  {Number} to   The volume to fade to (0.0 to 1.0).
     * @param  {Number} len  Time in milliseconds to fade.
     * @param  {Number} id   The sound id to fade.
     * @param  {Boolean} isGroup   If true, set the volume on the group.
     */
    _startFadeInterval: function(sound, from, to, len, id, isGroup) {
      var self = this;
      var vol = from;
      var diff = to - from;
      var steps = Math.abs(diff / 0.01);
      var stepLen = Math.max(4, (steps > 0) ? len / steps : len);
      var lastTick = Date.now();

      // Store the value being faded to.
      sound._fadeTo = to;

      // Update the volume value on each interval tick.
      sound._interval = setInterval(function() {
        // Update the volume based on the time since the last tick.
        var tick = (Date.now() - lastTick) / len;
        lastTick = Date.now();
        vol += diff * tick;

        // Make sure the volume is in the right bounds.
        vol = Math.max(0, vol);
        vol = Math.min(1, vol);

        // Round to within 2 decimal points.
        vol = Math.round(vol * 100) / 100;

        // Change the volume.
        if (self._webAudio) {
          sound._volume = vol;
        } else {
          self.volume(vol, sound._id, true);
        }

        // Set the group's volume.
        if (isGroup) {
          self._volume = vol;
        }

        // When the fade is complete, stop it and fire event.
        if ((to < from && vol <= to) || (to > from && vol >= to)) {
          clearInterval(sound._interval);
          sound._interval = null;
          sound._fadeTo = null;
          self.volume(to, sound._id);
          self._emit('fade', sound._id);
        }
      }, stepLen);
    },

    /**
     * Internal method that stops the currently playing fade when
     * a new fade starts, volume is changed or the sound is stopped.
     * @param  {Number} id The sound id.
     * @return {Howl}
     */
    _stopFade: function(id) {
      var self = this;
      var sound = self._soundById(id);

      if (sound && sound._interval) {
        if (self._webAudio) {
          sound._node.gain.cancelScheduledValues(Howler.ctx.currentTime);
        }

        clearInterval(sound._interval);
        sound._interval = null;
        self.volume(sound._fadeTo, id);
        sound._fadeTo = null;
        self._emit('fade', id);
      }

      return self;
    },

    /**
     * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments.
     *   loop() -> Returns the group's loop value.
     *   loop(id) -> Returns the sound id's loop value.
     *   loop(loop) -> Sets the loop value for all sounds in this Howl group.
     *   loop(loop, id) -> Sets the loop value of passed sound id.
     * @return {Howl/Boolean} Returns self or current loop value.
     */
    loop: function() {
      var self = this;
      var args = arguments;
      var loop, id, sound;

      // Determine the values for loop and id.
      if (args.length === 0) {
        // Return the grou's loop value.
        return self._loop;
      } else if (args.length === 1) {
        if (typeof args[0] === 'boolean') {
          loop = args[0];
          self._loop = loop;
        } else {
          // Return this sound's loop value.
          sound = self._soundById(parseInt(args[0], 10));
          return sound ? sound._loop : false;
        }
      } else if (args.length === 2) {
        loop = args[0];
        id = parseInt(args[1], 10);
      }

      // If no id is passed, get all ID's to be looped.
      var ids = self._getSoundIds(id);
      for (var i=0; i<ids.length; i++) {
        sound = self._soundById(ids[i]);

        if (sound) {
          sound._loop = loop;
          if (self._webAudio && sound._node && sound._node.bufferSource) {
            sound._node.bufferSource.loop = loop;
            if (loop) {
              sound._node.bufferSource.loopStart = sound._start || 0;
              sound._node.bufferSource.loopEnd = sound._stop;
            }
          }
        }
      }

      return self;
    },

    /**
     * Get/set the playback rate of a sound. This method can optionally take 0, 1 or 2 arguments.
     *   rate() -> Returns the first sound node's current playback rate.
     *   rate(id) -> Returns the sound id's current playback rate.
     *   rate(rate) -> Sets the playback rate of all sounds in this Howl group.
     *   rate(rate, id) -> Sets the playback rate of passed sound id.
     * @return {Howl/Number} Returns self or the current playback rate.
     */
    rate: function() {
      var self = this;
      var args = arguments;
      var rate, id;

      // Determine the values based on arguments.
      if (args.length === 0) {
        // We will simply return the current rate of the first node.
        id = self._sounds[0]._id;
      } else if (args.length === 1) {
        // First check if this is an ID, and if not, assume it is a new rate value.
        var ids = self._getSoundIds();
        var index = ids.indexOf(args[0]);
        if (index >= 0) {
          id = parseInt(args[0], 10);
        } else {
          rate = parseFloat(args[0]);
        }
      } else if (args.length === 2) {
        rate = parseFloat(args[0]);
        id = parseInt(args[1], 10);
      }

      // Update the playback rate or return the current value.
      var sound;
      if (typeof rate === 'number') {
        // If the sound hasn't loaded, add it to the load queue to change playback rate when capable.
        if (self._state !== 'loaded' || self._playLock) {
          self._queue.push({
            event: 'rate',
            action: function() {
              self.rate.apply(self, args);
            }
          });

          return self;
        }

        // Set the group rate.
        if (typeof id === 'undefined') {
          self._rate = rate;
        }

        // Update one or all volumes.
        id = self._getSoundIds(id);
        for (var i=0; i<id.length; i++) {
          // Get the sound.
          sound = self._soundById(id[i]);

          if (sound) {
            // Keep track of our position when the rate changed and update the playback
            // start position so we can properly adjust the seek position for time elapsed.
            if (self.playing(id[i])) {
              sound._rateSeek = self.seek(id[i]);
              sound._playStart = self._webAudio ? Howler.ctx.currentTime : sound._playStart;
            }
            sound._rate = rate;

            // Change the playback rate.
            if (self._webAudio && sound._node && sound._node.bufferSource) {
              sound._node.bufferSource.playbackRate.setValueAtTime(rate, Howler.ctx.currentTime);
            } else if (sound._node) {
              sound._node.playbackRate = rate;
            }

            // Reset the timers.
            var seek = self.seek(id[i]);
            var duration = ((self._sprite[sound._sprite][0] + self._sprite[sound._sprite][1]) / 1000) - seek;
            var timeout = (duration * 1000) / Math.abs(sound._rate);

            // Start a new end timer if sound is already playing.
            if (self._endTimers[id[i]] || !sound._paused) {
              self._clearTimer(id[i]);
              self._endTimers[id[i]] = setTimeout(self._ended.bind(self, sound), timeout);
            }

            self._emit('rate', sound._id);
          }
        }
      } else {
        sound = self._soundById(id);
        return sound ? sound._rate : self._rate;
      }

      return self;
    },

    /**
     * Get/set the seek position of a sound. This method can optionally take 0, 1 or 2 arguments.
     *   seek() -> Returns the first sound node's current seek position.
     *   seek(id) -> Returns the sound id's current seek position.
     *   seek(seek) -> Sets the seek position of the first sound node.
     *   seek(seek, id) -> Sets the seek position of passed sound id.
     * @return {Howl/Number} Returns self or the current seek position.
     */
    seek: function() {
      var self = this;
      var args = arguments;
      var seek, id;

      // Determine the values based on arguments.
      if (args.length === 0) {
        // We will simply return the current position of the first node.
        id = self._sounds[0]._id;
      } else if (args.length === 1) {
        // First check if this is an ID, and if not, assume it is a new seek position.
        var ids = self._getSoundIds();
        var index = ids.indexOf(args[0]);
        if (index >= 0) {
          id = parseInt(args[0], 10);
        } else if (self._sounds.length) {
          id = self._sounds[0]._id;
          seek = parseFloat(args[0]);
        }
      } else if (args.length === 2) {
        seek = parseFloat(args[0]);
        id = parseInt(args[1], 10);
      }

      // If there is no ID, bail out.
      if (typeof id === 'undefined') {
        return self;
      }

      // If the sound hasn't loaded, add it to the load queue to seek when capable.
      if (self._state !== 'loaded' || self._playLock) {
        self._queue.push({
          event: 'seek',
          action: function() {
            self.seek.apply(self, args);
          }
        });

        return self;
      }

      // Get the sound.
      var sound = self._soundById(id);

      if (sound) {
        if (typeof seek === 'number' && seek >= 0) {
          // Pause the sound and update position for restarting playback.
          var playing = self.playing(id);
          if (playing) {
            self.pause(id, true);
          }

          // Move the position of the track and cancel timer.
          sound._seek = seek;
          sound._ended = false;
          self._clearTimer(id);

          // Update the seek position for HTML5 Audio.
          if (!self._webAudio && sound._node && !isNaN(sound._node.duration)) {
            sound._node.currentTime = seek;
          }

          // Seek and emit when ready.
          var seekAndEmit = function() {
            self._emit('seek', id);

            // Restart the playback if the sound was playing.
            if (playing) {
              self.play(id, true);
            }
          };

          // Wait for the play lock to be unset before emitting (HTML5 Audio).
          if (playing && !self._webAudio) {
            var emitSeek = function() {
              if (!self._playLock) {
                seekAndEmit();
              } else {
                setTimeout(emitSeek, 0);
              }
            };
            setTimeout(emitSeek, 0);
          } else {
            seekAndEmit();
          }
        } else {
          if (self._webAudio) {
            var realTime = self.playing(id) ? Howler.ctx.currentTime - sound._playStart : 0;
            var rateSeek = sound._rateSeek ? sound._rateSeek - sound._seek : 0;
            return sound._seek + (rateSeek + realTime * Math.abs(sound._rate));
          } else {
            return sound._node.currentTime;
          }
        }
      }

      return self;
    },

    /**
     * Check if a specific sound is currently playing or not (if id is provided), or check if at least one of the sounds in the group is playing or not.
     * @param  {Number}  id The sound id to check. If none is passed, the whole sound group is checked.
     * @return {Boolean} True if playing and false if not.
     */
    playing: function(id) {
      var self = this;

      // Check the passed sound ID (if any).
      if (typeof id === 'number') {
        var sound = self._soundById(id);
        return sound ? !sound._paused : false;
      }

      // Otherwise, loop through all sounds and check if any are playing.
      for (var i=0; i<self._sounds.length; i++) {
        if (!self._sounds[i]._paused) {
          return true;
        }
      }

      return false;
    },

    /**
     * Get the duration of this sound. Passing a sound id will return the sprite duration.
     * @param  {Number} id The sound id to check. If none is passed, return full source duration.
     * @return {Number} Audio duration in seconds.
     */
    duration: function(id) {
      var self = this;
      var duration = self._duration;

      // If we pass an ID, get the sound and return the sprite length.
      var sound = self._soundById(id);
      if (sound) {
        duration = self._sprite[sound._sprite][1] / 1000;
      }

      return duration;
    },

    /**
     * Returns the current loaded state of this Howl.
     * @return {String} 'unloaded', 'loading', 'loaded'
     */
    state: function() {
      return this._state;
    },

    /**
     * Unload and destroy the current Howl object.
     * This will immediately stop all sound instances attached to this group.
     */
    unload: function() {
      var self = this;

      // Stop playing any active sounds.
      var sounds = self._sounds;
      for (var i=0; i<sounds.length; i++) {
        // Stop the sound if it is currently playing.
        if (!sounds[i]._paused) {
          self.stop(sounds[i]._id);
        }

        // Remove the source or disconnect.
        if (!self._webAudio) {
          // Set the source to 0-second silence to stop any downloading (except in IE).
          self._clearSound(sounds[i]._node);

          // Remove any event listeners.
          sounds[i]._node.removeEventListener('error', sounds[i]._errorFn, false);
          sounds[i]._node.removeEventListener(Howler._canPlayEvent, sounds[i]._loadFn, false);

          // Release the Audio object back to the pool.
          Howler._releaseHtml5Audio(sounds[i]._node);
        }

        // Empty out all of the nodes.
        delete sounds[i]._node;

        // Make sure all timers are cleared out.
        self._clearTimer(sounds[i]._id);
      }

      // Remove the references in the global Howler object.
      var index = Howler._howls.indexOf(self);
      if (index >= 0) {
        Howler._howls.splice(index, 1);
      }

      // Delete this sound from the cache (if no other Howl is using it).
      var remCache = true;
      for (i=0; i<Howler._howls.length; i++) {
        if (Howler._howls[i]._src === self._src || self._src.indexOf(Howler._howls[i]._src) >= 0) {
          remCache = false;
          break;
        }
      }

      if (cache && remCache) {
        delete cache[self._src];
      }

      // Clear global errors.
      Howler.noAudio = false;

      // Clear out `self`.
      self._state = 'unloaded';
      self._sounds = [];
      self = null;

      return null;
    },

    /**
     * Listen to a custom event.
     * @param  {String}   event Event name.
     * @param  {Function} fn    Listener to call.
     * @param  {Number}   id    (optional) Only listen to events for this sound.
     * @param  {Number}   once  (INTERNAL) Marks event to fire only once.
     * @return {Howl}
     */
    on: function(event, fn, id, once) {
      var self = this;
      var events = self['_on' + event];

      if (typeof fn === 'function') {
        events.push(once ? {id: id, fn: fn, once: once} : {id: id, fn: fn});
      }

      return self;
    },

    /**
     * Remove a custom event. Call without parameters to remove all events.
     * @param  {String}   event Event name.
     * @param  {Function} fn    Listener to remove. Leave empty to remove all.
     * @param  {Number}   id    (optional) Only remove events for this sound.
     * @return {Howl}
     */
    off: function(event, fn, id) {
      var self = this;
      var events = self['_on' + event];
      var i = 0;

      // Allow passing just an event and ID.
      if (typeof fn === 'number') {
        id = fn;
        fn = null;
      }

      if (fn || id) {
        // Loop through event store and remove the passed function.
        for (i=0; i<events.length; i++) {
          var isId = (id === events[i].id);
          if (fn === events[i].fn && isId || !fn && isId) {
            events.splice(i, 1);
            break;
          }
        }
      } else if (event) {
        // Clear out all events of this type.
        self['_on' + event] = [];
      } else {
        // Clear out all events of every type.
        var keys = Object.keys(self);
        for (i=0; i<keys.length; i++) {
          if ((keys[i].indexOf('_on') === 0) && Array.isArray(self[keys[i]])) {
            self[keys[i]] = [];
          }
        }
      }

      return self;
    },

    /**
     * Listen to a custom event and remove it once fired.
     * @param  {String}   event Event name.
     * @param  {Function} fn    Listener to call.
     * @param  {Number}   id    (optional) Only listen to events for this sound.
     * @return {Howl}
     */
    once: function(event, fn, id) {
      var self = this;

      // Setup the event listener.
      self.on(event, fn, id, 1);

      return self;
    },

    /**
     * Emit all events of a specific type and pass the sound id.
     * @param  {String} event Event name.
     * @param  {Number} id    Sound ID.
     * @param  {Number} msg   Message to go with event.
     * @return {Howl}
     */
    _emit: function(event, id, msg) {
      var self = this;
      var events = self['_on' + event];

      // Loop through event store and fire all functions.
      for (var i=events.length-1; i>=0; i--) {
        // Only fire the listener if the correct ID is used.
        if (!events[i].id || events[i].id === id || event === 'load') {
          setTimeout(function(fn) {
            fn.call(this, id, msg);
          }.bind(self, events[i].fn), 0);

          // If this event was setup with `once`, remove it.
          if (events[i].once) {
            self.off(event, events[i].fn, events[i].id);
          }
        }
      }

      // Pass the event type into load queue so that it can continue stepping.
      self._loadQueue(event);

      return self;
    },

    /**
     * Queue of actions initiated before the sound has loaded.
     * These will be called in sequence, with the next only firing
     * after the previous has finished executing (even if async like play).
     * @return {Howl}
     */
    _loadQueue: function(event) {
      var self = this;

      if (self._queue.length > 0) {
        var task = self._queue[0];

        // Remove this task if a matching event was passed.
        if (task.event === event) {
          self._queue.shift();
          self._loadQueue();
        }

        // Run the task if no event type is passed.
        if (!event) {
          task.action();
        }
      }

      return self;
    },

    /**
     * Fired when playback ends at the end of the duration.
     * @param  {Sound} sound The sound object to work with.
     * @return {Howl}
     */
    _ended: function(sound) {
      var self = this;
      var sprite = sound._sprite;

      // If we are using IE and there was network latency we may be clipping
      // audio before it completes playing. Lets check the node to make sure it
      // believes it has completed, before ending the playback.
      if (!self._webAudio && sound._node && !sound._node.paused && !sound._node.ended && sound._node.currentTime < sound._stop) {
        setTimeout(self._ended.bind(self, sound), 100);
        return self;
      }

      // Should this sound loop?
      var loop = !!(sound._loop || self._sprite[sprite][2]);

      // Fire the ended event.
      self._emit('end', sound._id);

      // Restart the playback for HTML5 Audio loop.
      if (!self._webAudio && loop) {
        self.stop(sound._id, true).play(sound._id);
      }

      // Restart this timer if on a Web Audio loop.
      if (self._webAudio && loop) {
        self._emit('play', sound._id);
        sound._seek = sound._start || 0;
        sound._rateSeek = 0;
        sound._playStart = Howler.ctx.currentTime;

        var timeout = ((sound._stop - sound._start) * 1000) / Math.abs(sound._rate);
        self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);
      }

      // Mark the node as paused.
      if (self._webAudio && !loop) {
        sound._paused = true;
        sound._ended = true;
        sound._seek = sound._start || 0;
        sound._rateSeek = 0;
        self._clearTimer(sound._id);

        // Clean up the buffer source.
        self._cleanBuffer(sound._node);

        // Attempt to auto-suspend AudioContext if no sounds are still playing.
        Howler._autoSuspend();
      }

      // When using a sprite, end the track.
      if (!self._webAudio && !loop) {
        self.stop(sound._id, true);
      }

      return self;
    },

    /**
     * Clear the end timer for a sound playback.
     * @param  {Number} id The sound ID.
     * @return {Howl}
     */
    _clearTimer: function(id) {
      var self = this;

      if (self._endTimers[id]) {
        // Clear the timeout or remove the ended listener.
        if (typeof self._endTimers[id] !== 'function') {
          clearTimeout(self._endTimers[id]);
        } else {
          var sound = self._soundById(id);
          if (sound && sound._node) {
            sound._node.removeEventListener('ended', self._endTimers[id], false);
          }
        }

        delete self._endTimers[id];
      }

      return self;
    },

    /**
     * Return the sound identified by this ID, or return null.
     * @param  {Number} id Sound ID
     * @return {Object}    Sound object or null.
     */
    _soundById: function(id) {
      var self = this;

      // Loop through all sounds and find the one with this ID.
      for (var i=0; i<self._sounds.length; i++) {
        if (id === self._sounds[i]._id) {
          return self._sounds[i];
        }
      }

      return null;
    },

    /**
     * Return an inactive sound from the pool or create a new one.
     * @return {Sound} Sound playback object.
     */
    _inactiveSound: function() {
      var self = this;

      self._drain();

      // Find the first inactive node to recycle.
      for (var i=0; i<self._sounds.length; i++) {
        if (self._sounds[i]._ended) {
          return self._sounds[i].reset();
        }
      }

      // If no inactive node was found, create a new one.
      return new Sound(self);
    },

    /**
     * Drain excess inactive sounds from the pool.
     */
    _drain: function() {
      var self = this;
      var limit = self._pool;
      var cnt = 0;
      var i = 0;

      // If there are less sounds than the max pool size, we are done.
      if (self._sounds.length < limit) {
        return;
      }

      // Count the number of inactive sounds.
      for (i=0; i<self._sounds.length; i++) {
        if (self._sounds[i]._ended) {
          cnt++;
        }
      }

      // Remove excess inactive sounds, going in reverse order.
      for (i=self._sounds.length - 1; i>=0; i--) {
        if (cnt <= limit) {
          return;
        }

        if (self._sounds[i]._ended) {
          // Disconnect the audio source when using Web Audio.
          if (self._webAudio && self._sounds[i]._node) {
            self._sounds[i]._node.disconnect(0);
          }

          // Remove sounds until we have the pool size.
          self._sounds.splice(i, 1);
          cnt--;
        }
      }
    },

    /**
     * Get all ID's from the sounds pool.
     * @param  {Number} id Only return one ID if one is passed.
     * @return {Array}    Array of IDs.
     */
    _getSoundIds: function(id) {
      var self = this;

      if (typeof id === 'undefined') {
        var ids = [];
        for (var i=0; i<self._sounds.length; i++) {
          ids.push(self._sounds[i]._id);
        }

        return ids;
      } else {
        return [id];
      }
    },

    /**
     * Load the sound back into the buffer source.
     * @param  {Sound} sound The sound object to work with.
     * @return {Howl}
     */
    _refreshBuffer: function(sound) {
      var self = this;

      // Setup the buffer source for playback.
      sound._node.bufferSource = Howler.ctx.createBufferSource();
      sound._node.bufferSource.buffer = cache[self._src];

      // Connect to the correct node.
      if (sound._panner) {
        sound._node.bufferSource.connect(sound._panner);
      } else {
        sound._node.bufferSource.connect(sound._node);
      }

      // Setup looping and playback rate.
      sound._node.bufferSource.loop = sound._loop;
      if (sound._loop) {
        sound._node.bufferSource.loopStart = sound._start || 0;
        sound._node.bufferSource.loopEnd = sound._stop || 0;
      }
      sound._node.bufferSource.playbackRate.setValueAtTime(sound._rate, Howler.ctx.currentTime);

      return self;
    },

    /**
     * Prevent memory leaks by cleaning up the buffer source after playback.
     * @param  {Object} node Sound's audio node containing the buffer source.
     * @return {Howl}
     */
    _cleanBuffer: function(node) {
      var self = this;
      var isIOS = Howler._navigator && Howler._navigator.vendor.indexOf('Apple') >= 0;

      if (Howler._scratchBuffer && node.bufferSource) {
        node.bufferSource.onended = null;
        node.bufferSource.disconnect(0);
        if (isIOS) {
          try { node.bufferSource.buffer = Howler._scratchBuffer; } catch(e) {}
        }
      }
      node.bufferSource = null;

      return self;
    },

    /**
     * Set the source to a 0-second silence to stop any downloading (except in IE).
     * @param  {Object} node Audio node to clear.
     */
    _clearSound: function(node) {
      var checkIE = /MSIE |Trident\//.test(Howler._navigator && Howler._navigator.userAgent);
      if (!checkIE) {
        node.src = 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA';
      }
    }
  };

  /** Single Sound Methods **/
  /***************************************************************************/

  /**
   * Setup the sound object, which each node attached to a Howl group is contained in.
   * @param {Object} howl The Howl parent group.
   */
  var Sound = function(howl) {
    this._parent = howl;
    this.init();
  };
  Sound.prototype = {
    /**
     * Initialize a new Sound object.
     * @return {Sound}
     */
    init: function() {
      var self = this;
      var parent = self._parent;

      // Setup the default parameters.
      self._muted = parent._muted;
      self._loop = parent._loop;
      self._volume = parent._volume;
      self._rate = parent._rate;
      self._seek = 0;
      self._paused = true;
      self._ended = true;
      self._sprite = '__default';

      // Generate a unique ID for this sound.
      self._id = ++Howler._counter;

      // Add itself to the parent's pool.
      parent._sounds.push(self);

      // Create the new node.
      self.create();

      return self;
    },

    /**
     * Create and setup a new sound object, whether HTML5 Audio or Web Audio.
     * @return {Sound}
     */
    create: function() {
      var self = this;
      var parent = self._parent;
      var volume = (Howler._muted || self._muted || self._parent._muted) ? 0 : self._volume;

      if (parent._webAudio) {
        // Create the gain node for controlling volume (the source will connect to this).
        self._node = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain();
        self._node.gain.setValueAtTime(volume, Howler.ctx.currentTime);
        self._node.paused = true;
        self._node.connect(Howler.masterGain);
      } else if (!Howler.noAudio) {
        // Get an unlocked Audio object from the pool.
        self._node = Howler._obtainHtml5Audio();

        // Listen for errors (http://dev.w3.org/html5/spec-author-view/spec.html#mediaerror).
        self._errorFn = self._errorListener.bind(self);
        self._node.addEventListener('error', self._errorFn, false);

        // Listen for 'canplaythrough' event to let us know the sound is ready.
        self._loadFn = self._loadListener.bind(self);
        self._node.addEventListener(Howler._canPlayEvent, self._loadFn, false);

        // Setup the new audio node.
        self._node.src = parent._src;
        self._node.preload = 'auto';
        self._node.volume = volume * Howler.volume();

        // Begin loading the source.
        self._node.load();
      }

      return self;
    },

    /**
     * Reset the parameters of this sound to the original state (for recycle).
     * @return {Sound}
     */
    reset: function() {
      var self = this;
      var parent = self._parent;

      // Reset all of the parameters of this sound.
      self._muted = parent._muted;
      self._loop = parent._loop;
      self._volume = parent._volume;
      self._rate = parent._rate;
      self._seek = 0;
      self._rateSeek = 0;
      self._paused = true;
      self._ended = true;
      self._sprite = '__default';

      // Generate a new ID so that it isn't confused with the previous sound.
      self._id = ++Howler._counter;

      return self;
    },

    /**
     * HTML5 Audio error listener callback.
     */
    _errorListener: function() {
      var self = this;

      // Fire an error event and pass back the code.
      self._parent._emit('loaderror', self._id, self._node.error ? self._node.error.code : 0);

      // Clear the event listener.
      self._node.removeEventListener('error', self._errorFn, false);
    },

    /**
     * HTML5 Audio canplaythrough listener callback.
     */
    _loadListener: function() {
      var self = this;
      var parent = self._parent;

      // Round up the duration to account for the lower precision in HTML5 Audio.
      parent._duration = Math.ceil(self._node.duration * 10) / 10;

      // Setup a sprite if none is defined.
      if (Object.keys(parent._sprite).length === 0) {
        parent._sprite = {__default: [0, parent._duration * 1000]};
      }

      if (parent._state !== 'loaded') {
        parent._state = 'loaded';
        parent._emit('load');
        parent._loadQueue();
      }

      // Clear the event listener.
      self._node.removeEventListener(Howler._canPlayEvent, self._loadFn, false);
    }
  };

  /** Helper Methods **/
  /***************************************************************************/

  var cache = {};

  /**
   * Buffer a sound from URL, Data URI or cache and decode to audio source (Web Audio API).
   * @param  {Howl} self
   */
  var loadBuffer = function(self) {
    var url = self._src;

    // Check if the buffer has already been cached and use it instead.
    if (cache[url]) {
      // Set the duration from the cache.
      self._duration = cache[url].duration;

      // Load the sound into this Howl.
      loadSound(self);

      return;
    }

    if (/^data:[^;]+;base64,/.test(url)) {
      // Decode the base64 data URI without XHR, since some browsers don't support it.
      var data = atob(url.split(',')[1]);
      var dataView = new Uint8Array(data.length);
      for (var i=0; i<data.length; ++i) {
        dataView[i] = data.charCodeAt(i);
      }

      decodeAudioData(dataView.buffer, self);
    } else {
      // Load the buffer from the URL.
      var xhr = new XMLHttpRequest();
      xhr.open('GET', url, true);
      xhr.withCredentials = self._xhrWithCredentials;
      xhr.responseType = 'arraybuffer';
      xhr.onload = function() {
        // Make sure we get a successful response back.
        var code = (xhr.status + '')[0];
        if (code !== '0' && code !== '2' && code !== '3') {
          self._emit('loaderror', null, 'Failed loading audio file with status: ' + xhr.status + '.');
          return;
        }

        decodeAudioData(xhr.response, self);
      };
      xhr.onerror = function() {
        // If there is an error, switch to HTML5 Audio.
        if (self._webAudio) {
          self._html5 = true;
          self._webAudio = false;
          self._sounds = [];
          delete cache[url];
          self.load();
        }
      };
      safeXhrSend(xhr);
    }
  };

  /**
   * Send the XHR request wrapped in a try/catch.
   * @param  {Object} xhr XHR to send.
   */
  var safeXhrSend = function(xhr) {
    try {
      xhr.send();
    } catch (e) {
      xhr.onerror();
    }
  };

  /**
   * Decode audio data from an array buffer.
   * @param  {ArrayBuffer} arraybuffer The audio data.
   * @param  {Howl}        self
   */
  var decodeAudioData = function(arraybuffer, self) {
    // Fire a load error if something broke.
    var error = function() {
      self._emit('loaderror', null, 'Decoding audio data failed.');
    };

    // Load the sound on success.
    var success = function(buffer) {
      if (buffer && self._sounds.length > 0) {
        cache[self._src] = buffer;
        loadSound(self, buffer);
      } else {
        error();
      }
    };

    // Decode the buffer into an audio source.
    if (typeof Promise !== 'undefined' && Howler.ctx.decodeAudioData.length === 1) {
      Howler.ctx.decodeAudioData(arraybuffer).then(success).catch(error);
    } else {
      Howler.ctx.decodeAudioData(arraybuffer, success, error);
    }
  }

  /**
   * Sound is now loaded, so finish setting everything up and fire the loaded event.
   * @param  {Howl} self
   * @param  {Object} buffer The decoded buffer sound source.
   */
  var loadSound = function(self, buffer) {
    // Set the duration.
    if (buffer && !self._duration) {
      self._duration = buffer.duration;
    }

    // Setup a sprite if none is defined.
    if (Object.keys(self._sprite).length === 0) {
      self._sprite = {__default: [0, self._duration * 1000]};
    }

    // Fire the loaded event.
    if (self._state !== 'loaded') {
      self._state = 'loaded';
      self._emit('load');
      self._loadQueue();
    }
  };

  /**
   * Setup the audio context when available, or switch to HTML5 Audio mode.
   */
  var setupAudioContext = function() {
    // If we have already detected that Web Audio isn't supported, don't run this step again.
    if (!Howler.usingWebAudio) {
      return;
    }

    // Check if we are using Web Audio and setup the AudioContext if we are.
    try {
      if (typeof AudioContext !== 'undefined') {
        Howler.ctx = new AudioContext();
      } else if (typeof webkitAudioContext !== 'undefined') {
        Howler.ctx = new webkitAudioContext();
      } else {
        Howler.usingWebAudio = false;
      }
    } catch(e) {
      Howler.usingWebAudio = false;
    }

    // If the audio context creation still failed, set using web audio to false.
    if (!Howler.ctx) {
      Howler.usingWebAudio = false;
    }

    // Check if a webview is being used on iOS8 or earlier (rather than the browser).
    // If it is, disable Web Audio as it causes crashing.
    var iOS = (/iP(hone|od|ad)/.test(Howler._navigator && Howler._navigator.platform));
    var appVersion = Howler._navigator && Howler._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
    var version = appVersion ? parseInt(appVersion[1], 10) : null;
    if (iOS && version && version < 9) {
      var safari = /safari/.test(Howler._navigator && Howler._navigator.userAgent.toLowerCase());
      if (Howler._navigator && Howler._navigator.standalone && !safari || Howler._navigator && !Howler._navigator.standalone && !safari) {
        Howler.usingWebAudio = false;
      }
    }

    // Create and expose the master GainNode when using Web Audio (useful for plugins or advanced usage).
    if (Howler.usingWebAudio) {
      Howler.masterGain = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain();
      Howler.masterGain.gain.setValueAtTime(Howler._muted ? 0 : Howler._volume, Howler.ctx.currentTime);
      Howler.masterGain.connect(Howler.ctx.destination);
    }

    // Re-run the setup on Howler.
    Howler._setup();
  };

  // Add support for AMD (Asynchronous Module Definition) libraries such as require.js.
  if (true) {
    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
      return {
        Howler: Howler,
        Howl: Howl
      };
    }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  }

  // Add support for CommonJS libraries such as browserify.
  if (true) {
    exports.Howler = Howler;
    exports.Howl = Howl;
  }

  // Define globally in case AMD is not available or unused.
  if (typeof window !== 'undefined') {
    window.HowlerGlobal = HowlerGlobal;
    window.Howler = Howler;
    window.Howl = Howl;
    window.Sound = Sound;
  } else if (typeof global !== 'undefined') { // Add to global in Node.js (for testing, etc).
    global.HowlerGlobal = HowlerGlobal;
    global.Howler = Howler;
    global.Howl = Howl;
    global.Sound = Sound;
  }
})();


/*!
 *  Spatial Plugin - Adds support for stereo and 3D audio where Web Audio is supported.
 *  
 *  howler.js v2.1.3
 *  howlerjs.com
 *
 *  (c) 2013-2019, James Simpson of GoldFire Studios
 *  goldfirestudios.com
 *
 *  MIT License
 */

(function() {

  'use strict';

  // Setup default properties.
  HowlerGlobal.prototype._pos = [0, 0, 0];
  HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0];

  /** Global Methods **/
  /***************************************************************************/

  /**
   * Helper method to update the stereo panning position of all current Howls.
   * Future Howls will not use this value unless explicitly set.
   * @param  {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.
   * @return {Howler/Number}     Self or current stereo panning value.
   */
  HowlerGlobal.prototype.stereo = function(pan) {
    var self = this;

    // Stop right here if not using Web Audio.
    if (!self.ctx || !self.ctx.listener) {
      return self;
    }

    // Loop through all Howls and update their stereo panning.
    for (var i=self._howls.length-1; i>=0; i--) {
      self._howls[i].stereo(pan);
    }

    return self;
  };

  /**
   * Get/set the position of the listener in 3D cartesian space. Sounds using
   * 3D position will be relative to the listener's position.
   * @param  {Number} x The x-position of the listener.
   * @param  {Number} y The y-position of the listener.
   * @param  {Number} z The z-position of the listener.
   * @return {Howler/Array}   Self or current listener position.
   */
  HowlerGlobal.prototype.pos = function(x, y, z) {
    var self = this;

    // Stop right here if not using Web Audio.
    if (!self.ctx || !self.ctx.listener) {
      return self;
    }

    // Set the defaults for optional 'y' & 'z'.
    y = (typeof y !== 'number') ? self._pos[1] : y;
    z = (typeof z !== 'number') ? self._pos[2] : z;

    if (typeof x === 'number') {
      self._pos = [x, y, z];

      if (typeof self.ctx.listener.positionX !== 'undefined') {
        self.ctx.listener.positionX.setTargetAtTime(self._pos[0], Howler.ctx.currentTime, 0.1);
        self.ctx.listener.positionY.setTargetAtTime(self._pos[1], Howler.ctx.currentTime, 0.1);
        self.ctx.listener.positionZ.setTargetAtTime(self._pos[2], Howler.ctx.currentTime, 0.1);
      } else {
        self.ctx.listener.setPosition(self._pos[0], self._pos[1], self._pos[2]);
      }
    } else {
      return self._pos;
    }

    return self;
  };

  /**
   * Get/set the direction the listener is pointing in the 3D cartesian space.
   * A front and up vector must be provided. The front is the direction the
   * face of the listener is pointing, and up is the direction the top of the
   * listener is pointing. Thus, these values are expected to be at right angles
   * from each other.
   * @param  {Number} x   The x-orientation of the listener.
   * @param  {Number} y   The y-orientation of the listener.
   * @param  {Number} z   The z-orientation of the listener.
   * @param  {Number} xUp The x-orientation of the top of the listener.
   * @param  {Number} yUp The y-orientation of the top of the listener.
   * @param  {Number} zUp The z-orientation of the top of the listener.
   * @return {Howler/Array}     Returns self or the current orientation vectors.
   */
  HowlerGlobal.prototype.orientation = function(x, y, z, xUp, yUp, zUp) {
    var self = this;

    // Stop right here if not using Web Audio.
    if (!self.ctx || !self.ctx.listener) {
      return self;
    }

    // Set the defaults for optional 'y' & 'z'.
    var or = self._orientation;
    y = (typeof y !== 'number') ? or[1] : y;
    z = (typeof z !== 'number') ? or[2] : z;
    xUp = (typeof xUp !== 'number') ? or[3] : xUp;
    yUp = (typeof yUp !== 'number') ? or[4] : yUp;
    zUp = (typeof zUp !== 'number') ? or[5] : zUp;

    if (typeof x === 'number') {
      self._orientation = [x, y, z, xUp, yUp, zUp];

      if (typeof self.ctx.listener.forwardX !== 'undefined') {
        self.ctx.listener.forwardX.setTargetAtTime(x, Howler.ctx.currentTime, 0.1);
        self.ctx.listener.forwardY.setTargetAtTime(y, Howler.ctx.currentTime, 0.1);
        self.ctx.listener.forwardZ.setTargetAtTime(z, Howler.ctx.currentTime, 0.1);
        self.ctx.listener.upX.setTargetAtTime(xUp, Howler.ctx.currentTime, 0.1);
        self.ctx.listener.upY.setTargetAtTime(yUp, Howler.ctx.currentTime, 0.1);
        self.ctx.listener.upZ.setTargetAtTime(zUp, Howler.ctx.currentTime, 0.1);
      } else {
        self.ctx.listener.setOrientation(x, y, z, xUp, yUp, zUp);
      }
    } else {
      return or;
    }

    return self;
  };

  /** Group Methods **/
  /***************************************************************************/

  /**
   * Add new properties to the core init.
   * @param  {Function} _super Core init method.
   * @return {Howl}
   */
  Howl.prototype.init = (function(_super) {
    return function(o) {
      var self = this;

      // Setup user-defined default properties.
      self._orientation = o.orientation || [1, 0, 0];
      self._stereo = o.stereo || null;
      self._pos = o.pos || null;
      self._pannerAttr = {
        coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : 360,
        coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : 360,
        coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : 0,
        distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : 'inverse',
        maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : 10000,
        panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : 'HRTF',
        refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : 1,
        rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : 1
      };

      // Setup event listeners.
      self._onstereo = o.onstereo ? [{fn: o.onstereo}] : [];
      self._onpos = o.onpos ? [{fn: o.onpos}] : [];
      self._onorientation = o.onorientation ? [{fn: o.onorientation}] : [];

      // Complete initilization with howler.js core's init function.
      return _super.call(this, o);
    };
  })(Howl.prototype.init);

  /**
   * Get/set the stereo panning of the audio source for this sound or all in the group.
   * @param  {Number} pan  A value of -1.0 is all the way left and 1.0 is all the way right.
   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
   * @return {Howl/Number}    Returns self or the current stereo panning value.
   */
  Howl.prototype.stereo = function(pan, id) {
    var self = this;

    // Stop right here if not using Web Audio.
    if (!self._webAudio) {
      return self;
    }

    // If the sound hasn't loaded, add it to the load queue to change stereo pan when capable.
    if (self._state !== 'loaded') {
      self._queue.push({
        event: 'stereo',
        action: function() {
          self.stereo(pan, id);
        }
      });

      return self;
    }

    // Check for PannerStereoNode support and fallback to PannerNode if it doesn't exist.
    var pannerType = (typeof Howler.ctx.createStereoPanner === 'undefined') ? 'spatial' : 'stereo';

    // Setup the group's stereo panning if no ID is passed.
    if (typeof id === 'undefined') {
      // Return the group's stereo panning if no parameters are passed.
      if (typeof pan === 'number') {
        self._stereo = pan;
        self._pos = [pan, 0, 0];
      } else {
        return self._stereo;
      }
    }

    // Change the streo panning of one or all sounds in group.
    var ids = self._getSoundIds(id);
    for (var i=0; i<ids.length; i++) {
      // Get the sound.
      var sound = self._soundById(ids[i]);

      if (sound) {
        if (typeof pan === 'number') {
          sound._stereo = pan;
          sound._pos = [pan, 0, 0];

          if (sound._node) {
            // If we are falling back, make sure the panningModel is equalpower.
            sound._pannerAttr.panningModel = 'equalpower';

            // Check if there is a panner setup and create a new one if not.
            if (!sound._panner || !sound._panner.pan) {
              setupPanner(sound, pannerType);
            }

            if (pannerType === 'spatial') {
              if (typeof sound._panner.positionX !== 'undefined') {
                sound._panner.positionX.setValueAtTime(pan, Howler.ctx.currentTime);
                sound._panner.positionY.setValueAtTime(0, Howler.ctx.currentTime);
                sound._panner.positionZ.setValueAtTime(0, Howler.ctx.currentTime);
              } else {
                sound._panner.setPosition(pan, 0, 0);
              }
            } else {
              sound._panner.pan.setValueAtTime(pan, Howler.ctx.currentTime);
            }
          }

          self._emit('stereo', sound._id);
        } else {
          return sound._stereo;
        }
      }
    }

    return self;
  };

  /**
   * Get/set the 3D spatial position of the audio source for this sound or group relative to the global listener.
   * @param  {Number} x  The x-position of the audio source.
   * @param  {Number} y  The y-position of the audio source.
   * @param  {Number} z  The z-position of the audio source.
   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
   * @return {Howl/Array}    Returns self or the current 3D spatial position: [x, y, z].
   */
  Howl.prototype.pos = function(x, y, z, id) {
    var self = this;

    // Stop right here if not using Web Audio.
    if (!self._webAudio) {
      return self;
    }

    // If the sound hasn't loaded, add it to the load queue to change position when capable.
    if (self._state !== 'loaded') {
      self._queue.push({
        event: 'pos',
        action: function() {
          self.pos(x, y, z, id);
        }
      });

      return self;
    }

    // Set the defaults for optional 'y' & 'z'.
    y = (typeof y !== 'number') ? 0 : y;
    z = (typeof z !== 'number') ? -0.5 : z;

    // Setup the group's spatial position if no ID is passed.
    if (typeof id === 'undefined') {
      // Return the group's spatial position if no parameters are passed.
      if (typeof x === 'number') {
        self._pos = [x, y, z];
      } else {
        return self._pos;
      }
    }

    // Change the spatial position of one or all sounds in group.
    var ids = self._getSoundIds(id);
    for (var i=0; i<ids.length; i++) {
      // Get the sound.
      var sound = self._soundById(ids[i]);

      if (sound) {
        if (typeof x === 'number') {
          sound._pos = [x, y, z];

          if (sound._node) {
            // Check if there is a panner setup and create a new one if not.
            if (!sound._panner || sound._panner.pan) {
              setupPanner(sound, 'spatial');
            }

            if (typeof sound._panner.positionX !== 'undefined') {
              sound._panner.positionX.setValueAtTime(x, Howler.ctx.currentTime);
              sound._panner.positionY.setValueAtTime(y, Howler.ctx.currentTime);
              sound._panner.positionZ.setValueAtTime(z, Howler.ctx.currentTime);
            } else {
              sound._panner.setPosition(x, y, z);
            }
          }

          self._emit('pos', sound._id);
        } else {
          return sound._pos;
        }
      }
    }

    return self;
  };

  /**
   * Get/set the direction the audio source is pointing in the 3D cartesian coordinate
   * space. Depending on how direction the sound is, based on the `cone` attributes,
   * a sound pointing away from the listener can be quiet or silent.
   * @param  {Number} x  The x-orientation of the source.
   * @param  {Number} y  The y-orientation of the source.
   * @param  {Number} z  The z-orientation of the source.
   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
   * @return {Howl/Array}    Returns self or the current 3D spatial orientation: [x, y, z].
   */
  Howl.prototype.orientation = function(x, y, z, id) {
    var self = this;

    // Stop right here if not using Web Audio.
    if (!self._webAudio) {
      return self;
    }

    // If the sound hasn't loaded, add it to the load queue to change orientation when capable.
    if (self._state !== 'loaded') {
      self._queue.push({
        event: 'orientation',
        action: function() {
          self.orientation(x, y, z, id);
        }
      });

      return self;
    }

    // Set the defaults for optional 'y' & 'z'.
    y = (typeof y !== 'number') ? self._orientation[1] : y;
    z = (typeof z !== 'number') ? self._orientation[2] : z;

    // Setup the group's spatial orientation if no ID is passed.
    if (typeof id === 'undefined') {
      // Return the group's spatial orientation if no parameters are passed.
      if (typeof x === 'number') {
        self._orientation = [x, y, z];
      } else {
        return self._orientation;
      }
    }

    // Change the spatial orientation of one or all sounds in group.
    var ids = self._getSoundIds(id);
    for (var i=0; i<ids.length; i++) {
      // Get the sound.
      var sound = self._soundById(ids[i]);

      if (sound) {
        if (typeof x === 'number') {
          sound._orientation = [x, y, z];

          if (sound._node) {
            // Check if there is a panner setup and create a new one if not.
            if (!sound._panner) {
              // Make sure we have a position to setup the node with.
              if (!sound._pos) {
                sound._pos = self._pos || [0, 0, -0.5];
              }

              setupPanner(sound, 'spatial');
            }

            if (typeof sound._panner.orientationX !== 'undefined') {
              sound._panner.orientationX.setValueAtTime(x, Howler.ctx.currentTime);
              sound._panner.orientationY.setValueAtTime(y, Howler.ctx.currentTime);
              sound._panner.orientationZ.setValueAtTime(z, Howler.ctx.currentTime);
            } else {
              sound._panner.setOrientation(x, y, z);
            }
          }

          self._emit('orientation', sound._id);
        } else {
          return sound._orientation;
        }
      }
    }

    return self;
  };

  /**
   * Get/set the panner node's attributes for a sound or group of sounds.
   * This method can optionall take 0, 1 or 2 arguments.
   *   pannerAttr() -> Returns the group's values.
   *   pannerAttr(id) -> Returns the sound id's values.
   *   pannerAttr(o) -> Set's the values of all sounds in this Howl group.
   *   pannerAttr(o, id) -> Set's the values of passed sound id.
   *
   *   Attributes:
   *     coneInnerAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,
   *                      inside of which there will be no volume reduction.
   *     coneOuterAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,
   *                      outside of which the volume will be reduced to a constant value of `coneOuterGain`.
   *     coneOuterGain - (0 by default) A parameter for directional audio sources, this is the gain outside of the
   *                     `coneOuterAngle`. It is a linear value in the range `[0, 1]`.
   *     distanceModel - ('inverse' by default) Determines algorithm used to reduce volume as audio moves away from
   *                     listener. Can be `linear`, `inverse` or `exponential.
   *     maxDistance - (10000 by default) The maximum distance between source and listener, after which the volume
   *                   will not be reduced any further.
   *     refDistance - (1 by default) A reference distance for reducing volume as source moves further from the listener.
   *                   This is simply a variable of the distance model and has a different effect depending on which model
   *                   is used and the scale of your coordinates. Generally, volume will be equal to 1 at this distance.
   *     rolloffFactor - (1 by default) How quickly the volume reduces as source moves from listener. This is simply a
   *                     variable of the distance model and can be in the range of `[0, 1]` with `linear` and `[0, ∞]`
   *                     with `inverse` and `exponential`.
   *     panningModel - ('HRTF' by default) Determines which spatialization algorithm is used to position audio.
   *                     Can be `HRTF` or `equalpower`.
   *
   * @return {Howl/Object} Returns self or current panner attributes.
   */
  Howl.prototype.pannerAttr = function() {
    var self = this;
    var args = arguments;
    var o, id, sound;

    // Stop right here if not using Web Audio.
    if (!self._webAudio) {
      return self;
    }

    // Determine the values based on arguments.
    if (args.length === 0) {
      // Return the group's panner attribute values.
      return self._pannerAttr;
    } else if (args.length === 1) {
      if (typeof args[0] === 'object') {
        o = args[0];

        // Set the grou's panner attribute values.
        if (typeof id === 'undefined') {
          if (!o.pannerAttr) {
            o.pannerAttr = {
              coneInnerAngle: o.coneInnerAngle,
              coneOuterAngle: o.coneOuterAngle,
              coneOuterGain: o.coneOuterGain,
              distanceModel: o.distanceModel,
              maxDistance: o.maxDistance,
              refDistance: o.refDistance,
              rolloffFactor: o.rolloffFactor,
              panningModel: o.panningModel
            };
          }

          self._pannerAttr = {
            coneInnerAngle: typeof o.pannerAttr.coneInnerAngle !== 'undefined' ? o.pannerAttr.coneInnerAngle : self._coneInnerAngle,
            coneOuterAngle: typeof o.pannerAttr.coneOuterAngle !== 'undefined' ? o.pannerAttr.coneOuterAngle : self._coneOuterAngle,
            coneOuterGain: typeof o.pannerAttr.coneOuterGain !== 'undefined' ? o.pannerAttr.coneOuterGain : self._coneOuterGain,
            distanceModel: typeof o.pannerAttr.distanceModel !== 'undefined' ? o.pannerAttr.distanceModel : self._distanceModel,
            maxDistance: typeof o.pannerAttr.maxDistance !== 'undefined' ? o.pannerAttr.maxDistance : self._maxDistance,
            refDistance: typeof o.pannerAttr.refDistance !== 'undefined' ? o.pannerAttr.refDistance : self._refDistance,
            rolloffFactor: typeof o.pannerAttr.rolloffFactor !== 'undefined' ? o.pannerAttr.rolloffFactor : self._rolloffFactor,
            panningModel: typeof o.pannerAttr.panningModel !== 'undefined' ? o.pannerAttr.panningModel : self._panningModel
          };
        }
      } else {
        // Return this sound's panner attribute values.
        sound = self._soundById(parseInt(args[0], 10));
        return sound ? sound._pannerAttr : self._pannerAttr;
      }
    } else if (args.length === 2) {
      o = args[0];
      id = parseInt(args[1], 10);
    }

    // Update the values of the specified sounds.
    var ids = self._getSoundIds(id);
    for (var i=0; i<ids.length; i++) {
      sound = self._soundById(ids[i]);

      if (sound) {
        // Merge the new values into the sound.
        var pa = sound._pannerAttr;
        pa = {
          coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : pa.coneInnerAngle,
          coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : pa.coneOuterAngle,
          coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : pa.coneOuterGain,
          distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : pa.distanceModel,
          maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : pa.maxDistance,
          refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : pa.refDistance,
          rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : pa.rolloffFactor,
          panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : pa.panningModel
        };

        // Update the panner values or create a new panner if none exists.
        var panner = sound._panner;
        if (panner) {
          panner.coneInnerAngle = pa.coneInnerAngle;
          panner.coneOuterAngle = pa.coneOuterAngle;
          panner.coneOuterGain = pa.coneOuterGain;
          panner.distanceModel = pa.distanceModel;
          panner.maxDistance = pa.maxDistance;
          panner.refDistance = pa.refDistance;
          panner.rolloffFactor = pa.rolloffFactor;
          panner.panningModel = pa.panningModel;
        } else {
          // Make sure we have a position to setup the node with.
          if (!sound._pos) {
            sound._pos = self._pos || [0, 0, -0.5];
          }

          // Create a new panner node.
          setupPanner(sound, 'spatial');
        }
      }
    }

    return self;
  };

  /** Single Sound Methods **/
  /***************************************************************************/

  /**
   * Add new properties to the core Sound init.
   * @param  {Function} _super Core Sound init method.
   * @return {Sound}
   */
  Sound.prototype.init = (function(_super) {
    return function() {
      var self = this;
      var parent = self._parent;

      // Setup user-defined default properties.
      self._orientation = parent._orientation;
      self._stereo = parent._stereo;
      self._pos = parent._pos;
      self._pannerAttr = parent._pannerAttr;

      // Complete initilization with howler.js core Sound's init function.
      _super.call(this);

      // If a stereo or position was specified, set it up.
      if (self._stereo) {
        parent.stereo(self._stereo);
      } else if (self._pos) {
        parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);
      }
    };
  })(Sound.prototype.init);

  /**
   * Override the Sound.reset method to clean up properties from the spatial plugin.
   * @param  {Function} _super Sound reset method.
   * @return {Sound}
   */
  Sound.prototype.reset = (function(_super) {
    return function() {
      var self = this;
      var parent = self._parent;

      // Reset all spatial plugin properties on this sound.
      self._orientation = parent._orientation;
      self._stereo = parent._stereo;
      self._pos = parent._pos;
      self._pannerAttr = parent._pannerAttr;

      // If a stereo or position was specified, set it up.
      if (self._stereo) {
        parent.stereo(self._stereo);
      } else if (self._pos) {
        parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);
      } else if (self._panner) {
        // Disconnect the panner.
        self._panner.disconnect(0);
        self._panner = undefined;
        parent._refreshBuffer(self);
      }

      // Complete resetting of the sound.
      return _super.call(this);
    };
  })(Sound.prototype.reset);

  /** Helper Methods **/
  /***************************************************************************/

  /**
   * Create a new panner node and save it on the sound.
   * @param  {Sound} sound Specific sound to setup panning on.
   * @param {String} type Type of panner to create: 'stereo' or 'spatial'.
   */
  var setupPanner = function(sound, type) {
    type = type || 'spatial';

    // Create the new panner node.
    if (type === 'spatial') {
      sound._panner = Howler.ctx.createPanner();
      sound._panner.coneInnerAngle = sound._pannerAttr.coneInnerAngle;
      sound._panner.coneOuterAngle = sound._pannerAttr.coneOuterAngle;
      sound._panner.coneOuterGain = sound._pannerAttr.coneOuterGain;
      sound._panner.distanceModel = sound._pannerAttr.distanceModel;
      sound._panner.maxDistance = sound._pannerAttr.maxDistance;
      sound._panner.refDistance = sound._pannerAttr.refDistance;
      sound._panner.rolloffFactor = sound._pannerAttr.rolloffFactor;
      sound._panner.panningModel = sound._pannerAttr.panningModel;

      if (typeof sound._panner.positionX !== 'undefined') {
        sound._panner.positionX.setValueAtTime(sound._pos[0], Howler.ctx.currentTime);
        sound._panner.positionY.setValueAtTime(sound._pos[1], Howler.ctx.currentTime);
        sound._panner.positionZ.setValueAtTime(sound._pos[2], Howler.ctx.currentTime);
      } else {
        sound._panner.setPosition(sound._pos[0], sound._pos[1], sound._pos[2]);
      }

      if (typeof sound._panner.orientationX !== 'undefined') {
        sound._panner.orientationX.setValueAtTime(sound._orientation[0], Howler.ctx.currentTime);
        sound._panner.orientationY.setValueAtTime(sound._orientation[1], Howler.ctx.currentTime);
        sound._panner.orientationZ.setValueAtTime(sound._orientation[2], Howler.ctx.currentTime);
      } else {
        sound._panner.setOrientation(sound._orientation[0], sound._orientation[1], sound._orientation[2]);
      }
    } else {
      sound._panner = Howler.ctx.createStereoPanner();
      sound._panner.pan.setValueAtTime(sound._stereo, Howler.ctx.currentTime);
    }

    sound._panner.connect(sound._node);

    // Update the connections.
    if (!sound._paused) {
      sound._parent.pause(sound._id, true).play(sound._id, true);
    }
  };
})();

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../gulp/node_modules/webpack/buildin/global.js */ "./gulp/node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/json-schema-traverse/index.js":
/*!****************************************************!*\
  !*** ./node_modules/json-schema-traverse/index.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var traverse = module.exports = function (schema, opts, cb) {
  // Legacy support for v0.3.1 and earlier.
  if (typeof opts == 'function') {
    cb = opts;
    opts = {};
  }

  cb = opts.cb || cb;
  var pre = (typeof cb == 'function') ? cb : cb.pre || function() {};
  var post = cb.post || function() {};

  _traverse(opts, pre, post, schema, '', schema);
};


traverse.keywords = {
  additionalItems: true,
  items: true,
  contains: true,
  additionalProperties: true,
  propertyNames: true,
  not: true
};

traverse.arrayKeywords = {
  items: true,
  allOf: true,
  anyOf: true,
  oneOf: true
};

traverse.propsKeywords = {
  definitions: true,
  properties: true,
  patternProperties: true,
  dependencies: true
};

traverse.skipKeywords = {
  default: true,
  enum: true,
  const: true,
  required: true,
  maximum: true,
  minimum: true,
  exclusiveMaximum: true,
  exclusiveMinimum: true,
  multipleOf: true,
  maxLength: true,
  minLength: true,
  pattern: true,
  format: true,
  maxItems: true,
  minItems: true,
  uniqueItems: true,
  maxProperties: true,
  minProperties: true
};


function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
  if (schema && typeof schema == 'object' && !Array.isArray(schema)) {
    pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
    for (var key in schema) {
      var sch = schema[key];
      if (Array.isArray(sch)) {
        if (key in traverse.arrayKeywords) {
          for (var i=0; i<sch.length; i++)
            _traverse(opts, pre, post, sch[i], jsonPtr + '/' + key + '/' + i, rootSchema, jsonPtr, key, schema, i);
        }
      } else if (key in traverse.propsKeywords) {
        if (sch && typeof sch == 'object') {
          for (var prop in sch)
            _traverse(opts, pre, post, sch[prop], jsonPtr + '/' + key + '/' + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop);
        }
      } else if (key in traverse.keywords || (opts.allKeys && !(key in traverse.skipKeywords))) {
        _traverse(opts, pre, post, sch, jsonPtr + '/' + key, rootSchema, jsonPtr, key, schema);
      }
    }
    post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
  }
}


function escapeJsonPtr(str) {
  return str.replace(/~/g, '~0').replace(/\//g, '~1');
}


/***/ }),

/***/ "./node_modules/query-string/index.js":
/*!********************************************!*\
  !*** ./node_modules/query-string/index.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

const strictUriEncode = __webpack_require__(/*! strict-uri-encode */ "./node_modules/strict-uri-encode/index.js");
const decodeComponent = __webpack_require__(/*! decode-uri-component */ "./node_modules/decode-uri-component/index.js");
const splitOnFirst = __webpack_require__(/*! split-on-first */ "./node_modules/split-on-first/index.js");

const isNullOrUndefined = value => value === null || value === undefined;

function encoderForArrayFormat(options) {
	switch (options.arrayFormat) {
		case 'index':
			return key => (result, value) => {
				const index = result.length;

				if (
					value === undefined ||
					(options.skipNull && value === null) ||
					(options.skipEmptyString && value === '')
				) {
					return result;
				}

				if (value === null) {
					return [...result, [encode(key, options), '[', index, ']'].join('')];
				}

				return [
					...result,
					[encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join('')
				];
			};

		case 'bracket':
			return key => (result, value) => {
				if (
					value === undefined ||
					(options.skipNull && value === null) ||
					(options.skipEmptyString && value === '')
				) {
					return result;
				}

				if (value === null) {
					return [...result, [encode(key, options), '[]'].join('')];
				}

				return [...result, [encode(key, options), '[]=', encode(value, options)].join('')];
			};

		case 'comma':
		case 'separator':
			return key => (result, value) => {
				if (value === null || value === undefined || value.length === 0) {
					return result;
				}

				if (result.length === 0) {
					return [[encode(key, options), '=', encode(value, options)].join('')];
				}

				return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
			};

		default:
			return key => (result, value) => {
				if (
					value === undefined ||
					(options.skipNull && value === null) ||
					(options.skipEmptyString && value === '')
				) {
					return result;
				}

				if (value === null) {
					return [...result, encode(key, options)];
				}

				return [...result, [encode(key, options), '=', encode(value, options)].join('')];
			};
	}
}

function parserForArrayFormat(options) {
	let result;

	switch (options.arrayFormat) {
		case 'index':
			return (key, value, accumulator) => {
				result = /\[(\d*)\]$/.exec(key);

				key = key.replace(/\[\d*\]$/, '');

				if (!result) {
					accumulator[key] = value;
					return;
				}

				if (accumulator[key] === undefined) {
					accumulator[key] = {};
				}

				accumulator[key][result[1]] = value;
			};

		case 'bracket':
			return (key, value, accumulator) => {
				result = /(\[\])$/.exec(key);
				key = key.replace(/\[\]$/, '');

				if (!result) {
					accumulator[key] = value;
					return;
				}

				if (accumulator[key] === undefined) {
					accumulator[key] = [value];
					return;
				}

				accumulator[key] = [].concat(accumulator[key], value);
			};

		case 'comma':
		case 'separator':
			return (key, value, accumulator) => {
				const isArray = typeof value === 'string' && value.split('').indexOf(options.arrayFormatSeparator) > -1;
				const newValue = isArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options);
				accumulator[key] = newValue;
			};

		default:
			return (key, value, accumulator) => {
				if (accumulator[key] === undefined) {
					accumulator[key] = value;
					return;
				}

				accumulator[key] = [].concat(accumulator[key], value);
			};
	}
}

function validateArrayFormatSeparator(value) {
	if (typeof value !== 'string' || value.length !== 1) {
		throw new TypeError('arrayFormatSeparator must be single character string');
	}
}

function encode(value, options) {
	if (options.encode) {
		return options.strict ? strictUriEncode(value) : encodeURIComponent(value);
	}

	return value;
}

function decode(value, options) {
	if (options.decode) {
		return decodeComponent(value);
	}

	return value;
}

function keysSorter(input) {
	if (Array.isArray(input)) {
		return input.sort();
	}

	if (typeof input === 'object') {
		return keysSorter(Object.keys(input))
			.sort((a, b) => Number(a) - Number(b))
			.map(key => input[key]);
	}

	return input;
}

function removeHash(input) {
	const hashStart = input.indexOf('#');
	if (hashStart !== -1) {
		input = input.slice(0, hashStart);
	}

	return input;
}

function getHash(url) {
	let hash = '';
	const hashStart = url.indexOf('#');
	if (hashStart !== -1) {
		hash = url.slice(hashStart);
	}

	return hash;
}

function extract(input) {
	input = removeHash(input);
	const queryStart = input.indexOf('?');
	if (queryStart === -1) {
		return '';
	}

	return input.slice(queryStart + 1);
}

function parseValue(value, options) {
	if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) {
		value = Number(value);
	} else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
		value = value.toLowerCase() === 'true';
	}

	return value;
}

function parse(input, options) {
	options = Object.assign({
		decode: true,
		sort: true,
		arrayFormat: 'none',
		arrayFormatSeparator: ',',
		parseNumbers: false,
		parseBooleans: false
	}, options);

	validateArrayFormatSeparator(options.arrayFormatSeparator);

	const formatter = parserForArrayFormat(options);

	// Create an object with no prototype
	const ret = Object.create(null);

	if (typeof input !== 'string') {
		return ret;
	}

	input = input.trim().replace(/^[?#&]/, '');

	if (!input) {
		return ret;
	}

	for (const param of input.split('&')) {
		let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '=');

		// Missing `=` should be `null`:
		// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
		value = value === undefined ? null : ['comma', 'separator'].includes(options.arrayFormat) ? value : decode(value, options);
		formatter(decode(key, options), value, ret);
	}

	for (const key of Object.keys(ret)) {
		const value = ret[key];
		if (typeof value === 'object' && value !== null) {
			for (const k of Object.keys(value)) {
				value[k] = parseValue(value[k], options);
			}
		} else {
			ret[key] = parseValue(value, options);
		}
	}

	if (options.sort === false) {
		return ret;
	}

	return (options.sort === true ? Object.keys(ret).sort() : Object.keys(ret).sort(options.sort)).reduce((result, key) => {
		const value = ret[key];
		if (Boolean(value) && typeof value === 'object' && !Array.isArray(value)) {
			// Sort object keys, not values
			result[key] = keysSorter(value);
		} else {
			result[key] = value;
		}

		return result;
	}, Object.create(null));
}

exports.extract = extract;
exports.parse = parse;

exports.stringify = (object, options) => {
	if (!object) {
		return '';
	}

	options = Object.assign({
		encode: true,
		strict: true,
		arrayFormat: 'none',
		arrayFormatSeparator: ','
	}, options);

	validateArrayFormatSeparator(options.arrayFormatSeparator);

	const shouldFilter = key => (
		(options.skipNull && isNullOrUndefined(object[key])) ||
		(options.skipEmptyString && object[key] === '')
	);

	const formatter = encoderForArrayFormat(options);

	const objectCopy = {};

	for (const key of Object.keys(object)) {
		if (!shouldFilter(key)) {
			objectCopy[key] = object[key];
		}
	}

	const keys = Object.keys(objectCopy);

	if (options.sort !== false) {
		keys.sort(options.sort);
	}

	return keys.map(key => {
		const value = object[key];

		if (value === undefined) {
			return '';
		}

		if (value === null) {
			return encode(key, options);
		}

		if (Array.isArray(value)) {
			return value
				.reduce(formatter(key), [])
				.join('&');
		}

		return encode(key, options) + '=' + encode(value, options);
	}).filter(x => x.length > 0).join('&');
};

exports.parseUrl = (input, options) => {
	return {
		url: removeHash(input).split('?')[0] || '',
		query: parse(extract(input), options)
	};
};

exports.stringifyUrl = (input, options) => {
	const url = removeHash(input.url).split('?')[0] || '';
	const queryFromUrl = exports.extract(input.url);
	const parsedQueryFromUrl = exports.parse(queryFromUrl);
	const hash = getHash(input.url);
	const query = Object.assign(parsedQueryFromUrl, input.query);
	let queryString = exports.stringify(query, options);
	if (queryString) {
		queryString = `?${queryString}`;
	}

	return `${url}${queryString}${hash}`;
};


/***/ }),

/***/ "./node_modules/rusha/dist/rusha.js":
/*!******************************************!*\
  !*** ./node_modules/rusha/dist/rusha.js ***!
  \******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

(function webpackUniversalModuleDefinition(root, factory) {
	if(true)
		module.exports = factory();
	else {}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/* eslint-env commonjs, browser */

var RushaCore = __webpack_require__(5);

var _require = __webpack_require__(1),
    toHex = _require.toHex,
    ceilHeapSize = _require.ceilHeapSize;

var conv = __webpack_require__(6);

// Calculate the length of buffer that the sha1 routine uses
// including the padding.
var padlen = function (len) {
  for (len += 9; len % 64 > 0; len += 1) {}
  return len;
};

var padZeroes = function (bin, len) {
  var h8 = new Uint8Array(bin.buffer);
  var om = len % 4,
      align = len - om;
  switch (om) {
    case 0:
      h8[align + 3] = 0;
    case 1:
      h8[align + 2] = 0;
    case 2:
      h8[align + 1] = 0;
    case 3:
      h8[align + 0] = 0;
  }
  for (var i = (len >> 2) + 1; i < bin.length; i++) {
    bin[i] = 0;
  }
};

var padData = function (bin, chunkLen, msgLen) {
  bin[chunkLen >> 2] |= 0x80 << 24 - (chunkLen % 4 << 3);
  // To support msgLen >= 2 GiB, use a float division when computing the
  // high 32-bits of the big-endian message length in bits.
  bin[((chunkLen >> 2) + 2 & ~0x0f) + 14] = msgLen / (1 << 29) | 0;
  bin[((chunkLen >> 2) + 2 & ~0x0f) + 15] = msgLen << 3;
};

var getRawDigest = function (heap, padMaxChunkLen) {
  var io = new Int32Array(heap, padMaxChunkLen + 320, 5);
  var out = new Int32Array(5);
  var arr = new DataView(out.buffer);
  arr.setInt32(0, io[0], false);
  arr.setInt32(4, io[1], false);
  arr.setInt32(8, io[2], false);
  arr.setInt32(12, io[3], false);
  arr.setInt32(16, io[4], false);
  return out;
};

var Rusha = function () {
  function Rusha(chunkSize) {
    _classCallCheck(this, Rusha);

    chunkSize = chunkSize || 64 * 1024;
    if (chunkSize % 64 > 0) {
      throw new Error('Chunk size must be a multiple of 128 bit');
    }
    this._offset = 0;
    this._maxChunkLen = chunkSize;
    this._padMaxChunkLen = padlen(chunkSize);
    // The size of the heap is the sum of:
    // 1. The padded input message size
    // 2. The extended space the algorithm needs (320 byte)
    // 3. The 160 bit state the algoritm uses
    this._heap = new ArrayBuffer(ceilHeapSize(this._padMaxChunkLen + 320 + 20));
    this._h32 = new Int32Array(this._heap);
    this._h8 = new Int8Array(this._heap);
    this._core = new RushaCore({ Int32Array: Int32Array }, {}, this._heap);
  }

  Rusha.prototype._initState = function _initState(heap, padMsgLen) {
    this._offset = 0;
    var io = new Int32Array(heap, padMsgLen + 320, 5);
    io[0] = 1732584193;
    io[1] = -271733879;
    io[2] = -1732584194;
    io[3] = 271733878;
    io[4] = -1009589776;
  };

  Rusha.prototype._padChunk = function _padChunk(chunkLen, msgLen) {
    var padChunkLen = padlen(chunkLen);
    var view = new Int32Array(this._heap, 0, padChunkLen >> 2);
    padZeroes(view, chunkLen);
    padData(view, chunkLen, msgLen);
    return padChunkLen;
  };

  Rusha.prototype._write = function _write(data, chunkOffset, chunkLen, off) {
    conv(data, this._h8, this._h32, chunkOffset, chunkLen, off || 0);
  };

  Rusha.prototype._coreCall = function _coreCall(data, chunkOffset, chunkLen, msgLen, finalize) {
    var padChunkLen = chunkLen;
    this._write(data, chunkOffset, chunkLen);
    if (finalize) {
      padChunkLen = this._padChunk(chunkLen, msgLen);
    }
    this._core.hash(padChunkLen, this._padMaxChunkLen);
  };

  Rusha.prototype.rawDigest = function rawDigest(str) {
    var msgLen = str.byteLength || str.length || str.size || 0;
    this._initState(this._heap, this._padMaxChunkLen);
    var chunkOffset = 0,
        chunkLen = this._maxChunkLen;
    for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) {
      this._coreCall(str, chunkOffset, chunkLen, msgLen, false);
    }
    this._coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true);
    return getRawDigest(this._heap, this._padMaxChunkLen);
  };

  Rusha.prototype.digest = function digest(str) {
    return toHex(this.rawDigest(str).buffer);
  };

  Rusha.prototype.digestFromString = function digestFromString(str) {
    return this.digest(str);
  };

  Rusha.prototype.digestFromBuffer = function digestFromBuffer(str) {
    return this.digest(str);
  };

  Rusha.prototype.digestFromArrayBuffer = function digestFromArrayBuffer(str) {
    return this.digest(str);
  };

  Rusha.prototype.resetState = function resetState() {
    this._initState(this._heap, this._padMaxChunkLen);
    return this;
  };

  Rusha.prototype.append = function append(chunk) {
    var chunkOffset = 0;
    var chunkLen = chunk.byteLength || chunk.length || chunk.size || 0;
    var turnOffset = this._offset % this._maxChunkLen;
    var inputLen = void 0;

    this._offset += chunkLen;
    while (chunkOffset < chunkLen) {
      inputLen = Math.min(chunkLen - chunkOffset, this._maxChunkLen - turnOffset);
      this._write(chunk, chunkOffset, inputLen, turnOffset);
      turnOffset += inputLen;
      chunkOffset += inputLen;
      if (turnOffset === this._maxChunkLen) {
        this._core.hash(this._maxChunkLen, this._padMaxChunkLen);
        turnOffset = 0;
      }
    }
    return this;
  };

  Rusha.prototype.getState = function getState() {
    var turnOffset = this._offset % this._maxChunkLen;
    var heap = void 0;
    if (!turnOffset) {
      var io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5);
      heap = io.buffer.slice(io.byteOffset, io.byteOffset + io.byteLength);
    } else {
      heap = this._heap.slice(0);
    }
    return {
      offset: this._offset,
      heap: heap
    };
  };

  Rusha.prototype.setState = function setState(state) {
    this._offset = state.offset;
    if (state.heap.byteLength === 20) {
      var io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5);
      io.set(new Int32Array(state.heap));
    } else {
      this._h32.set(new Int32Array(state.heap));
    }
    return this;
  };

  Rusha.prototype.rawEnd = function rawEnd() {
    var msgLen = this._offset;
    var chunkLen = msgLen % this._maxChunkLen;
    var padChunkLen = this._padChunk(chunkLen, msgLen);
    this._core.hash(padChunkLen, this._padMaxChunkLen);
    var result = getRawDigest(this._heap, this._padMaxChunkLen);
    this._initState(this._heap, this._padMaxChunkLen);
    return result;
  };

  Rusha.prototype.end = function end() {
    return toHex(this.rawEnd().buffer);
  };

  return Rusha;
}();

module.exports = Rusha;
module.exports._core = RushaCore;

/***/ }),
/* 1 */
/***/ (function(module, exports) {

/* eslint-env commonjs, browser */

//
// toHex
//

var precomputedHex = new Array(256);
for (var i = 0; i < 256; i++) {
  precomputedHex[i] = (i < 0x10 ? '0' : '') + i.toString(16);
}

module.exports.toHex = function (arrayBuffer) {
  var binarray = new Uint8Array(arrayBuffer);
  var res = new Array(arrayBuffer.byteLength);
  for (var _i = 0; _i < res.length; _i++) {
    res[_i] = precomputedHex[binarray[_i]];
  }
  return res.join('');
};

//
// ceilHeapSize
//

module.exports.ceilHeapSize = function (v) {
  // The asm.js spec says:
  // The heap object's byteLength must be either
  // 2^n for n in [12, 24) or 2^24 * n for n ≥ 1.
  // Also, byteLengths smaller than 2^16 are deprecated.
  var p = 0;
  // If v is smaller than 2^16, the smallest possible solution
  // is 2^16.
  if (v <= 65536) return 65536;
  // If v < 2^24, we round up to 2^n,
  // otherwise we round up to 2^24 * n.
  if (v < 16777216) {
    for (p = 1; p < v; p = p << 1) {}
  } else {
    for (p = 16777216; p < v; p += 16777216) {}
  }
  return p;
};

//
// isDedicatedWorkerScope
//

module.exports.isDedicatedWorkerScope = function (self) {
  var isRunningInWorker = 'WorkerGlobalScope' in self && self instanceof self.WorkerGlobalScope;
  var isRunningInSharedWorker = 'SharedWorkerGlobalScope' in self && self instanceof self.SharedWorkerGlobalScope;
  var isRunningInServiceWorker = 'ServiceWorkerGlobalScope' in self && self instanceof self.ServiceWorkerGlobalScope;

  // Detects whether we run inside a dedicated worker or not.
  //
  // We can't just check for `DedicatedWorkerGlobalScope`, since IE11
  // has a bug where it only supports `WorkerGlobalScope`.
  //
  // Therefore, we consider us as running inside a dedicated worker
  // when we are running inside a worker, but not in a shared or service worker.
  //
  // When new types of workers are introduced, we will need to adjust this code.
  return isRunningInWorker && !isRunningInSharedWorker && !isRunningInServiceWorker;
};

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

/* eslint-env commonjs, worker */

module.exports = function () {
  var Rusha = __webpack_require__(0);

  var hashData = function (hasher, data, cb) {
    try {
      return cb(null, hasher.digest(data));
    } catch (e) {
      return cb(e);
    }
  };

  var hashFile = function (hasher, readTotal, blockSize, file, cb) {
    var reader = new self.FileReader();
    reader.onloadend = function onloadend() {
      if (reader.error) {
        return cb(reader.error);
      }
      var buffer = reader.result;
      readTotal += reader.result.byteLength;
      try {
        hasher.append(buffer);
      } catch (e) {
        cb(e);
        return;
      }
      if (readTotal < file.size) {
        hashFile(hasher, readTotal, blockSize, file, cb);
      } else {
        cb(null, hasher.end());
      }
    };
    reader.readAsArrayBuffer(file.slice(readTotal, readTotal + blockSize));
  };

  var workerBehaviourEnabled = true;

  self.onmessage = function (event) {
    if (!workerBehaviourEnabled) {
      return;
    }

    var data = event.data.data,
        file = event.data.file,
        id = event.data.id;
    if (typeof id === 'undefined') return;
    if (!file && !data) return;
    var blockSize = event.data.blockSize || 4 * 1024 * 1024;
    var hasher = new Rusha(blockSize);
    hasher.resetState();
    var done = function (err, hash) {
      if (!err) {
        self.postMessage({ id: id, hash: hash });
      } else {
        self.postMessage({ id: id, error: err.name });
      }
    };
    if (data) hashData(hasher, data, done);
    if (file) hashFile(hasher, 0, blockSize, file, done);
  };

  return function () {
    workerBehaviourEnabled = false;
  };
};

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

/* eslint-env commonjs, browser */

var work = __webpack_require__(4);
var Rusha = __webpack_require__(0);
var createHash = __webpack_require__(7);
var runWorker = __webpack_require__(2);

var _require = __webpack_require__(1),
    isDedicatedWorkerScope = _require.isDedicatedWorkerScope;

var isRunningInDedicatedWorker = typeof self !== 'undefined' && isDedicatedWorkerScope(self);

Rusha.disableWorkerBehaviour = isRunningInDedicatedWorker ? runWorker() : function () {};

Rusha.createWorker = function () {
  var worker = work(/*require.resolve*/(2));
  var terminate = worker.terminate;
  worker.terminate = function () {
    URL.revokeObjectURL(worker.objectURL);
    terminate.call(worker);
  };
  return worker;
};

Rusha.createHash = createHash;

module.exports = Rusha;

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

function webpackBootstrapFunc (modules) {
/******/  // The module cache
/******/  var installedModules = {};

/******/  // The require function
/******/  function __webpack_require__(moduleId) {

/******/    // Check if module is in cache
/******/    if(installedModules[moduleId])
/******/      return installedModules[moduleId].exports;

/******/    // Create a new module (and put it into the cache)
/******/    var module = installedModules[moduleId] = {
/******/      i: moduleId,
/******/      l: false,
/******/      exports: {}
/******/    };

/******/    // Execute the module function
/******/    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/    // Flag the module as loaded
/******/    module.l = true;

/******/    // Return the exports of the module
/******/    return module.exports;
/******/  }

/******/  // expose the modules object (__webpack_modules__)
/******/  __webpack_require__.m = modules;

/******/  // expose the module cache
/******/  __webpack_require__.c = installedModules;

/******/  // identity function for calling harmony imports with the correct context
/******/  __webpack_require__.i = function(value) { return value; };

/******/  // define getter function for harmony exports
/******/  __webpack_require__.d = function(exports, name, getter) {
/******/    if(!__webpack_require__.o(exports, name)) {
/******/      Object.defineProperty(exports, name, {
/******/        configurable: false,
/******/        enumerable: true,
/******/        get: getter
/******/      });
/******/    }
/******/  };

/******/  // define __esModule on exports
/******/  __webpack_require__.r = function(exports) {
/******/    Object.defineProperty(exports, '__esModule', { value: true });
/******/  };

/******/  // getDefaultExport function for compatibility with non-harmony modules
/******/  __webpack_require__.n = function(module) {
/******/    var getter = module && module.__esModule ?
/******/      function getDefault() { return module['default']; } :
/******/      function getModuleExports() { return module; };
/******/    __webpack_require__.d(getter, 'a', getter);
/******/    return getter;
/******/  };

/******/  // Object.prototype.hasOwnProperty.call
/******/  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

/******/  // __webpack_public_path__
/******/  __webpack_require__.p = "/";

/******/  // on error function for async loading
/******/  __webpack_require__.oe = function(err) { console.error(err); throw err; };

  var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE)
  return f.default || f // try to call default if defined to also support babel esmodule exports
}

var moduleNameReqExp = '[\\.|\\-|\\+|\\w|\/|@]+'
var dependencyRegExp = '\\((\/\\*.*?\\*\/)?\s?.*?(' + moduleNameReqExp + ').*?\\)' // additional chars when output.pathinfo is true

// http://stackoverflow.com/a/2593661/130442
function quoteRegExp (str) {
  return (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&')
}

function getModuleDependencies (sources, module, queueName) {
  var retval = {}
  retval[queueName] = []

  var fnString = module.toString()
  var wrapperSignature = fnString.match(/^function\s?\(\w+,\s*\w+,\s*(\w+)\)/)
  if (!wrapperSignature) return retval
  var webpackRequireName = wrapperSignature[1]

  // main bundle deps
  var re = new RegExp('(\\\\n|\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g')
  var match
  while ((match = re.exec(fnString))) {
    if (match[3] === 'dll-reference') continue
    retval[queueName].push(match[3])
  }

  // dll deps
  re = new RegExp('\\(' + quoteRegExp(webpackRequireName) + '\\("(dll-reference\\s(' + moduleNameReqExp + '))"\\)\\)' + dependencyRegExp, 'g')
  while ((match = re.exec(fnString))) {
    if (!sources[match[2]]) {
      retval[queueName].push(match[1])
      sources[match[2]] = __webpack_require__(match[1]).m
    }
    retval[match[2]] = retval[match[2]] || []
    retval[match[2]].push(match[4])
  }

  return retval
}

function hasValuesInQueues (queues) {
  var keys = Object.keys(queues)
  return keys.reduce(function (hasValues, key) {
    return hasValues || queues[key].length > 0
  }, false)
}

function getRequiredModules (sources, moduleId) {
  var modulesQueue = {
    main: [moduleId]
  }
  var requiredModules = {
    main: []
  }
  var seenModules = {
    main: {}
  }

  while (hasValuesInQueues(modulesQueue)) {
    var queues = Object.keys(modulesQueue)
    for (var i = 0; i < queues.length; i++) {
      var queueName = queues[i]
      var queue = modulesQueue[queueName]
      var moduleToCheck = queue.pop()
      seenModules[queueName] = seenModules[queueName] || {}
      if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue
      seenModules[queueName][moduleToCheck] = true
      requiredModules[queueName] = requiredModules[queueName] || []
      requiredModules[queueName].push(moduleToCheck)
      var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName)
      var newModulesKeys = Object.keys(newModules)
      for (var j = 0; j < newModulesKeys.length; j++) {
        modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || []
        modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]])
      }
    }
  }

  return requiredModules
}

module.exports = function (moduleId, options) {
  options = options || {}
  var sources = {
    main: __webpack_require__.m
  }

  var requiredModules = options.all ? { main: Object.keys(sources) } : getRequiredModules(sources, moduleId)

  var src = ''

  Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) {
    var entryModule = 0
    while (requiredModules[module][entryModule]) {
      entryModule++
    }
    requiredModules[module].push(entryModule)
    sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })'
    src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\n'
  })

  src = src + '(' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '})(self);'

  var blob = new window.Blob([src], { type: 'text/javascript' })
  if (options.bare) { return blob }

  var URL = window.URL || window.webkitURL || window.mozURL || window.msURL

  var workerUrl = URL.createObjectURL(blob)
  var worker = new window.Worker(workerUrl)
  worker.objectURL = workerUrl

  return worker
}


/***/ }),
/* 5 */
/***/ (function(module, exports) {

// The low-level RushCore module provides the heart of Rusha,
// a high-speed sha1 implementation working on an Int32Array heap.
// At first glance, the implementation seems complicated, however
// with the SHA1 spec at hand, it is obvious this almost a textbook
// implementation that has a few functions hand-inlined and a few loops
// hand-unrolled.
module.exports = function RushaCore(stdlib$846, foreign$847, heap$848) {
    'use asm';
    var H$849 = new stdlib$846.Int32Array(heap$848);
    function hash$850(k$851, x$852) {
        // k in bytes
        k$851 = k$851 | 0;
        x$852 = x$852 | 0;
        var i$853 = 0, j$854 = 0, y0$855 = 0, z0$856 = 0, y1$857 = 0, z1$858 = 0, y2$859 = 0, z2$860 = 0, y3$861 = 0, z3$862 = 0, y4$863 = 0, z4$864 = 0, t0$865 = 0, t1$866 = 0;
        y0$855 = H$849[x$852 + 320 >> 2] | 0;
        y1$857 = H$849[x$852 + 324 >> 2] | 0;
        y2$859 = H$849[x$852 + 328 >> 2] | 0;
        y3$861 = H$849[x$852 + 332 >> 2] | 0;
        y4$863 = H$849[x$852 + 336 >> 2] | 0;
        for (i$853 = 0; (i$853 | 0) < (k$851 | 0); i$853 = i$853 + 64 | 0) {
            z0$856 = y0$855;
            z1$858 = y1$857;
            z2$860 = y2$859;
            z3$862 = y3$861;
            z4$864 = y4$863;
            for (j$854 = 0; (j$854 | 0) < 64; j$854 = j$854 + 4 | 0) {
                t1$866 = H$849[i$853 + j$854 >> 2] | 0;
                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 & y2$859 | ~y1$857 & y3$861) | 0) + ((t1$866 + y4$863 | 0) + 1518500249 | 0) | 0;
                y4$863 = y3$861;
                y3$861 = y2$859;
                y2$859 = y1$857 << 30 | y1$857 >>> 2;
                y1$857 = y0$855;
                y0$855 = t0$865;
                H$849[k$851 + j$854 >> 2] = t1$866;
            }
            for (j$854 = k$851 + 64 | 0; (j$854 | 0) < (k$851 + 80 | 0); j$854 = j$854 + 4 | 0) {
                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;
                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 & y2$859 | ~y1$857 & y3$861) | 0) + ((t1$866 + y4$863 | 0) + 1518500249 | 0) | 0;
                y4$863 = y3$861;
                y3$861 = y2$859;
                y2$859 = y1$857 << 30 | y1$857 >>> 2;
                y1$857 = y0$855;
                y0$855 = t0$865;
                H$849[j$854 >> 2] = t1$866;
            }
            for (j$854 = k$851 + 80 | 0; (j$854 | 0) < (k$851 + 160 | 0); j$854 = j$854 + 4 | 0) {
                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;
                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 ^ y2$859 ^ y3$861) | 0) + ((t1$866 + y4$863 | 0) + 1859775393 | 0) | 0;
                y4$863 = y3$861;
                y3$861 = y2$859;
                y2$859 = y1$857 << 30 | y1$857 >>> 2;
                y1$857 = y0$855;
                y0$855 = t0$865;
                H$849[j$854 >> 2] = t1$866;
            }
            for (j$854 = k$851 + 160 | 0; (j$854 | 0) < (k$851 + 240 | 0); j$854 = j$854 + 4 | 0) {
                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;
                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 & y2$859 | y1$857 & y3$861 | y2$859 & y3$861) | 0) + ((t1$866 + y4$863 | 0) - 1894007588 | 0) | 0;
                y4$863 = y3$861;
                y3$861 = y2$859;
                y2$859 = y1$857 << 30 | y1$857 >>> 2;
                y1$857 = y0$855;
                y0$855 = t0$865;
                H$849[j$854 >> 2] = t1$866;
            }
            for (j$854 = k$851 + 240 | 0; (j$854 | 0) < (k$851 + 320 | 0); j$854 = j$854 + 4 | 0) {
                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;
                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 ^ y2$859 ^ y3$861) | 0) + ((t1$866 + y4$863 | 0) - 899497514 | 0) | 0;
                y4$863 = y3$861;
                y3$861 = y2$859;
                y2$859 = y1$857 << 30 | y1$857 >>> 2;
                y1$857 = y0$855;
                y0$855 = t0$865;
                H$849[j$854 >> 2] = t1$866;
            }
            y0$855 = y0$855 + z0$856 | 0;
            y1$857 = y1$857 + z1$858 | 0;
            y2$859 = y2$859 + z2$860 | 0;
            y3$861 = y3$861 + z3$862 | 0;
            y4$863 = y4$863 + z4$864 | 0;
        }
        H$849[x$852 + 320 >> 2] = y0$855;
        H$849[x$852 + 324 >> 2] = y1$857;
        H$849[x$852 + 328 >> 2] = y2$859;
        H$849[x$852 + 332 >> 2] = y3$861;
        H$849[x$852 + 336 >> 2] = y4$863;
    }
    return { hash: hash$850 };
};

/***/ }),
/* 6 */
/***/ (function(module, exports) {

var _this = this;

/* eslint-env commonjs, browser */

var reader = void 0;
if (typeof self !== 'undefined' && typeof self.FileReaderSync !== 'undefined') {
  reader = new self.FileReaderSync();
}

// Convert a binary string and write it to the heap.
// A binary string is expected to only contain char codes < 256.
var convStr = function (str, H8, H32, start, len, off) {
  var i = void 0,
      om = off % 4,
      lm = (len + om) % 4,
      j = len - lm;
  switch (om) {
    case 0:
      H8[off] = str.charCodeAt(start + 3);
    case 1:
      H8[off + 1 - (om << 1) | 0] = str.charCodeAt(start + 2);
    case 2:
      H8[off + 2 - (om << 1) | 0] = str.charCodeAt(start + 1);
    case 3:
      H8[off + 3 - (om << 1) | 0] = str.charCodeAt(start);
  }
  if (len < lm + (4 - om)) {
    return;
  }
  for (i = 4 - om; i < j; i = i + 4 | 0) {
    H32[off + i >> 2] = str.charCodeAt(start + i) << 24 | str.charCodeAt(start + i + 1) << 16 | str.charCodeAt(start + i + 2) << 8 | str.charCodeAt(start + i + 3);
  }
  switch (lm) {
    case 3:
      H8[off + j + 1 | 0] = str.charCodeAt(start + j + 2);
    case 2:
      H8[off + j + 2 | 0] = str.charCodeAt(start + j + 1);
    case 1:
      H8[off + j + 3 | 0] = str.charCodeAt(start + j);
  }
};

// Convert a buffer or array and write it to the heap.
// The buffer or array is expected to only contain elements < 256.
var convBuf = function (buf, H8, H32, start, len, off) {
  var i = void 0,
      om = off % 4,
      lm = (len + om) % 4,
      j = len - lm;
  switch (om) {
    case 0:
      H8[off] = buf[start + 3];
    case 1:
      H8[off + 1 - (om << 1) | 0] = buf[start + 2];
    case 2:
      H8[off + 2 - (om << 1) | 0] = buf[start + 1];
    case 3:
      H8[off + 3 - (om << 1) | 0] = buf[start];
  }
  if (len < lm + (4 - om)) {
    return;
  }
  for (i = 4 - om; i < j; i = i + 4 | 0) {
    H32[off + i >> 2 | 0] = buf[start + i] << 24 | buf[start + i + 1] << 16 | buf[start + i + 2] << 8 | buf[start + i + 3];
  }
  switch (lm) {
    case 3:
      H8[off + j + 1 | 0] = buf[start + j + 2];
    case 2:
      H8[off + j + 2 | 0] = buf[start + j + 1];
    case 1:
      H8[off + j + 3 | 0] = buf[start + j];
  }
};

var convBlob = function (blob, H8, H32, start, len, off) {
  var i = void 0,
      om = off % 4,
      lm = (len + om) % 4,
      j = len - lm;
  var buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len)));
  switch (om) {
    case 0:
      H8[off] = buf[3];
    case 1:
      H8[off + 1 - (om << 1) | 0] = buf[2];
    case 2:
      H8[off + 2 - (om << 1) | 0] = buf[1];
    case 3:
      H8[off + 3 - (om << 1) | 0] = buf[0];
  }
  if (len < lm + (4 - om)) {
    return;
  }
  for (i = 4 - om; i < j; i = i + 4 | 0) {
    H32[off + i >> 2 | 0] = buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3];
  }
  switch (lm) {
    case 3:
      H8[off + j + 1 | 0] = buf[j + 2];
    case 2:
      H8[off + j + 2 | 0] = buf[j + 1];
    case 1:
      H8[off + j + 3 | 0] = buf[j];
  }
};

module.exports = function (data, H8, H32, start, len, off) {
  if (typeof data === 'string') {
    return convStr(data, H8, H32, start, len, off);
  }
  if (data instanceof Array) {
    return convBuf(data, H8, H32, start, len, off);
  }
  // Safely doing a Buffer check using "this" to avoid Buffer polyfill to be included in the dist
  if (_this && _this.Buffer && _this.Buffer.isBuffer(data)) {
    return convBuf(data, H8, H32, start, len, off);
  }
  if (data instanceof ArrayBuffer) {
    return convBuf(new Uint8Array(data), H8, H32, start, len, off);
  }
  if (data.buffer instanceof ArrayBuffer) {
    return convBuf(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), H8, H32, start, len, off);
  }
  if (data instanceof Blob) {
    return convBlob(data, H8, H32, start, len, off);
  }
  throw new Error('Unsupported data type.');
};

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/* eslint-env commonjs, browser */

var Rusha = __webpack_require__(0);

var _require = __webpack_require__(1),
    toHex = _require.toHex;

var Hash = function () {
  function Hash() {
    _classCallCheck(this, Hash);

    this._rusha = new Rusha();
    this._rusha.resetState();
  }

  Hash.prototype.update = function update(data) {
    this._rusha.append(data);
    return this;
  };

  Hash.prototype.digest = function digest(encoding) {
    var digest = this._rusha.rawEnd().buffer;
    if (!encoding) {
      return digest;
    }
    if (encoding === 'hex') {
      return toHex(digest);
    }
    throw new Error('unsupported digest encoding');
  };

  return Hash;
}();

module.exports = function () {
  return new Hash();
};

/***/ })
/******/ ]);
});

/***/ }),

/***/ "./node_modules/split-on-first/index.js":
/*!**********************************************!*\
  !*** ./node_modules/split-on-first/index.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = (string, separator) => {
	if (!(typeof string === 'string' && typeof separator === 'string')) {
		throw new TypeError('Expected the arguments to be of type `string`');
	}

	if (separator === '') {
		return [string];
	}

	const separatorIndex = string.indexOf(separator);

	if (separatorIndex === -1) {
		return [string];
	}

	return [
		string.slice(0, separatorIndex),
		string.slice(separatorIndex + separator.length)
	];
};


/***/ }),

/***/ "./node_modules/strict-uri-encode/index.js":
/*!*************************************************!*\
  !*** ./node_modules/strict-uri-encode/index.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

module.exports = str => encodeURIComponent(str).replace(/[!'()*]/g, x => `%${x.charCodeAt(0).toString(16).toUpperCase()}`);


/***/ }),

/***/ "./node_modules/trim/index.js":
/*!************************************!*\
  !*** ./node_modules/trim/index.js ***!
  \************************************/
/*! no static exports found */
/***/ (function(module, exports) {


exports = module.exports = trim;

function trim(str){
  return str.replace(/^\s*|\s*$/g, '');
}

exports.left = function(str){
  return str.replace(/^\s*/, '');
};

exports.right = function(str){
  return str.replace(/\s*$/, '');
};


/***/ }),

/***/ "./node_modules/uri-js/dist/es5/uri.all.js":
/*!*************************************************!*\
  !*** ./node_modules/uri-js/dist/es5/uri.all.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/** @license URI.js v4.2.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */
(function (global, factory) {
	 true ? factory(exports) :
	undefined;
}(this, (function (exports) { 'use strict';

function merge() {
    for (var _len = arguments.length, sets = Array(_len), _key = 0; _key < _len; _key++) {
        sets[_key] = arguments[_key];
    }

    if (sets.length > 1) {
        sets[0] = sets[0].slice(0, -1);
        var xl = sets.length - 1;
        for (var x = 1; x < xl; ++x) {
            sets[x] = sets[x].slice(1, -1);
        }
        sets[xl] = sets[xl].slice(1);
        return sets.join('');
    } else {
        return sets[0];
    }
}
function subexp(str) {
    return "(?:" + str + ")";
}
function typeOf(o) {
    return o === undefined ? "undefined" : o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase();
}
function toUpperCase(str) {
    return str.toUpperCase();
}
function toArray(obj) {
    return obj !== undefined && obj !== null ? obj instanceof Array ? obj : typeof obj.length !== "number" || obj.split || obj.setInterval || obj.call ? [obj] : Array.prototype.slice.call(obj) : [];
}
function assign(target, source) {
    var obj = target;
    if (source) {
        for (var key in source) {
            obj[key] = source[key];
        }
    }
    return obj;
}

function buildExps(isIRI) {
    var ALPHA$$ = "[A-Za-z]",
        CR$ = "[\\x0D]",
        DIGIT$$ = "[0-9]",
        DQUOTE$$ = "[\\x22]",
        HEXDIG$$ = merge(DIGIT$$, "[A-Fa-f]"),
        //case-insensitive
    LF$$ = "[\\x0A]",
        SP$$ = "[\\x20]",
        PCT_ENCODED$ = subexp(subexp("%[EFef]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%[89A-Fa-f]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%" + HEXDIG$$ + HEXDIG$$)),
        //expanded
    GEN_DELIMS$$ = "[\\:\\/\\?\\#\\[\\]\\@]",
        SUB_DELIMS$$ = "[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]",
        RESERVED$$ = merge(GEN_DELIMS$$, SUB_DELIMS$$),
        UCSCHAR$$ = isIRI ? "[\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]" : "[]",
        //subset, excludes bidi control characters
    IPRIVATE$$ = isIRI ? "[\\uE000-\\uF8FF]" : "[]",
        //subset
    UNRESERVED$$ = merge(ALPHA$$, DIGIT$$, "[\\-\\.\\_\\~]", UCSCHAR$$),
        SCHEME$ = subexp(ALPHA$$ + merge(ALPHA$$, DIGIT$$, "[\\+\\-\\.]") + "*"),
        USERINFO$ = subexp(subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\:]")) + "*"),
        DEC_OCTET$ = subexp(subexp("25[0-5]") + "|" + subexp("2[0-4]" + DIGIT$$) + "|" + subexp("1" + DIGIT$$ + DIGIT$$) + "|" + subexp("[1-9]" + DIGIT$$) + "|" + DIGIT$$),
        DEC_OCTET_RELAXED$ = subexp(subexp("25[0-5]") + "|" + subexp("2[0-4]" + DIGIT$$) + "|" + subexp("1" + DIGIT$$ + DIGIT$$) + "|" + subexp("0?[1-9]" + DIGIT$$) + "|0?0?" + DIGIT$$),
        //relaxed parsing rules
    IPV4ADDRESS$ = subexp(DEC_OCTET_RELAXED$ + "\\." + DEC_OCTET_RELAXED$ + "\\." + DEC_OCTET_RELAXED$ + "\\." + DEC_OCTET_RELAXED$),
        H16$ = subexp(HEXDIG$$ + "{1,4}"),
        LS32$ = subexp(subexp(H16$ + "\\:" + H16$) + "|" + IPV4ADDRESS$),
        IPV6ADDRESS1$ = subexp(subexp(H16$ + "\\:") + "{6}" + LS32$),
        //                           6( h16 ":" ) ls32
    IPV6ADDRESS2$ = subexp("\\:\\:" + subexp(H16$ + "\\:") + "{5}" + LS32$),
        //                      "::" 5( h16 ":" ) ls32
    IPV6ADDRESS3$ = subexp(subexp(H16$) + "?\\:\\:" + subexp(H16$ + "\\:") + "{4}" + LS32$),
        //[               h16 ] "::" 4( h16 ":" ) ls32
    IPV6ADDRESS4$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,1}" + H16$) + "?\\:\\:" + subexp(H16$ + "\\:") + "{3}" + LS32$),
        //[ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
    IPV6ADDRESS5$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,2}" + H16$) + "?\\:\\:" + subexp(H16$ + "\\:") + "{2}" + LS32$),
        //[ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
    IPV6ADDRESS6$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,3}" + H16$) + "?\\:\\:" + H16$ + "\\:" + LS32$),
        //[ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
    IPV6ADDRESS7$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,4}" + H16$) + "?\\:\\:" + LS32$),
        //[ *4( h16 ":" ) h16 ] "::"              ls32
    IPV6ADDRESS8$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,5}" + H16$) + "?\\:\\:" + H16$),
        //[ *5( h16 ":" ) h16 ] "::"              h16
    IPV6ADDRESS9$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,6}" + H16$) + "?\\:\\:"),
        //[ *6( h16 ":" ) h16 ] "::"
    IPV6ADDRESS$ = subexp([IPV6ADDRESS1$, IPV6ADDRESS2$, IPV6ADDRESS3$, IPV6ADDRESS4$, IPV6ADDRESS5$, IPV6ADDRESS6$, IPV6ADDRESS7$, IPV6ADDRESS8$, IPV6ADDRESS9$].join("|")),
        ZONEID$ = subexp(subexp(UNRESERVED$$ + "|" + PCT_ENCODED$) + "+"),
        //RFC 6874
    IPV6ADDRZ$ = subexp(IPV6ADDRESS$ + "\\%25" + ZONEID$),
        //RFC 6874
    IPV6ADDRZ_RELAXED$ = subexp(IPV6ADDRESS$ + subexp("\\%25|\\%(?!" + HEXDIG$$ + "{2})") + ZONEID$),
        //RFC 6874, with relaxed parsing rules
    IPVFUTURE$ = subexp("[vV]" + HEXDIG$$ + "+\\." + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\:]") + "+"),
        IP_LITERAL$ = subexp("\\[" + subexp(IPV6ADDRZ_RELAXED$ + "|" + IPV6ADDRESS$ + "|" + IPVFUTURE$) + "\\]"),
        //RFC 6874
    REG_NAME$ = subexp(subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$)) + "*"),
        HOST$ = subexp(IP_LITERAL$ + "|" + IPV4ADDRESS$ + "(?!" + REG_NAME$ + ")" + "|" + REG_NAME$),
        PORT$ = subexp(DIGIT$$ + "*"),
        AUTHORITY$ = subexp(subexp(USERINFO$ + "@") + "?" + HOST$ + subexp("\\:" + PORT$) + "?"),
        PCHAR$ = subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@]")),
        SEGMENT$ = subexp(PCHAR$ + "*"),
        SEGMENT_NZ$ = subexp(PCHAR$ + "+"),
        SEGMENT_NZ_NC$ = subexp(subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\@]")) + "+"),
        PATH_ABEMPTY$ = subexp(subexp("\\/" + SEGMENT$) + "*"),
        PATH_ABSOLUTE$ = subexp("\\/" + subexp(SEGMENT_NZ$ + PATH_ABEMPTY$) + "?"),
        //simplified
    PATH_NOSCHEME$ = subexp(SEGMENT_NZ_NC$ + PATH_ABEMPTY$),
        //simplified
    PATH_ROOTLESS$ = subexp(SEGMENT_NZ$ + PATH_ABEMPTY$),
        //simplified
    PATH_EMPTY$ = "(?!" + PCHAR$ + ")",
        PATH$ = subexp(PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$),
        QUERY$ = subexp(subexp(PCHAR$ + "|" + merge("[\\/\\?]", IPRIVATE$$)) + "*"),
        FRAGMENT$ = subexp(subexp(PCHAR$ + "|[\\/\\?]") + "*"),
        HIER_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$),
        URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"),
        RELATIVE_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$),
        RELATIVE$ = subexp(RELATIVE_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"),
        URI_REFERENCE$ = subexp(URI$ + "|" + RELATIVE$),
        ABSOLUTE_URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?"),
        GENERIC_REF$ = "^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$",
        RELATIVE_REF$ = "^(){0}" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$",
        ABSOLUTE_REF$ = "^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?$",
        SAMEDOC_REF$ = "^" + subexp("\\#(" + FRAGMENT$ + ")") + "?$",
        AUTHORITY_REF$ = "^" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?$";
    return {
        NOT_SCHEME: new RegExp(merge("[^]", ALPHA$$, DIGIT$$, "[\\+\\-\\.]"), "g"),
        NOT_USERINFO: new RegExp(merge("[^\\%\\:]", UNRESERVED$$, SUB_DELIMS$$), "g"),
        NOT_HOST: new RegExp(merge("[^\\%\\[\\]\\:]", UNRESERVED$$, SUB_DELIMS$$), "g"),
        NOT_PATH: new RegExp(merge("[^\\%\\/\\:\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"),
        NOT_PATH_NOSCHEME: new RegExp(merge("[^\\%\\/\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"),
        NOT_QUERY: new RegExp(merge("[^\\%]", UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@\\/\\?]", IPRIVATE$$), "g"),
        NOT_FRAGMENT: new RegExp(merge("[^\\%]", UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@\\/\\?]"), "g"),
        ESCAPE: new RegExp(merge("[^]", UNRESERVED$$, SUB_DELIMS$$), "g"),
        UNRESERVED: new RegExp(UNRESERVED$$, "g"),
        OTHER_CHARS: new RegExp(merge("[^\\%]", UNRESERVED$$, RESERVED$$), "g"),
        PCT_ENCODED: new RegExp(PCT_ENCODED$, "g"),
        IPV4ADDRESS: new RegExp("^(" + IPV4ADDRESS$ + ")$"),
        IPV6ADDRESS: new RegExp("^\\[?(" + IPV6ADDRESS$ + ")" + subexp(subexp("\\%25|\\%(?!" + HEXDIG$$ + "{2})") + "(" + ZONEID$ + ")") + "?\\]?$") //RFC 6874, with relaxed parsing rules
    };
}
var URI_PROTOCOL = buildExps(false);

var IRI_PROTOCOL = buildExps(true);

var slicedToArray = function () {
  function sliceIterator(arr, i) {
    var _arr = [];
    var _n = true;
    var _d = false;
    var _e = undefined;

    try {
      for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
        _arr.push(_s.value);

        if (i && _arr.length === i) break;
      }
    } catch (err) {
      _d = true;
      _e = err;
    } finally {
      try {
        if (!_n && _i["return"]) _i["return"]();
      } finally {
        if (_d) throw _e;
      }
    }

    return _arr;
  }

  return function (arr, i) {
    if (Array.isArray(arr)) {
      return arr;
    } else if (Symbol.iterator in Object(arr)) {
      return sliceIterator(arr, i);
    } else {
      throw new TypeError("Invalid attempt to destructure non-iterable instance");
    }
  };
}();













var toConsumableArray = function (arr) {
  if (Array.isArray(arr)) {
    for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];

    return arr2;
  } else {
    return Array.from(arr);
  }
};

/** Highest positive signed 32-bit float value */

var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1

/** Bootstring parameters */
var base = 36;
var tMin = 1;
var tMax = 26;
var skew = 38;
var damp = 700;
var initialBias = 72;
var initialN = 128; // 0x80
var delimiter = '-'; // '\x2D'

/** Regular expressions */
var regexPunycode = /^xn--/;
var regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars
var regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators

/** Error messages */
var errors = {
	'overflow': 'Overflow: input needs wider integers to process',
	'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
	'invalid-input': 'Invalid input'
};

/** Convenience shortcuts */
var baseMinusTMin = base - tMin;
var floor = Math.floor;
var stringFromCharCode = String.fromCharCode;

/*--------------------------------------------------------------------------*/

/**
 * A generic error utility function.
 * @private
 * @param {String} type The error type.
 * @returns {Error} Throws a `RangeError` with the applicable error message.
 */
function error$1(type) {
	throw new RangeError(errors[type]);
}

/**
 * A generic `Array#map` utility function.
 * @private
 * @param {Array} array The array to iterate over.
 * @param {Function} callback The function that gets called for every array
 * item.
 * @returns {Array} A new array of values returned by the callback function.
 */
function map(array, fn) {
	var result = [];
	var length = array.length;
	while (length--) {
		result[length] = fn(array[length]);
	}
	return result;
}

/**
 * A simple `Array#map`-like wrapper to work with domain name strings or email
 * addresses.
 * @private
 * @param {String} domain The domain name or email address.
 * @param {Function} callback The function that gets called for every
 * character.
 * @returns {Array} A new string of characters returned by the callback
 * function.
 */
function mapDomain(string, fn) {
	var parts = string.split('@');
	var result = '';
	if (parts.length > 1) {
		// In email addresses, only the domain name should be punycoded. Leave
		// the local part (i.e. everything up to `@`) intact.
		result = parts[0] + '@';
		string = parts[1];
	}
	// Avoid `split(regex)` for IE8 compatibility. See #17.
	string = string.replace(regexSeparators, '\x2E');
	var labels = string.split('.');
	var encoded = map(labels, fn).join('.');
	return result + encoded;
}

/**
 * Creates an array containing the numeric code points of each Unicode
 * character in the string. While JavaScript uses UCS-2 internally,
 * this function will convert a pair of surrogate halves (each of which
 * UCS-2 exposes as separate characters) into a single code point,
 * matching UTF-16.
 * @see `punycode.ucs2.encode`
 * @see <https://mathiasbynens.be/notes/javascript-encoding>
 * @memberOf punycode.ucs2
 * @name decode
 * @param {String} string The Unicode input string (UCS-2).
 * @returns {Array} The new array of code points.
 */
function ucs2decode(string) {
	var output = [];
	var counter = 0;
	var length = string.length;
	while (counter < length) {
		var value = string.charCodeAt(counter++);
		if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
			// It's a high surrogate, and there is a next character.
			var extra = string.charCodeAt(counter++);
			if ((extra & 0xFC00) == 0xDC00) {
				// Low surrogate.
				output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
			} else {
				// It's an unmatched surrogate; only append this code unit, in case the
				// next code unit is the high surrogate of a surrogate pair.
				output.push(value);
				counter--;
			}
		} else {
			output.push(value);
		}
	}
	return output;
}

/**
 * Creates a string based on an array of numeric code points.
 * @see `punycode.ucs2.decode`
 * @memberOf punycode.ucs2
 * @name encode
 * @param {Array} codePoints The array of numeric code points.
 * @returns {String} The new Unicode string (UCS-2).
 */
var ucs2encode = function ucs2encode(array) {
	return String.fromCodePoint.apply(String, toConsumableArray(array));
};

/**
 * Converts a basic code point into a digit/integer.
 * @see `digitToBasic()`
 * @private
 * @param {Number} codePoint The basic numeric code point value.
 * @returns {Number} The numeric value of a basic code point (for use in
 * representing integers) in the range `0` to `base - 1`, or `base` if
 * the code point does not represent a value.
 */
var basicToDigit = function basicToDigit(codePoint) {
	if (codePoint - 0x30 < 0x0A) {
		return codePoint - 0x16;
	}
	if (codePoint - 0x41 < 0x1A) {
		return codePoint - 0x41;
	}
	if (codePoint - 0x61 < 0x1A) {
		return codePoint - 0x61;
	}
	return base;
};

/**
 * Converts a digit/integer into a basic code point.
 * @see `basicToDigit()`
 * @private
 * @param {Number} digit The numeric value of a basic code point.
 * @returns {Number} The basic code point whose value (when used for
 * representing integers) is `digit`, which needs to be in the range
 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
 * used; else, the lowercase form is used. The behavior is undefined
 * if `flag` is non-zero and `digit` has no uppercase form.
 */
var digitToBasic = function digitToBasic(digit, flag) {
	//  0..25 map to ASCII a..z or A..Z
	// 26..35 map to ASCII 0..9
	return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
};

/**
 * Bias adaptation function as per section 3.4 of RFC 3492.
 * https://tools.ietf.org/html/rfc3492#section-3.4
 * @private
 */
var adapt = function adapt(delta, numPoints, firstTime) {
	var k = 0;
	delta = firstTime ? floor(delta / damp) : delta >> 1;
	delta += floor(delta / numPoints);
	for (; /* no initialization */delta > baseMinusTMin * tMax >> 1; k += base) {
		delta = floor(delta / baseMinusTMin);
	}
	return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
};

/**
 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
 * symbols.
 * @memberOf punycode
 * @param {String} input The Punycode string of ASCII-only symbols.
 * @returns {String} The resulting string of Unicode symbols.
 */
var decode = function decode(input) {
	// Don't use UCS-2.
	var output = [];
	var inputLength = input.length;
	var i = 0;
	var n = initialN;
	var bias = initialBias;

	// Handle the basic code points: let `basic` be the number of input code
	// points before the last delimiter, or `0` if there is none, then copy
	// the first basic code points to the output.

	var basic = input.lastIndexOf(delimiter);
	if (basic < 0) {
		basic = 0;
	}

	for (var j = 0; j < basic; ++j) {
		// if it's not a basic code point
		if (input.charCodeAt(j) >= 0x80) {
			error$1('not-basic');
		}
		output.push(input.charCodeAt(j));
	}

	// Main decoding loop: start just after the last delimiter if any basic code
	// points were copied; start at the beginning otherwise.

	for (var index = basic > 0 ? basic + 1 : 0; index < inputLength;) /* no final expression */{

		// `index` is the index of the next character to be consumed.
		// Decode a generalized variable-length integer into `delta`,
		// which gets added to `i`. The overflow checking is easier
		// if we increase `i` as we go, then subtract off its starting
		// value at the end to obtain `delta`.
		var oldi = i;
		for (var w = 1, k = base;; /* no condition */k += base) {

			if (index >= inputLength) {
				error$1('invalid-input');
			}

			var digit = basicToDigit(input.charCodeAt(index++));

			if (digit >= base || digit > floor((maxInt - i) / w)) {
				error$1('overflow');
			}

			i += digit * w;
			var t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;

			if (digit < t) {
				break;
			}

			var baseMinusT = base - t;
			if (w > floor(maxInt / baseMinusT)) {
				error$1('overflow');
			}

			w *= baseMinusT;
		}

		var out = output.length + 1;
		bias = adapt(i - oldi, out, oldi == 0);

		// `i` was supposed to wrap around from `out` to `0`,
		// incrementing `n` each time, so we'll fix that now:
		if (floor(i / out) > maxInt - n) {
			error$1('overflow');
		}

		n += floor(i / out);
		i %= out;

		// Insert `n` at position `i` of the output.
		output.splice(i++, 0, n);
	}

	return String.fromCodePoint.apply(String, output);
};

/**
 * Converts a string of Unicode symbols (e.g. a domain name label) to a
 * Punycode string of ASCII-only symbols.
 * @memberOf punycode
 * @param {String} input The string of Unicode symbols.
 * @returns {String} The resulting Punycode string of ASCII-only symbols.
 */
var encode = function encode(input) {
	var output = [];

	// Convert the input in UCS-2 to an array of Unicode code points.
	input = ucs2decode(input);

	// Cache the length.
	var inputLength = input.length;

	// Initialize the state.
	var n = initialN;
	var delta = 0;
	var bias = initialBias;

	// Handle the basic code points.
	var _iteratorNormalCompletion = true;
	var _didIteratorError = false;
	var _iteratorError = undefined;

	try {
		for (var _iterator = input[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
			var _currentValue2 = _step.value;

			if (_currentValue2 < 0x80) {
				output.push(stringFromCharCode(_currentValue2));
			}
		}
	} catch (err) {
		_didIteratorError = true;
		_iteratorError = err;
	} finally {
		try {
			if (!_iteratorNormalCompletion && _iterator.return) {
				_iterator.return();
			}
		} finally {
			if (_didIteratorError) {
				throw _iteratorError;
			}
		}
	}

	var basicLength = output.length;
	var handledCPCount = basicLength;

	// `handledCPCount` is the number of code points that have been handled;
	// `basicLength` is the number of basic code points.

	// Finish the basic string with a delimiter unless it's empty.
	if (basicLength) {
		output.push(delimiter);
	}

	// Main encoding loop:
	while (handledCPCount < inputLength) {

		// All non-basic code points < n have been handled already. Find the next
		// larger one:
		var m = maxInt;
		var _iteratorNormalCompletion2 = true;
		var _didIteratorError2 = false;
		var _iteratorError2 = undefined;

		try {
			for (var _iterator2 = input[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
				var currentValue = _step2.value;

				if (currentValue >= n && currentValue < m) {
					m = currentValue;
				}
			}

			// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
			// but guard against overflow.
		} catch (err) {
			_didIteratorError2 = true;
			_iteratorError2 = err;
		} finally {
			try {
				if (!_iteratorNormalCompletion2 && _iterator2.return) {
					_iterator2.return();
				}
			} finally {
				if (_didIteratorError2) {
					throw _iteratorError2;
				}
			}
		}

		var handledCPCountPlusOne = handledCPCount + 1;
		if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
			error$1('overflow');
		}

		delta += (m - n) * handledCPCountPlusOne;
		n = m;

		var _iteratorNormalCompletion3 = true;
		var _didIteratorError3 = false;
		var _iteratorError3 = undefined;

		try {
			for (var _iterator3 = input[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
				var _currentValue = _step3.value;

				if (_currentValue < n && ++delta > maxInt) {
					error$1('overflow');
				}
				if (_currentValue == n) {
					// Represent delta as a generalized variable-length integer.
					var q = delta;
					for (var k = base;; /* no condition */k += base) {
						var t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
						if (q < t) {
							break;
						}
						var qMinusT = q - t;
						var baseMinusT = base - t;
						output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)));
						q = floor(qMinusT / baseMinusT);
					}

					output.push(stringFromCharCode(digitToBasic(q, 0)));
					bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
					delta = 0;
					++handledCPCount;
				}
			}
		} catch (err) {
			_didIteratorError3 = true;
			_iteratorError3 = err;
		} finally {
			try {
				if (!_iteratorNormalCompletion3 && _iterator3.return) {
					_iterator3.return();
				}
			} finally {
				if (_didIteratorError3) {
					throw _iteratorError3;
				}
			}
		}

		++delta;
		++n;
	}
	return output.join('');
};

/**
 * Converts a Punycode string representing a domain name or an email address
 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
 * it doesn't matter if you call it on a string that has already been
 * converted to Unicode.
 * @memberOf punycode
 * @param {String} input The Punycoded domain name or email address to
 * convert to Unicode.
 * @returns {String} The Unicode representation of the given Punycode
 * string.
 */
var toUnicode = function toUnicode(input) {
	return mapDomain(input, function (string) {
		return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string;
	});
};

/**
 * Converts a Unicode string representing a domain name or an email address to
 * Punycode. Only the non-ASCII parts of the domain name will be converted,
 * i.e. it doesn't matter if you call it with a domain that's already in
 * ASCII.
 * @memberOf punycode
 * @param {String} input The domain name or email address to convert, as a
 * Unicode string.
 * @returns {String} The Punycode representation of the given domain name or
 * email address.
 */
var toASCII = function toASCII(input) {
	return mapDomain(input, function (string) {
		return regexNonASCII.test(string) ? 'xn--' + encode(string) : string;
	});
};

/*--------------------------------------------------------------------------*/

/** Define the public API */
var punycode = {
	/**
  * A string representing the current Punycode.js version number.
  * @memberOf punycode
  * @type String
  */
	'version': '2.1.0',
	/**
  * An object of methods to convert from JavaScript's internal character
  * representation (UCS-2) to Unicode code points, and back.
  * @see <https://mathiasbynens.be/notes/javascript-encoding>
  * @memberOf punycode
  * @type Object
  */
	'ucs2': {
		'decode': ucs2decode,
		'encode': ucs2encode
	},
	'decode': decode,
	'encode': encode,
	'toASCII': toASCII,
	'toUnicode': toUnicode
};

/**
 * URI.js
 *
 * @fileoverview An RFC 3986 compliant, scheme extendable URI parsing/validating/resolving library for JavaScript.
 * @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
 * @see http://github.com/garycourt/uri-js
 */
/**
 * Copyright 2011 Gary Court. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Gary Court.
 */
var SCHEMES = {};
function pctEncChar(chr) {
    var c = chr.charCodeAt(0);
    var e = void 0;
    if (c < 16) e = "%0" + c.toString(16).toUpperCase();else if (c < 128) e = "%" + c.toString(16).toUpperCase();else if (c < 2048) e = "%" + (c >> 6 | 192).toString(16).toUpperCase() + "%" + (c & 63 | 128).toString(16).toUpperCase();else e = "%" + (c >> 12 | 224).toString(16).toUpperCase() + "%" + (c >> 6 & 63 | 128).toString(16).toUpperCase() + "%" + (c & 63 | 128).toString(16).toUpperCase();
    return e;
}
function pctDecChars(str) {
    var newStr = "";
    var i = 0;
    var il = str.length;
    while (i < il) {
        var c = parseInt(str.substr(i + 1, 2), 16);
        if (c < 128) {
            newStr += String.fromCharCode(c);
            i += 3;
        } else if (c >= 194 && c < 224) {
            if (il - i >= 6) {
                var c2 = parseInt(str.substr(i + 4, 2), 16);
                newStr += String.fromCharCode((c & 31) << 6 | c2 & 63);
            } else {
                newStr += str.substr(i, 6);
            }
            i += 6;
        } else if (c >= 224) {
            if (il - i >= 9) {
                var _c = parseInt(str.substr(i + 4, 2), 16);
                var c3 = parseInt(str.substr(i + 7, 2), 16);
                newStr += String.fromCharCode((c & 15) << 12 | (_c & 63) << 6 | c3 & 63);
            } else {
                newStr += str.substr(i, 9);
            }
            i += 9;
        } else {
            newStr += str.substr(i, 3);
            i += 3;
        }
    }
    return newStr;
}
function _normalizeComponentEncoding(components, protocol) {
    function decodeUnreserved(str) {
        var decStr = pctDecChars(str);
        return !decStr.match(protocol.UNRESERVED) ? str : decStr;
    }
    if (components.scheme) components.scheme = String(components.scheme).replace(protocol.PCT_ENCODED, decodeUnreserved).toLowerCase().replace(protocol.NOT_SCHEME, "");
    if (components.userinfo !== undefined) components.userinfo = String(components.userinfo).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(protocol.NOT_USERINFO, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase);
    if (components.host !== undefined) components.host = String(components.host).replace(protocol.PCT_ENCODED, decodeUnreserved).toLowerCase().replace(protocol.NOT_HOST, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase);
    if (components.path !== undefined) components.path = String(components.path).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(components.scheme ? protocol.NOT_PATH : protocol.NOT_PATH_NOSCHEME, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase);
    if (components.query !== undefined) components.query = String(components.query).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(protocol.NOT_QUERY, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase);
    if (components.fragment !== undefined) components.fragment = String(components.fragment).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(protocol.NOT_FRAGMENT, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase);
    return components;
}

function _stripLeadingZeros(str) {
    return str.replace(/^0*(.*)/, "$1") || "0";
}
function _normalizeIPv4(host, protocol) {
    var matches = host.match(protocol.IPV4ADDRESS) || [];

    var _matches = slicedToArray(matches, 2),
        address = _matches[1];

    if (address) {
        return address.split(".").map(_stripLeadingZeros).join(".");
    } else {
        return host;
    }
}
function _normalizeIPv6(host, protocol) {
    var matches = host.match(protocol.IPV6ADDRESS) || [];

    var _matches2 = slicedToArray(matches, 3),
        address = _matches2[1],
        zone = _matches2[2];

    if (address) {
        var _address$toLowerCase$ = address.toLowerCase().split('::').reverse(),
            _address$toLowerCase$2 = slicedToArray(_address$toLowerCase$, 2),
            last = _address$toLowerCase$2[0],
            first = _address$toLowerCase$2[1];

        var firstFields = first ? first.split(":").map(_stripLeadingZeros) : [];
        var lastFields = last.split(":").map(_stripLeadingZeros);
        var isLastFieldIPv4Address = protocol.IPV4ADDRESS.test(lastFields[lastFields.length - 1]);
        var fieldCount = isLastFieldIPv4Address ? 7 : 8;
        var lastFieldsStart = lastFields.length - fieldCount;
        var fields = Array(fieldCount);
        for (var x = 0; x < fieldCount; ++x) {
            fields[x] = firstFields[x] || lastFields[lastFieldsStart + x] || '';
        }
        if (isLastFieldIPv4Address) {
            fields[fieldCount - 1] = _normalizeIPv4(fields[fieldCount - 1], protocol);
        }
        var allZeroFields = fields.reduce(function (acc, field, index) {
            if (!field || field === "0") {
                var lastLongest = acc[acc.length - 1];
                if (lastLongest && lastLongest.index + lastLongest.length === index) {
                    lastLongest.length++;
                } else {
                    acc.push({ index: index, length: 1 });
                }
            }
            return acc;
        }, []);
        var longestZeroFields = allZeroFields.sort(function (a, b) {
            return b.length - a.length;
        })[0];
        var newHost = void 0;
        if (longestZeroFields && longestZeroFields.length > 1) {
            var newFirst = fields.slice(0, longestZeroFields.index);
            var newLast = fields.slice(longestZeroFields.index + longestZeroFields.length);
            newHost = newFirst.join(":") + "::" + newLast.join(":");
        } else {
            newHost = fields.join(":");
        }
        if (zone) {
            newHost += "%" + zone;
        }
        return newHost;
    } else {
        return host;
    }
}
var URI_PARSE = /^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?(\[[^\/?#\]]+\]|[^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i;
var NO_MATCH_IS_UNDEFINED = "".match(/(){0}/)[1] === undefined;
function parse(uriString) {
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    var components = {};
    var protocol = options.iri !== false ? IRI_PROTOCOL : URI_PROTOCOL;
    if (options.reference === "suffix") uriString = (options.scheme ? options.scheme + ":" : "") + "//" + uriString;
    var matches = uriString.match(URI_PARSE);
    if (matches) {
        if (NO_MATCH_IS_UNDEFINED) {
            //store each component
            components.scheme = matches[1];
            components.userinfo = matches[3];
            components.host = matches[4];
            components.port = parseInt(matches[5], 10);
            components.path = matches[6] || "";
            components.query = matches[7];
            components.fragment = matches[8];
            //fix port number
            if (isNaN(components.port)) {
                components.port = matches[5];
            }
        } else {
            //IE FIX for improper RegExp matching
            //store each component
            components.scheme = matches[1] || undefined;
            components.userinfo = uriString.indexOf("@") !== -1 ? matches[3] : undefined;
            components.host = uriString.indexOf("//") !== -1 ? matches[4] : undefined;
            components.port = parseInt(matches[5], 10);
            components.path = matches[6] || "";
            components.query = uriString.indexOf("?") !== -1 ? matches[7] : undefined;
            components.fragment = uriString.indexOf("#") !== -1 ? matches[8] : undefined;
            //fix port number
            if (isNaN(components.port)) {
                components.port = uriString.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/) ? matches[4] : undefined;
            }
        }
        if (components.host) {
            //normalize IP hosts
            components.host = _normalizeIPv6(_normalizeIPv4(components.host, protocol), protocol);
        }
        //determine reference type
        if (components.scheme === undefined && components.userinfo === undefined && components.host === undefined && components.port === undefined && !components.path && components.query === undefined) {
            components.reference = "same-document";
        } else if (components.scheme === undefined) {
            components.reference = "relative";
        } else if (components.fragment === undefined) {
            components.reference = "absolute";
        } else {
            components.reference = "uri";
        }
        //check for reference errors
        if (options.reference && options.reference !== "suffix" && options.reference !== components.reference) {
            components.error = components.error || "URI is not a " + options.reference + " reference.";
        }
        //find scheme handler
        var schemeHandler = SCHEMES[(options.scheme || components.scheme || "").toLowerCase()];
        //check if scheme can't handle IRIs
        if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
            //if host component is a domain name
            if (components.host && (options.domainHost || schemeHandler && schemeHandler.domainHost)) {
                //convert Unicode IDN -> ASCII IDN
                try {
                    components.host = punycode.toASCII(components.host.replace(protocol.PCT_ENCODED, pctDecChars).toLowerCase());
                } catch (e) {
                    components.error = components.error || "Host's domain name can not be converted to ASCII via punycode: " + e;
                }
            }
            //convert IRI -> URI
            _normalizeComponentEncoding(components, URI_PROTOCOL);
        } else {
            //normalize encodings
            _normalizeComponentEncoding(components, protocol);
        }
        //perform scheme specific parsing
        if (schemeHandler && schemeHandler.parse) {
            schemeHandler.parse(components, options);
        }
    } else {
        components.error = components.error || "URI can not be parsed.";
    }
    return components;
}

function _recomposeAuthority(components, options) {
    var protocol = options.iri !== false ? IRI_PROTOCOL : URI_PROTOCOL;
    var uriTokens = [];
    if (components.userinfo !== undefined) {
        uriTokens.push(components.userinfo);
        uriTokens.push("@");
    }
    if (components.host !== undefined) {
        //normalize IP hosts, add brackets and escape zone separator for IPv6
        uriTokens.push(_normalizeIPv6(_normalizeIPv4(String(components.host), protocol), protocol).replace(protocol.IPV6ADDRESS, function (_, $1, $2) {
            return "[" + $1 + ($2 ? "%25" + $2 : "") + "]";
        }));
    }
    if (typeof components.port === "number") {
        uriTokens.push(":");
        uriTokens.push(components.port.toString(10));
    }
    return uriTokens.length ? uriTokens.join("") : undefined;
}

var RDS1 = /^\.\.?\//;
var RDS2 = /^\/\.(\/|$)/;
var RDS3 = /^\/\.\.(\/|$)/;
var RDS5 = /^\/?(?:.|\n)*?(?=\/|$)/;
function removeDotSegments(input) {
    var output = [];
    while (input.length) {
        if (input.match(RDS1)) {
            input = input.replace(RDS1, "");
        } else if (input.match(RDS2)) {
            input = input.replace(RDS2, "/");
        } else if (input.match(RDS3)) {
            input = input.replace(RDS3, "/");
            output.pop();
        } else if (input === "." || input === "..") {
            input = "";
        } else {
            var im = input.match(RDS5);
            if (im) {
                var s = im[0];
                input = input.slice(s.length);
                output.push(s);
            } else {
                throw new Error("Unexpected dot segment condition");
            }
        }
    }
    return output.join("");
}

function serialize(components) {
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    var protocol = options.iri ? IRI_PROTOCOL : URI_PROTOCOL;
    var uriTokens = [];
    //find scheme handler
    var schemeHandler = SCHEMES[(options.scheme || components.scheme || "").toLowerCase()];
    //perform scheme specific serialization
    if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(components, options);
    if (components.host) {
        //if host component is an IPv6 address
        if (protocol.IPV6ADDRESS.test(components.host)) {}
        //TODO: normalize IPv6 address as per RFC 5952

        //if host component is a domain name
        else if (options.domainHost || schemeHandler && schemeHandler.domainHost) {
                //convert IDN via punycode
                try {
                    components.host = !options.iri ? punycode.toASCII(components.host.replace(protocol.PCT_ENCODED, pctDecChars).toLowerCase()) : punycode.toUnicode(components.host);
                } catch (e) {
                    components.error = components.error || "Host's domain name can not be converted to " + (!options.iri ? "ASCII" : "Unicode") + " via punycode: " + e;
                }
            }
    }
    //normalize encoding
    _normalizeComponentEncoding(components, protocol);
    if (options.reference !== "suffix" && components.scheme) {
        uriTokens.push(components.scheme);
        uriTokens.push(":");
    }
    var authority = _recomposeAuthority(components, options);
    if (authority !== undefined) {
        if (options.reference !== "suffix") {
            uriTokens.push("//");
        }
        uriTokens.push(authority);
        if (components.path && components.path.charAt(0) !== "/") {
            uriTokens.push("/");
        }
    }
    if (components.path !== undefined) {
        var s = components.path;
        if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
            s = removeDotSegments(s);
        }
        if (authority === undefined) {
            s = s.replace(/^\/\//, "/%2F"); //don't allow the path to start with "//"
        }
        uriTokens.push(s);
    }
    if (components.query !== undefined) {
        uriTokens.push("?");
        uriTokens.push(components.query);
    }
    if (components.fragment !== undefined) {
        uriTokens.push("#");
        uriTokens.push(components.fragment);
    }
    return uriTokens.join(""); //merge tokens into a string
}

function resolveComponents(base, relative) {
    var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    var skipNormalization = arguments[3];

    var target = {};
    if (!skipNormalization) {
        base = parse(serialize(base, options), options); //normalize base components
        relative = parse(serialize(relative, options), options); //normalize relative components
    }
    options = options || {};
    if (!options.tolerant && relative.scheme) {
        target.scheme = relative.scheme;
        //target.authority = relative.authority;
        target.userinfo = relative.userinfo;
        target.host = relative.host;
        target.port = relative.port;
        target.path = removeDotSegments(relative.path || "");
        target.query = relative.query;
    } else {
        if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) {
            //target.authority = relative.authority;
            target.userinfo = relative.userinfo;
            target.host = relative.host;
            target.port = relative.port;
            target.path = removeDotSegments(relative.path || "");
            target.query = relative.query;
        } else {
            if (!relative.path) {
                target.path = base.path;
                if (relative.query !== undefined) {
                    target.query = relative.query;
                } else {
                    target.query = base.query;
                }
            } else {
                if (relative.path.charAt(0) === "/") {
                    target.path = removeDotSegments(relative.path);
                } else {
                    if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
                        target.path = "/" + relative.path;
                    } else if (!base.path) {
                        target.path = relative.path;
                    } else {
                        target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
                    }
                    target.path = removeDotSegments(target.path);
                }
                target.query = relative.query;
            }
            //target.authority = base.authority;
            target.userinfo = base.userinfo;
            target.host = base.host;
            target.port = base.port;
        }
        target.scheme = base.scheme;
    }
    target.fragment = relative.fragment;
    return target;
}

function resolve(baseURI, relativeURI, options) {
    var schemelessOptions = assign({ scheme: 'null' }, options);
    return serialize(resolveComponents(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true), schemelessOptions);
}

function normalize(uri, options) {
    if (typeof uri === "string") {
        uri = serialize(parse(uri, options), options);
    } else if (typeOf(uri) === "object") {
        uri = parse(serialize(uri, options), options);
    }
    return uri;
}

function equal(uriA, uriB, options) {
    if (typeof uriA === "string") {
        uriA = serialize(parse(uriA, options), options);
    } else if (typeOf(uriA) === "object") {
        uriA = serialize(uriA, options);
    }
    if (typeof uriB === "string") {
        uriB = serialize(parse(uriB, options), options);
    } else if (typeOf(uriB) === "object") {
        uriB = serialize(uriB, options);
    }
    return uriA === uriB;
}

function escapeComponent(str, options) {
    return str && str.toString().replace(!options || !options.iri ? URI_PROTOCOL.ESCAPE : IRI_PROTOCOL.ESCAPE, pctEncChar);
}

function unescapeComponent(str, options) {
    return str && str.toString().replace(!options || !options.iri ? URI_PROTOCOL.PCT_ENCODED : IRI_PROTOCOL.PCT_ENCODED, pctDecChars);
}

var handler = {
    scheme: "http",
    domainHost: true,
    parse: function parse(components, options) {
        //report missing host
        if (!components.host) {
            components.error = components.error || "HTTP URIs must have a host.";
        }
        return components;
    },
    serialize: function serialize(components, options) {
        //normalize the default port
        if (components.port === (String(components.scheme).toLowerCase() !== "https" ? 80 : 443) || components.port === "") {
            components.port = undefined;
        }
        //normalize the empty path
        if (!components.path) {
            components.path = "/";
        }
        //NOTE: We do not parse query strings for HTTP URIs
        //as WWW Form Url Encoded query strings are part of the HTML4+ spec,
        //and not the HTTP spec.
        return components;
    }
};

var handler$1 = {
    scheme: "https",
    domainHost: handler.domainHost,
    parse: handler.parse,
    serialize: handler.serialize
};

var O = {};
var isIRI = true;
//RFC 3986
var UNRESERVED$$ = "[A-Za-z0-9\\-\\.\\_\\~" + (isIRI ? "\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF" : "") + "]";
var HEXDIG$$ = "[0-9A-Fa-f]"; //case-insensitive
var PCT_ENCODED$ = subexp(subexp("%[EFef]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%[89A-Fa-f]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%" + HEXDIG$$ + HEXDIG$$)); //expanded
//RFC 5322, except these symbols as per RFC 6068: @ : / ? # [ ] & ; =
//const ATEXT$$ = "[A-Za-z0-9\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~]";
//const WSP$$ = "[\\x20\\x09]";
//const OBS_QTEXT$$ = "[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]";  //(%d1-8 / %d11-12 / %d14-31 / %d127)
//const QTEXT$$ = merge("[\\x21\\x23-\\x5B\\x5D-\\x7E]", OBS_QTEXT$$);  //%d33 / %d35-91 / %d93-126 / obs-qtext
//const VCHAR$$ = "[\\x21-\\x7E]";
//const WSP$$ = "[\\x20\\x09]";
//const OBS_QP$ = subexp("\\\\" + merge("[\\x00\\x0D\\x0A]", OBS_QTEXT$$));  //%d0 / CR / LF / obs-qtext
//const FWS$ = subexp(subexp(WSP$$ + "*" + "\\x0D\\x0A") + "?" + WSP$$ + "+");
//const QUOTED_PAIR$ = subexp(subexp("\\\\" + subexp(VCHAR$$ + "|" + WSP$$)) + "|" + OBS_QP$);
//const QUOTED_STRING$ = subexp('\\"' + subexp(FWS$ + "?" + QCONTENT$) + "*" + FWS$ + "?" + '\\"');
var ATEXT$$ = "[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]";
var QTEXT$$ = "[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]";
var VCHAR$$ = merge(QTEXT$$, "[\\\"\\\\]");
var SOME_DELIMS$$ = "[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]";
var UNRESERVED = new RegExp(UNRESERVED$$, "g");
var PCT_ENCODED = new RegExp(PCT_ENCODED$, "g");
var NOT_LOCAL_PART = new RegExp(merge("[^]", ATEXT$$, "[\\.]", '[\\"]', VCHAR$$), "g");
var NOT_HFNAME = new RegExp(merge("[^]", UNRESERVED$$, SOME_DELIMS$$), "g");
var NOT_HFVALUE = NOT_HFNAME;
function decodeUnreserved(str) {
    var decStr = pctDecChars(str);
    return !decStr.match(UNRESERVED) ? str : decStr;
}
var handler$2 = {
    scheme: "mailto",
    parse: function parse$$1(components, options) {
        var mailtoComponents = components;
        var to = mailtoComponents.to = mailtoComponents.path ? mailtoComponents.path.split(",") : [];
        mailtoComponents.path = undefined;
        if (mailtoComponents.query) {
            var unknownHeaders = false;
            var headers = {};
            var hfields = mailtoComponents.query.split("&");
            for (var x = 0, xl = hfields.length; x < xl; ++x) {
                var hfield = hfields[x].split("=");
                switch (hfield[0]) {
                    case "to":
                        var toAddrs = hfield[1].split(",");
                        for (var _x = 0, _xl = toAddrs.length; _x < _xl; ++_x) {
                            to.push(toAddrs[_x]);
                        }
                        break;
                    case "subject":
                        mailtoComponents.subject = unescapeComponent(hfield[1], options);
                        break;
                    case "body":
                        mailtoComponents.body = unescapeComponent(hfield[1], options);
                        break;
                    default:
                        unknownHeaders = true;
                        headers[unescapeComponent(hfield[0], options)] = unescapeComponent(hfield[1], options);
                        break;
                }
            }
            if (unknownHeaders) mailtoComponents.headers = headers;
        }
        mailtoComponents.query = undefined;
        for (var _x2 = 0, _xl2 = to.length; _x2 < _xl2; ++_x2) {
            var addr = to[_x2].split("@");
            addr[0] = unescapeComponent(addr[0]);
            if (!options.unicodeSupport) {
                //convert Unicode IDN -> ASCII IDN
                try {
                    addr[1] = punycode.toASCII(unescapeComponent(addr[1], options).toLowerCase());
                } catch (e) {
                    mailtoComponents.error = mailtoComponents.error || "Email address's domain name can not be converted to ASCII via punycode: " + e;
                }
            } else {
                addr[1] = unescapeComponent(addr[1], options).toLowerCase();
            }
            to[_x2] = addr.join("@");
        }
        return mailtoComponents;
    },
    serialize: function serialize$$1(mailtoComponents, options) {
        var components = mailtoComponents;
        var to = toArray(mailtoComponents.to);
        if (to) {
            for (var x = 0, xl = to.length; x < xl; ++x) {
                var toAddr = String(to[x]);
                var atIdx = toAddr.lastIndexOf("@");
                var localPart = toAddr.slice(0, atIdx).replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_LOCAL_PART, pctEncChar);
                var domain = toAddr.slice(atIdx + 1);
                //convert IDN via punycode
                try {
                    domain = !options.iri ? punycode.toASCII(unescapeComponent(domain, options).toLowerCase()) : punycode.toUnicode(domain);
                } catch (e) {
                    components.error = components.error || "Email address's domain name can not be converted to " + (!options.iri ? "ASCII" : "Unicode") + " via punycode: " + e;
                }
                to[x] = localPart + "@" + domain;
            }
            components.path = to.join(",");
        }
        var headers = mailtoComponents.headers = mailtoComponents.headers || {};
        if (mailtoComponents.subject) headers["subject"] = mailtoComponents.subject;
        if (mailtoComponents.body) headers["body"] = mailtoComponents.body;
        var fields = [];
        for (var name in headers) {
            if (headers[name] !== O[name]) {
                fields.push(name.replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_HFNAME, pctEncChar) + "=" + headers[name].replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_HFVALUE, pctEncChar));
            }
        }
        if (fields.length) {
            components.query = fields.join("&");
        }
        return components;
    }
};

var URN_PARSE = /^([^\:]+)\:(.*)/;
//RFC 2141
var handler$3 = {
    scheme: "urn",
    parse: function parse$$1(components, options) {
        var matches = components.path && components.path.match(URN_PARSE);
        var urnComponents = components;
        if (matches) {
            var scheme = options.scheme || urnComponents.scheme || "urn";
            var nid = matches[1].toLowerCase();
            var nss = matches[2];
            var urnScheme = scheme + ":" + (options.nid || nid);
            var schemeHandler = SCHEMES[urnScheme];
            urnComponents.nid = nid;
            urnComponents.nss = nss;
            urnComponents.path = undefined;
            if (schemeHandler) {
                urnComponents = schemeHandler.parse(urnComponents, options);
            }
        } else {
            urnComponents.error = urnComponents.error || "URN can not be parsed.";
        }
        return urnComponents;
    },
    serialize: function serialize$$1(urnComponents, options) {
        var scheme = options.scheme || urnComponents.scheme || "urn";
        var nid = urnComponents.nid;
        var urnScheme = scheme + ":" + (options.nid || nid);
        var schemeHandler = SCHEMES[urnScheme];
        if (schemeHandler) {
            urnComponents = schemeHandler.serialize(urnComponents, options);
        }
        var uriComponents = urnComponents;
        var nss = urnComponents.nss;
        uriComponents.path = (nid || options.nid) + ":" + nss;
        return uriComponents;
    }
};

var UUID = /^[0-9A-Fa-f]{8}(?:\-[0-9A-Fa-f]{4}){3}\-[0-9A-Fa-f]{12}$/;
//RFC 4122
var handler$4 = {
    scheme: "urn:uuid",
    parse: function parse(urnComponents, options) {
        var uuidComponents = urnComponents;
        uuidComponents.uuid = uuidComponents.nss;
        uuidComponents.nss = undefined;
        if (!options.tolerant && (!uuidComponents.uuid || !uuidComponents.uuid.match(UUID))) {
            uuidComponents.error = uuidComponents.error || "UUID is not valid.";
        }
        return uuidComponents;
    },
    serialize: function serialize(uuidComponents, options) {
        var urnComponents = uuidComponents;
        //normalize UUID
        urnComponents.nss = (uuidComponents.uuid || "").toLowerCase();
        return urnComponents;
    }
};

SCHEMES[handler.scheme] = handler;
SCHEMES[handler$1.scheme] = handler$1;
SCHEMES[handler$2.scheme] = handler$2;
SCHEMES[handler$3.scheme] = handler$3;
SCHEMES[handler$4.scheme] = handler$4;

exports.SCHEMES = SCHEMES;
exports.pctEncChar = pctEncChar;
exports.pctDecChars = pctDecChars;
exports.parse = parse;
exports.removeDotSegments = removeDotSegments;
exports.serialize = serialize;
exports.resolveComponents = resolveComponents;
exports.resolve = resolve;
exports.normalize = normalize;
exports.equal = equal;
exports.escapeComponent = escapeComponent;
exports.unescapeComponent = unescapeComponent;

Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=uri.all.js.map


/***/ }),

/***/ "./node_modules/whatwg-fetch/fetch.js":
/*!********************************************!*\
  !*** ./node_modules/whatwg-fetch/fetch.js ***!
  \********************************************/
/*! exports provided: Headers, Request, Response, DOMException, fetch */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Headers", function() { return Headers; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Request", function() { return Request; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Response", function() { return Response; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DOMException", function() { return DOMException; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fetch", function() { return fetch; });
var support = {
  searchParams: 'URLSearchParams' in self,
  iterable: 'Symbol' in self && 'iterator' in Symbol,
  blob:
    'FileReader' in self &&
    'Blob' in self &&
    (function() {
      try {
        new Blob()
        return true
      } catch (e) {
        return false
      }
    })(),
  formData: 'FormData' in self,
  arrayBuffer: 'ArrayBuffer' in self
}

function isDataView(obj) {
  return obj && DataView.prototype.isPrototypeOf(obj)
}

if (support.arrayBuffer) {
  var viewClasses = [
    '[object Int8Array]',
    '[object Uint8Array]',
    '[object Uint8ClampedArray]',
    '[object Int16Array]',
    '[object Uint16Array]',
    '[object Int32Array]',
    '[object Uint32Array]',
    '[object Float32Array]',
    '[object Float64Array]'
  ]

  var isArrayBufferView =
    ArrayBuffer.isView ||
    function(obj) {
      return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
    }
}

function normalizeName(name) {
  if (typeof name !== 'string') {
    name = String(name)
  }
  if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) {
    throw new TypeError('Invalid character in header field name')
  }
  return name.toLowerCase()
}

function normalizeValue(value) {
  if (typeof value !== 'string') {
    value = String(value)
  }
  return value
}

// Build a destructive iterator for the value list
function iteratorFor(items) {
  var iterator = {
    next: function() {
      var value = items.shift()
      return {done: value === undefined, value: value}
    }
  }

  if (support.iterable) {
    iterator[Symbol.iterator] = function() {
      return iterator
    }
  }

  return iterator
}

function Headers(headers) {
  this.map = {}

  if (headers instanceof Headers) {
    headers.forEach(function(value, name) {
      this.append(name, value)
    }, this)
  } else if (Array.isArray(headers)) {
    headers.forEach(function(header) {
      this.append(header[0], header[1])
    }, this)
  } else if (headers) {
    Object.getOwnPropertyNames(headers).forEach(function(name) {
      this.append(name, headers[name])
    }, this)
  }
}

Headers.prototype.append = function(name, value) {
  name = normalizeName(name)
  value = normalizeValue(value)
  var oldValue = this.map[name]
  this.map[name] = oldValue ? oldValue + ', ' + value : value
}

Headers.prototype['delete'] = function(name) {
  delete this.map[normalizeName(name)]
}

Headers.prototype.get = function(name) {
  name = normalizeName(name)
  return this.has(name) ? this.map[name] : null
}

Headers.prototype.has = function(name) {
  return this.map.hasOwnProperty(normalizeName(name))
}

Headers.prototype.set = function(name, value) {
  this.map[normalizeName(name)] = normalizeValue(value)
}

Headers.prototype.forEach = function(callback, thisArg) {
  for (var name in this.map) {
    if (this.map.hasOwnProperty(name)) {
      callback.call(thisArg, this.map[name], name, this)
    }
  }
}

Headers.prototype.keys = function() {
  var items = []
  this.forEach(function(value, name) {
    items.push(name)
  })
  return iteratorFor(items)
}

Headers.prototype.values = function() {
  var items = []
  this.forEach(function(value) {
    items.push(value)
  })
  return iteratorFor(items)
}

Headers.prototype.entries = function() {
  var items = []
  this.forEach(function(value, name) {
    items.push([name, value])
  })
  return iteratorFor(items)
}

if (support.iterable) {
  Headers.prototype[Symbol.iterator] = Headers.prototype.entries
}

function consumed(body) {
  if (body.bodyUsed) {
    return Promise.reject(new TypeError('Already read'))
  }
  body.bodyUsed = true
}

function fileReaderReady(reader) {
  return new Promise(function(resolve, reject) {
    reader.onload = function() {
      resolve(reader.result)
    }
    reader.onerror = function() {
      reject(reader.error)
    }
  })
}

function readBlobAsArrayBuffer(blob) {
  var reader = new FileReader()
  var promise = fileReaderReady(reader)
  reader.readAsArrayBuffer(blob)
  return promise
}

function readBlobAsText(blob) {
  var reader = new FileReader()
  var promise = fileReaderReady(reader)
  reader.readAsText(blob)
  return promise
}

function readArrayBufferAsText(buf) {
  var view = new Uint8Array(buf)
  var chars = new Array(view.length)

  for (var i = 0; i < view.length; i++) {
    chars[i] = String.fromCharCode(view[i])
  }
  return chars.join('')
}

function bufferClone(buf) {
  if (buf.slice) {
    return buf.slice(0)
  } else {
    var view = new Uint8Array(buf.byteLength)
    view.set(new Uint8Array(buf))
    return view.buffer
  }
}

function Body() {
  this.bodyUsed = false

  this._initBody = function(body) {
    this._bodyInit = body
    if (!body) {
      this._bodyText = ''
    } else if (typeof body === 'string') {
      this._bodyText = body
    } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
      this._bodyBlob = body
    } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
      this._bodyFormData = body
    } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
      this._bodyText = body.toString()
    } else if (support.arrayBuffer && support.blob && isDataView(body)) {
      this._bodyArrayBuffer = bufferClone(body.buffer)
      // IE 10-11 can't handle a DataView body.
      this._bodyInit = new Blob([this._bodyArrayBuffer])
    } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
      this._bodyArrayBuffer = bufferClone(body)
    } else {
      this._bodyText = body = Object.prototype.toString.call(body)
    }

    if (!this.headers.get('content-type')) {
      if (typeof body === 'string') {
        this.headers.set('content-type', 'text/plain;charset=UTF-8')
      } else if (this._bodyBlob && this._bodyBlob.type) {
        this.headers.set('content-type', this._bodyBlob.type)
      } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
        this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
      }
    }
  }

  if (support.blob) {
    this.blob = function() {
      var rejected = consumed(this)
      if (rejected) {
        return rejected
      }

      if (this._bodyBlob) {
        return Promise.resolve(this._bodyBlob)
      } else if (this._bodyArrayBuffer) {
        return Promise.resolve(new Blob([this._bodyArrayBuffer]))
      } else if (this._bodyFormData) {
        throw new Error('could not read FormData body as blob')
      } else {
        return Promise.resolve(new Blob([this._bodyText]))
      }
    }

    this.arrayBuffer = function() {
      if (this._bodyArrayBuffer) {
        return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
      } else {
        return this.blob().then(readBlobAsArrayBuffer)
      }
    }
  }

  this.text = function() {
    var rejected = consumed(this)
    if (rejected) {
      return rejected
    }

    if (this._bodyBlob) {
      return readBlobAsText(this._bodyBlob)
    } else if (this._bodyArrayBuffer) {
      return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
    } else if (this._bodyFormData) {
      throw new Error('could not read FormData body as text')
    } else {
      return Promise.resolve(this._bodyText)
    }
  }

  if (support.formData) {
    this.formData = function() {
      return this.text().then(decode)
    }
  }

  this.json = function() {
    return this.text().then(JSON.parse)
  }

  return this
}

// HTTP methods whose capitalization should be normalized
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']

function normalizeMethod(method) {
  var upcased = method.toUpperCase()
  return methods.indexOf(upcased) > -1 ? upcased : method
}

function Request(input, options) {
  options = options || {}
  var body = options.body

  if (input instanceof Request) {
    if (input.bodyUsed) {
      throw new TypeError('Already read')
    }
    this.url = input.url
    this.credentials = input.credentials
    if (!options.headers) {
      this.headers = new Headers(input.headers)
    }
    this.method = input.method
    this.mode = input.mode
    this.signal = input.signal
    if (!body && input._bodyInit != null) {
      body = input._bodyInit
      input.bodyUsed = true
    }
  } else {
    this.url = String(input)
  }

  this.credentials = options.credentials || this.credentials || 'same-origin'
  if (options.headers || !this.headers) {
    this.headers = new Headers(options.headers)
  }
  this.method = normalizeMethod(options.method || this.method || 'GET')
  this.mode = options.mode || this.mode || null
  this.signal = options.signal || this.signal
  this.referrer = null

  if ((this.method === 'GET' || this.method === 'HEAD') && body) {
    throw new TypeError('Body not allowed for GET or HEAD requests')
  }
  this._initBody(body)
}

Request.prototype.clone = function() {
  return new Request(this, {body: this._bodyInit})
}

function decode(body) {
  var form = new FormData()
  body
    .trim()
    .split('&')
    .forEach(function(bytes) {
      if (bytes) {
        var split = bytes.split('=')
        var name = split.shift().replace(/\+/g, ' ')
        var value = split.join('=').replace(/\+/g, ' ')
        form.append(decodeURIComponent(name), decodeURIComponent(value))
      }
    })
  return form
}

function parseHeaders(rawHeaders) {
  var headers = new Headers()
  // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
  // https://tools.ietf.org/html/rfc7230#section-3.2
  var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
  preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
    var parts = line.split(':')
    var key = parts.shift().trim()
    if (key) {
      var value = parts.join(':').trim()
      headers.append(key, value)
    }
  })
  return headers
}

Body.call(Request.prototype)

function Response(bodyInit, options) {
  if (!options) {
    options = {}
  }

  this.type = 'default'
  this.status = options.status === undefined ? 200 : options.status
  this.ok = this.status >= 200 && this.status < 300
  this.statusText = 'statusText' in options ? options.statusText : 'OK'
  this.headers = new Headers(options.headers)
  this.url = options.url || ''
  this._initBody(bodyInit)
}

Body.call(Response.prototype)

Response.prototype.clone = function() {
  return new Response(this._bodyInit, {
    status: this.status,
    statusText: this.statusText,
    headers: new Headers(this.headers),
    url: this.url
  })
}

Response.error = function() {
  var response = new Response(null, {status: 0, statusText: ''})
  response.type = 'error'
  return response
}

var redirectStatuses = [301, 302, 303, 307, 308]

Response.redirect = function(url, status) {
  if (redirectStatuses.indexOf(status) === -1) {
    throw new RangeError('Invalid status code')
  }

  return new Response(null, {status: status, headers: {location: url}})
}

var DOMException = self.DOMException
try {
  new DOMException()
} catch (err) {
  DOMException = function(message, name) {
    this.message = message
    this.name = name
    var error = Error(message)
    this.stack = error.stack
  }
  DOMException.prototype = Object.create(Error.prototype)
  DOMException.prototype.constructor = DOMException
}

function fetch(input, init) {
  return new Promise(function(resolve, reject) {
    var request = new Request(input, init)

    if (request.signal && request.signal.aborted) {
      return reject(new DOMException('Aborted', 'AbortError'))
    }

    var xhr = new XMLHttpRequest()

    function abortXhr() {
      xhr.abort()
    }

    xhr.onload = function() {
      var options = {
        status: xhr.status,
        statusText: xhr.statusText,
        headers: parseHeaders(xhr.getAllResponseHeaders() || '')
      }
      options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
      var body = 'response' in xhr ? xhr.response : xhr.responseText
      resolve(new Response(body, options))
    }

    xhr.onerror = function() {
      reject(new TypeError('Network request failed'))
    }

    xhr.ontimeout = function() {
      reject(new TypeError('Network request failed'))
    }

    xhr.onabort = function() {
      reject(new DOMException('Aborted', 'AbortError'))
    }

    xhr.open(request.method, request.url, true)

    if (request.credentials === 'include') {
      xhr.withCredentials = true
    } else if (request.credentials === 'omit') {
      xhr.withCredentials = false
    }

    if ('responseType' in xhr && support.blob) {
      xhr.responseType = 'blob'
    }

    request.headers.forEach(function(value, name) {
      xhr.setRequestHeader(name, value)
    })

    if (request.signal) {
      request.signal.addEventListener('abort', abortXhr)

      xhr.onreadystatechange = function() {
        // DONE (success or failure)
        if (xhr.readyState === 4) {
          request.signal.removeEventListener('abort', abortXhr)
        }
      }
    }

    xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
  })
}

fetch.polyfill = true

if (!self.fetch) {
  self.fetch = fetch
  self.Headers = Headers
  self.Request = Request
  self.Response = Response
}


/***/ }),

/***/ "./node_modules/worker-loader/dist/workers/InlineWorker.js":
/*!*****************************************************************!*\
  !*** ./node_modules/worker-loader/dist/workers/InlineWorker.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string

var URL = window.URL || window.webkitURL;

module.exports = function (content, url) {
  try {
    try {
      var blob;

      try {
        // BlobBuilder = Deprecated, but widely implemented
        var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;

        blob = new BlobBuilder();

        blob.append(content);

        blob = blob.getBlob();
      } catch (e) {
        // The proposed API
        blob = new Blob([content]);
      }

      return new Worker(URL.createObjectURL(blob));
    } catch (e) {
      return new Worker('data:application/javascript,' + encodeURIComponent(content));
    }
  } catch (e) {
    if (!url) {
      throw Error('Inline worker is not supported');
    }

    return new Worker(url);
  }
};

/***/ }),

/***/ "./res_built/atlas sync .*\\.json/":
/*!*****************************************************!*\
  !*** ./res_built/atlas sync nonrecursive .*\.json/ ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var map = {
	"./atlas0_hq.json": "./res_built/atlas/atlas0_hq.json",
	"./atlas0_lq.json": "./res_built/atlas/atlas0_lq.json",
	"./atlas0_mq.json": "./res_built/atlas/atlas0_mq.json"
};


function webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}
function webpackContextResolve(req) {
	if(!__webpack_require__.o(map, req)) {
		var e = new Error("Cannot find module '" + req + "'");
		e.code = 'MODULE_NOT_FOUND';
		throw e;
	}
	return map[req];
}
webpackContext.keys = function webpackContextKeys() {
	return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "./res_built/atlas sync .*\\.json/";

/***/ }),

/***/ "./res_built/atlas sync recursive ^\\.\\/.*$":
/*!***************************************!*\
  !*** ./res_built/atlas sync ^\.\/.*$ ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var map = {
	"./atlas0_hq": "./res_built/atlas/atlas0_hq.json",
	"./atlas0_hq.atlas": "./res_built/atlas/atlas0_hq.atlas",
	"./atlas0_hq.json": "./res_built/atlas/atlas0_hq.json",
	"./atlas0_hq.png": "./res_built/atlas/atlas0_hq.png",
	"./atlas0_lq": "./res_built/atlas/atlas0_lq.json",
	"./atlas0_lq.atlas": "./res_built/atlas/atlas0_lq.atlas",
	"./atlas0_lq.json": "./res_built/atlas/atlas0_lq.json",
	"./atlas0_lq.png": "./res_built/atlas/atlas0_lq.png",
	"./atlas0_mq": "./res_built/atlas/atlas0_mq.json",
	"./atlas0_mq.atlas": "./res_built/atlas/atlas0_mq.atlas",
	"./atlas0_mq.json": "./res_built/atlas/atlas0_mq.json",
	"./atlas0_mq.png": "./res_built/atlas/atlas0_mq.png"
};


function webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}
function webpackContextResolve(req) {
	if(!__webpack_require__.o(map, req)) {
		var e = new Error("Cannot find module '" + req + "'");
		e.code = 'MODULE_NOT_FOUND';
		throw e;
	}
	return map[req];
}
webpackContext.keys = function webpackContextKeys() {
	return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "./res_built/atlas sync recursive ^\\.\\/.*$";

/***/ }),

/***/ "./res_built/atlas/atlas0_hq.atlas":
/*!*****************************************!*\
  !*** ./res_built/atlas/atlas0_hq.atlas ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports) {


atlas0_hq.png
size: 2048, 2048
format: RGBA8888
filter: Nearest, Nearest
repeat: none
sprites/belt/built/forward_1
  rotate: false
  xy: 1240, 1465
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_10
  rotate: false
  xy: 829, 200
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_11
  rotate: false
  xy: 829, 54
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_12
  rotate: false
  xy: 947, 200
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_13
  rotate: false
  xy: 947, 54
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_2
  rotate: false
  xy: 675, 1078
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_3
  rotate: false
  xy: 693, 932
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_4
  rotate: false
  xy: 699, 786
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_5
  rotate: false
  xy: 811, 941
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_6
  rotate: false
  xy: 817, 795
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_7
  rotate: false
  xy: 1237, 1182
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_8
  rotate: false
  xy: 692, 624
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_9
  rotate: false
  xy: 790, 478
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/left_1
  rotate: false
  xy: 1308, 918
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_10
  rotate: false
  xy: 1172, 567
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_11
  rotate: false
  xy: 1086, 402
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_12
  rotate: false
  xy: 1304, 522
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_13
  rotate: false
  xy: 1065, 270
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_2
  rotate: false
  xy: 1308, 786
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_3
  rotate: false
  xy: 1544, 1048
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_4
  rotate: false
  xy: 1308, 654
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_5
  rotate: false
  xy: 1440, 629
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_6
  rotate: false
  xy: 935, 666
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_7
  rotate: false
  xy: 908, 534
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_8
  rotate: false
  xy: 954, 402
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_9
  rotate: false
  xy: 1040, 534
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/right_1
  rotate: false
  xy: 1065, 138
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_10
  rotate: false
  xy: 1489, 119
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_11
  rotate: false
  xy: 1197, 13
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_12
  rotate: false
  xy: 1544, 916
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_13
  rotate: false
  xy: 1548, 784
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_2
  rotate: false
  xy: 1065, 6
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_3
  rotate: false
  xy: 1225, 277
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_4
  rotate: false
  xy: 1357, 383
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_5
  rotate: false
  xy: 1357, 251
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_6
  rotate: false
  xy: 1489, 383
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_7
  rotate: false
  xy: 1489, 251
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_8
  rotate: false
  xy: 1225, 145
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_9
  rotate: false
  xy: 1357, 119
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/blueprints/analyzer
  rotate: false
  xy: 841, 1612
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/blueprints/balancer
  rotate: false
  xy: 291, 1060
  size: 257, 144
  orig: 288, 144
  offset: 17, 0
  index: -1
sprites/blueprints/balancer-merger
  rotate: false
  xy: 396, 338
  size: 139, 138
  orig: 144, 144
  offset: 5, 4
  index: -1
sprites/blueprints/balancer-merger-inverse
  rotate: false
  xy: 394, 52
  size: 142, 138
  orig: 144, 144
  offset: 0, 4
  index: -1
sprites/blueprints/balancer-splitter
  rotate: false
  xy: 537, 338
  size: 139, 138
  orig: 144, 144
  offset: 5, 4
  index: -1
sprites/blueprints/balancer-splitter-inverse
  rotate: false
  xy: 678, 338
  size: 142, 138
  orig: 144, 144
  offset: 0, 4
  index: -1
sprites/blueprints/belt_left
  rotate: false
  xy: 1915, 1189
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/blueprints/belt_right
  rotate: false
  xy: 822, 346
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/blueprints/belt_top
  rotate: false
  xy: 1879, 1757
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/blueprints/comparator
  rotate: false
  xy: 250, 343
  size: 144, 133
  orig: 144, 144
  offset: 0, 11
  index: -1
sprites/blueprints/constant_signal
  rotate: false
  xy: 1440, 761
  size: 106, 130
  orig: 144, 144
  offset: 19, 14
  index: -1
sprites/blueprints/cutter
  rotate: false
  xy: 291, 914
  size: 257, 144
  orig: 288, 144
  offset: 17, 0
  index: -1
sprites/blueprints/cutter-quad
  rotate: false
  xy: 1, 1206
  size: 526, 144
  orig: 576, 144
  offset: 24, 0
  index: -1
sprites/blueprints/display
  rotate: false
  xy: 1495, 1192
  size: 128, 136
  orig: 144, 144
  offset: 8, 0
  index: -1
sprites/blueprints/filter
  rotate: false
  xy: 1077, 1757
  size: 268, 144
  orig: 288, 144
  offset: 16, 0
  index: -1
sprites/blueprints/item_producer
  rotate: false
  xy: 1916, 1321
  size: 131, 142
  orig: 144, 144
  offset: 8, 2
  index: -1
sprites/blueprints/lever
  rotate: false
  xy: 1067, 720
  size: 100, 116
  orig: 144, 144
  offset: 22, 19
  index: -1
sprites/blueprints/logic_gate
  rotate: false
  xy: 945, 1204
  size: 144, 122
  orig: 144, 144
  offset: 0, 22
  index: -1
sprites/blueprints/logic_gate-not
  rotate: false
  xy: 550, 1062
  size: 123, 144
  orig: 144, 144
  offset: 11, 0
  index: -1
sprites/blueprints/logic_gate-or
  rotate: false
  xy: 1091, 1202
  size: 144, 124
  orig: 144, 144
  offset: 0, 20
  index: -1
sprites/blueprints/logic_gate-xor
  rotate: false
  xy: 248, 66
  size: 144, 129
  orig: 144, 144
  offset: 0, 15
  index: -1
sprites/blueprints/miner
  rotate: false
  xy: 508, 478
  size: 137, 143
  orig: 144, 144
  offset: 4, 1
  index: -1
sprites/blueprints/miner-chainable
  rotate: false
  xy: 553, 623
  size: 137, 143
  orig: 144, 144
  offset: 4, 1
  index: -1
sprites/blueprints/mixer
  rotate: false
  xy: 1616, 1757
  size: 261, 144
  orig: 288, 144
  offset: 13, 0
  index: -1
sprites/blueprints/painter
  rotate: false
  xy: 1090, 1903
  size: 288, 144
  orig: 288, 144
  offset: 0, 0
  index: -1
sprites/blueprints/painter-double
  rotate: false
  xy: 1, 924
  size: 288, 280
  orig: 288, 288
  offset: 0, 8
  index: -1
sprites/blueprints/painter-mirrored
  rotate: false
  xy: 1380, 1903
  size: 288, 144
  orig: 288, 144
  offset: 0, 0
  index: -1
sprites/blueprints/painter-quad
  rotate: false
  xy: 1, 1352
  size: 538, 144
  orig: 576, 144
  offset: 12, 0
  index: -1
sprites/blueprints/reader
  rotate: false
  xy: 394, 192
  size: 142, 144
  orig: 144, 144
  offset: 1, 0
  index: -1
sprites/blueprints/rotater
  rotate: false
  xy: 554, 770
  size: 143, 144
  orig: 144, 144
  offset: 1, 0
  index: -1
sprites/blueprints/rotater-ccw
  rotate: false
  xy: 538, 192
  size: 143, 144
  orig: 144, 144
  offset: 1, 0
  index: -1
sprites/blueprints/rotater-rotate180
  rotate: false
  xy: 538, 46
  size: 143, 144
  orig: 144, 144
  offset: 1, 0
  index: -1
sprites/blueprints/stacker
  rotate: false
  xy: 291, 768
  size: 261, 144
  orig: 288, 144
  offset: 13, 0
  index: -1
sprites/blueprints/storage
  rotate: false
  xy: 1, 354
  size: 247, 287
  orig: 288, 288
  offset: 21, 0
  index: -1
sprites/blueprints/transistor
  rotate: false
  xy: 1061, 984
  size: 102, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/blueprints/transistor-mirrored
  rotate: false
  xy: 1442, 1039
  size: 100, 144
  orig: 144, 144
  offset: 44, 0
  index: -1
sprites/blueprints/trash
  rotate: false
  xy: 683, 46
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/blueprints/underground_belt_entry
  rotate: false
  xy: 917, 1090
  size: 138, 112
  orig: 144, 144
  offset: 4, 0
  index: -1
sprites/blueprints/underground_belt_entry-tier2
  rotate: false
  xy: 1775, 1212
  size: 138, 125
  orig: 144, 144
  offset: 4, 0
  index: -1
sprites/blueprints/underground_belt_exit
  rotate: false
  xy: 1436, 515
  size: 138, 112
  orig: 144, 144
  offset: 4, 32
  index: -1
sprites/blueprints/underground_belt_exit-tier2
  rotate: false
  xy: 529, 1208
  size: 139, 112
  orig: 144, 144
  offset: 4, 32
  index: -1
sprites/blueprints/virtual_processor
  rotate: false
  xy: 1483, 1614
  size: 144, 141
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/blueprints/virtual_processor-painter
  rotate: false
  xy: 1358, 1466
  size: 131, 144
  orig: 144, 144
  offset: 13, 0
  index: -1
sprites/blueprints/virtual_processor-rotater
  rotate: false
  xy: 1960, 1903
  size: 58, 144
  orig: 144, 144
  offset: 44, 0
  index: -1
sprites/blueprints/virtual_processor-stacker
  rotate: false
  xy: 929, 944
  size: 130, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/blueprints/virtual_processor-unstacker
  rotate: false
  xy: 1191, 1611
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/blueprints/wire_tunnel
  rotate: false
  xy: 1355, 1185
  size: 138, 135
  orig: 144, 144
  offset: 4, 5
  index: -1
sprites/buildings/analyzer
  rotate: false
  xy: 813, 1466
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/buildings/balancer
  rotate: false
  xy: 541, 1322
  size: 256, 143
  orig: 288, 144
  offset: 17, 1
  index: -1
sprites/buildings/balancer-merger
  rotate: false
  xy: 1775, 1339
  size: 139, 136
  orig: 144, 144
  offset: 5, 5
  index: -1
sprites/buildings/balancer-merger-inverse
  rotate: false
  xy: 1629, 1477
  size: 141, 136
  orig: 144, 144
  offset: 0, 5
  index: -1
sprites/buildings/balancer-splitter
  rotate: false
  xy: 1089, 1328
  size: 139, 136
  orig: 144, 144
  offset: 5, 5
  index: -1
sprites/buildings/balancer-splitter-inverse
  rotate: false
  xy: 945, 1328
  size: 142, 136
  orig: 144, 144
  offset: 0, 5
  index: -1
sprites/buildings/belt_left
  rotate: false
  xy: 1915, 1057
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/belt/built/left_0
  rotate: false
  xy: 1915, 1057
  size: 130, 130
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/buildings/belt_right
  rotate: false
  xy: 1310, 1050
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/right_0
  rotate: false
  xy: 1310, 1050
  size: 130, 130
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/buildings/belt_top
  rotate: false
  xy: 1918, 1611
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/belt/built/forward_0
  rotate: false
  xy: 1918, 1611
  size: 116, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/buildings/comparator
  rotate: false
  xy: 1772, 1477
  size: 143, 133
  orig: 144, 144
  offset: 1, 11
  index: -1
sprites/buildings/constant_signal
  rotate: false
  xy: 1572, 653
  size: 104, 129
  orig: 144, 144
  offset: 20, 15
  index: -1
sprites/buildings/cutter
  rotate: false
  xy: 250, 478
  size: 256, 143
  orig: 288, 144
  offset: 17, 1
  index: -1
sprites/buildings/cutter-quad
  rotate: false
  xy: 551, 1758
  size: 524, 143
  orig: 576, 144
  offset: 25, 1
  index: -1
sprites/buildings/display
  rotate: false
  xy: 1230, 1328
  size: 126, 135
  orig: 144, 144
  offset: 9, 0
  index: -1
sprites/buildings/filter
  rotate: false
  xy: 1347, 1757
  size: 267, 144
  orig: 288, 144
  offset: 17, 0
  index: -1
sprites/buildings/hub
  rotate: false
  xy: 1, 1498
  size: 548, 549
  orig: 576, 576
  offset: 14, 11
  index: -1
sprites/buildings/item_producer
  rotate: false
  xy: 1358, 1322
  size: 130, 142
  orig: 144, 144
  offset: 8, 2
  index: -1
sprites/buildings/lever
  rotate: false
  xy: 1433, 3
  size: 98, 114
  orig: 144, 144
  offset: 23, 20
  index: -1
sprites/buildings/logic_gate
  rotate: false
  xy: 1625, 1180
  size: 143, 121
  orig: 144, 144
  offset: 1, 23
  index: -1
sprites/buildings/logic_gate-not
  rotate: false
  xy: 793, 1087
  size: 122, 144
  orig: 144, 144
  offset: 12, 0
  index: -1
sprites/buildings/logic_gate-or
  rotate: false
  xy: 1770, 1086
  size: 143, 124
  orig: 144, 144
  offset: 1, 20
  index: -1
sprites/buildings/logic_gate-xor
  rotate: false
  xy: 1165, 1051
  size: 143, 129
  orig: 144, 144
  offset: 1, 15
  index: -1
sprites/buildings/miner
  rotate: false
  xy: 1102, 1467
  size: 136, 142
  orig: 144, 144
  offset: 5, 2
  index: -1
sprites/buildings/miner-chainable
  rotate: false
  xy: 1491, 1470
  size: 136, 142
  orig: 144, 144
  offset: 5, 2
  index: -1
sprites/buildings/mixer
  rotate: false
  xy: 291, 623
  size: 260, 143
  orig: 288, 144
  offset: 14, 1
  index: -1
sprites/buildings/painter
  rotate: false
  xy: 551, 1612
  size: 288, 144
  orig: 288, 144
  offset: 0, 0
  index: -1
sprites/buildings/painter-double
  rotate: false
  xy: 1, 643
  size: 288, 279
  orig: 288, 288
  offset: 0, 9
  index: -1
sprites/buildings/painter-mirrored
  rotate: false
  xy: 1670, 1903
  size: 288, 144
  orig: 288, 144
  offset: 0, 0
  index: -1
sprites/buildings/painter-quad
  rotate: false
  xy: 551, 1903
  size: 537, 144
  orig: 576, 144
  offset: 13, 0
  index: -1
sprites/buildings/reader
  rotate: false
  xy: 550, 916
  size: 141, 144
  orig: 144, 144
  offset: 2, 0
  index: -1
sprites/buildings/rotater
  rotate: false
  xy: 647, 478
  size: 141, 143
  orig: 144, 144
  offset: 2, 1
  index: -1
sprites/buildings/rotater-ccw
  rotate: false
  xy: 1775, 1612
  size: 141, 143
  orig: 144, 144
  offset: 2, 1
  index: -1
sprites/buildings/rotater-rotate180
  rotate: false
  xy: 959, 1466
  size: 141, 143
  orig: 144, 144
  offset: 2, 1
  index: -1
sprites/buildings/stacker
  rotate: false
  xy: 551, 1467
  size: 260, 143
  orig: 288, 144
  offset: 14, 1
  index: -1
sprites/buildings/storage
  rotate: false
  xy: 1, 66
  size: 245, 286
  orig: 288, 288
  offset: 22, 0
  index: -1
sprites/buildings/transistor
  rotate: false
  xy: 1067, 838
  size: 100, 144
  orig: 144, 144
  offset: 1, 0
  index: -1
sprites/buildings/transistor-mirrored
  rotate: false
  xy: 1440, 893
  size: 99, 144
  orig: 144, 144
  offset: 45, 0
  index: -1
sprites/buildings/trash
  rotate: false
  xy: 1045, 1611
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/buildings/underground_belt_entry
  rotate: false
  xy: 1169, 812
  size: 137, 111
  orig: 144, 144
  offset: 5, 0
  index: -1
sprites/buildings/underground_belt_entry-tier2
  rotate: false
  xy: 1169, 925
  size: 137, 124
  orig: 144, 144
  offset: 5, 0
  index: -1
sprites/buildings/underground_belt_exit
  rotate: false
  xy: 1169, 699
  size: 137, 111
  orig: 144, 144
  offset: 5, 33
  index: -1
sprites/buildings/underground_belt_exit-tier2
  rotate: false
  xy: 1218, 409
  size: 137, 111
  orig: 144, 144
  offset: 5, 33
  index: -1
sprites/buildings/virtual_processor
  rotate: false
  xy: 1629, 1615
  size: 144, 140
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/buildings/virtual_processor-painter
  rotate: false
  xy: 1917, 1465
  size: 130, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/buildings/virtual_processor-rotater
  rotate: false
  xy: 987, 1612
  size: 56, 144
  orig: 144, 144
  offset: 45, 0
  index: -1
sprites/buildings/virtual_processor-stacker
  rotate: false
  xy: 935, 798
  size: 130, 144
  orig: 144, 144
  offset: 14, 0
  index: -1
sprites/buildings/virtual_processor-unstacker
  rotate: false
  xy: 1337, 1612
  size: 144, 143
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/buildings/wire_tunnel
  rotate: false
  xy: 1490, 1330
  size: 137, 134
  orig: 144, 144
  offset: 5, 5
  index: -1
sprites/colors/blue
  rotate: false
  xy: 1067, 669
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/cyan
  rotate: false
  xy: 954, 351
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/green
  rotate: false
  xy: 92, 15
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/purple
  rotate: false
  xy: 148, 15
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/red
  rotate: false
  xy: 204, 15
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/uncolored
  rotate: false
  xy: 260, 15
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/white
  rotate: false
  xy: 316, 15
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/colors/yellow
  rotate: false
  xy: 372, 1
  size: 54, 49
  orig: 54, 54
  offset: 0, 1
  index: -1
sprites/debug/acceptor_slot
  rotate: false
  xy: 372, 52
  size: 12, 12
  orig: 12, 12
  offset: 0, 0
  index: -1
sprites/debug/ejector_slot
  rotate: false
  xy: 250, 629
  size: 12, 12
  orig: 12, 12
  offset: 0, 0
  index: -1
sprites/misc/hub_direction_indicator
  rotate: false
  xy: 1010, 352
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/misc/processor_disabled
  rotate: false
  xy: 1676, 923
  size: 78, 81
  orig: 96, 96
  offset: 10, 5
  index: -1
sprites/misc/processor_disconnected
  rotate: false
  xy: 1763, 1000
  size: 65, 84
  orig: 96, 96
  offset: 17, 4
  index: -1
sprites/misc/reader_overlay
  rotate: false
  xy: 1057, 1130
  size: 104, 70
  orig: 144, 144
  offset: 20, 36
  index: -1
sprites/misc/slot_bad_arrow
  rotate: false
  xy: 528, 9
  size: 35, 35
  orig: 39, 39
  offset: 2, 2
  index: -1
sprites/misc/slot_good_arrow
  rotate: false
  xy: 1727, 717
  size: 35, 39
  orig: 39, 39
  offset: 2, 0
  index: -1
sprites/misc/storage_overlay
  rotate: false
  xy: 1, 20
  size: 89, 44
  orig: 90, 45
  offset: 1, 0
  index: -1
sprites/misc/waypoint
  rotate: false
  xy: 1123, 670
  size: 38, 48
  orig: 48, 48
  offset: 5, 0
  index: -1
sprites/misc/waypoint_wires
  rotate: false
  xy: 428, 2
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/boolean_false
  rotate: false
  xy: 1727, 674
  size: 31, 41
  orig: 48, 48
  offset: 9, 2
  index: -1
sprites/wires/boolean_true
  rotate: false
  xy: 2025, 1860
  size: 22, 41
  orig: 48, 48
  offset: 11, 2
  index: -1
sprites/wires/display/blue
  rotate: false
  xy: 1756, 951
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/display/cyan
  rotate: false
  xy: 1680, 766
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/display/green
  rotate: false
  xy: 1678, 717
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/display/purple
  rotate: false
  xy: 1678, 668
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/display/red
  rotate: false
  xy: 1744, 856
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/display/white
  rotate: false
  xy: 1744, 807
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/display/yellow
  rotate: false
  xy: 1729, 758
  size: 47, 47
  orig: 49, 49
  offset: 1, 1
  index: -1
sprites/wires/lever_on
  rotate: false
  xy: 1329, 3
  size: 102, 114
  orig: 144, 144
  offset: 21, 20
  index: -1
sprites/wires/logical_acceptor
  rotate: false
  xy: 1680, 815
  size: 62, 106
  orig: 144, 144
  offset: 43, 38
  index: -1
sprites/wires/logical_ejector
  rotate: false
  xy: 1830, 1017
  size: 60, 67
  orig: 144, 144
  offset: 44, 77
  index: -1
sprites/wires/network_conflict
  rotate: false
  xy: 1756, 905
  size: 47, 44
  orig: 48, 48
  offset: 1, 2
  index: -1
sprites/wires/network_empty
  rotate: false
  xy: 908, 484
  size: 41, 48
  orig: 48, 48
  offset: 5, 0
  index: -1
sprites/wires/overlay_tile
  rotate: false
  xy: 670, 1224
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_cross
  rotate: false
  xy: 799, 1320
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_forward
  rotate: false
  xy: 1997, 1757
  size: 26, 144
  orig: 144, 144
  offset: 59, 0
  index: -1
sprites/wires/sets/conflict_split
  rotate: false
  xy: 768, 1233
  size: 144, 85
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_turn
  rotate: false
  xy: 1676, 1093
  size: 85, 85
  orig: 144, 144
  offset: 59, 0
  index: -1
sprites/wires/sets/first_cross
  rotate: false
  xy: 248, 197
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/wires/sets/first_forward
  rotate: false
  xy: 2020, 1903
  size: 26, 144
  orig: 144, 144
  offset: 59, 0
  index: -1
sprites/wires/sets/first_split
  rotate: false
  xy: 1629, 1390
  size: 144, 85
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/wires/sets/first_turn
  rotate: false
  xy: 1533, 32
  size: 85, 85
  orig: 144, 144
  offset: 59, 0
  index: -1
sprites/wires/sets/second_cross
  rotate: false
  xy: 683, 192
  size: 144, 144
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/wires/sets/second_forward
  rotate: false
  xy: 1197, 256
  size: 26, 144
  orig: 144, 144
  offset: 59, 0
  index: -1
sprites/wires/sets/second_split
  rotate: false
  xy: 1629, 1303
  size: 144, 85
  orig: 144, 144
  offset: 0, 0
  index: -1
sprites/wires/sets/second_turn
  rotate: false
  xy: 1676, 1006
  size: 85, 85
  orig: 144, 144
  offset: 59, 0
  index: -1
sprites/wires/wires_preview
  rotate: false
  xy: 478, 2
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1


/***/ }),

/***/ "./res_built/atlas/atlas0_hq.json":
/*!****************************************!*\
  !*** ./res_built/atlas/atlas0_hq.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./res_built/atlas/atlas0_hq.png":
/*!***************************************!*\
  !*** ./res_built/atlas/atlas0_hq.png ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports) {



/***/ }),

/***/ "./res_built/atlas/atlas0_lq.atlas":
/*!*****************************************!*\
  !*** ./res_built/atlas/atlas0_lq.atlas ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports) {


atlas0_lq.png
size: 2048, 256
format: RGBA8888
filter: Nearest, Nearest
repeat: none
sprites/belt/built/forward_1
  rotate: false
  xy: 565, 10
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_10
  rotate: false
  xy: 1724, 14
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_11
  rotate: false
  xy: 1777, 111
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_12
  rotate: false
  xy: 1994, 119
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_13
  rotate: false
  xy: 1766, 14
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_2
  rotate: false
  xy: 607, 11
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_3
  rotate: false
  xy: 1726, 160
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_4
  rotate: false
  xy: 1768, 161
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_5
  rotate: false
  xy: 661, 12
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_6
  rotate: false
  xy: 1723, 110
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_7
  rotate: false
  xy: 1640, 65
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_8
  rotate: false
  xy: 1640, 15
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_9
  rotate: false
  xy: 1682, 11
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/left_1
  rotate: false
  xy: 1084, 61
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_10
  rotate: false
  xy: 1856, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_11
  rotate: false
  xy: 1902, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_12
  rotate: false
  xy: 1948, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_13
  rotate: false
  xy: 1654, 115
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_2
  rotate: false
  xy: 1130, 61
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_3
  rotate: false
  xy: 1176, 61
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_4
  rotate: false
  xy: 481, 13
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_5
  rotate: false
  xy: 1504, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_6
  rotate: false
  xy: 1550, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_7
  rotate: false
  xy: 1596, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_8
  rotate: false
  xy: 1642, 161
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_9
  rotate: false
  xy: 1810, 163
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/right_1
  rotate: false
  xy: 1502, 84
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_10
  rotate: false
  xy: 1902, 115
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_11
  rotate: false
  xy: 1948, 115
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_12
  rotate: false
  xy: 1765, 64
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_13
  rotate: false
  xy: 1808, 15
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_2
  rotate: false
  xy: 1548, 84
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_3
  rotate: false
  xy: 1594, 84
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_4
  rotate: false
  xy: 1456, 64
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_5
  rotate: false
  xy: 1502, 38
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_6
  rotate: false
  xy: 1548, 38
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_7
  rotate: false
  xy: 1594, 38
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_8
  rotate: false
  xy: 1719, 64
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_9
  rotate: false
  xy: 1856, 115
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/blueprints/analyzer
  rotate: false
  xy: 826, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/balancer
  rotate: false
  xy: 747, 157
  size: 87, 48
  orig: 96, 48
  offset: 5, 0
  index: -1
sprites/blueprints/balancer-merger
  rotate: false
  xy: 1176, 107
  size: 47, 48
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/blueprints/balancer-merger-inverse
  rotate: false
  xy: 925, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/balancer-splitter
  rotate: false
  xy: 1275, 157
  size: 47, 48
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/blueprints/balancer-splitter-inverse
  rotate: false
  xy: 876, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/belt_left
  rotate: false
  xy: 854, 61
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/belt_right
  rotate: false
  xy: 946, 61
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/blueprints/belt_top
  rotate: false
  xy: 1324, 157
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/blueprints/comparator
  rotate: false
  xy: 555, 60
  size: 48, 45
  orig: 48, 48
  offset: 0, 3
  index: -1
sprites/blueprints/constant_signal
  rotate: false
  xy: 527, 13
  size: 36, 44
  orig: 48, 48
  offset: 6, 4
  index: -1
sprites/blueprints/cutter
  rotate: false
  xy: 926, 207
  size: 87, 48
  orig: 96, 48
  offset: 5, 0
  index: -1
sprites/blueprints/cutter-quad
  rotate: false
  xy: 187, 157
  size: 177, 48
  orig: 192, 48
  offset: 7, 0
  index: -1
sprites/blueprints/display
  rotate: false
  xy: 1456, 110
  size: 44, 46
  orig: 48, 48
  offset: 2, 0
  index: -1
sprites/blueprints/filter
  rotate: false
  xy: 562, 157
  size: 92, 48
  orig: 96, 48
  offset: 4, 0
  index: -1
sprites/blueprints/item_producer
  rotate: false
  xy: 1859, 207
  size: 45, 48
  orig: 48, 48
  offset: 2, 0
  index: -1
sprites/blueprints/lever
  rotate: false
  xy: 1883, 72
  size: 35, 41
  orig: 48, 48
  offset: 6, 5
  index: -1
sprites/blueprints/logic_gate
  rotate: false
  xy: 1999, 213
  size: 48, 42
  orig: 48, 48
  offset: 0, 6
  index: -1
sprites/blueprints/logic_gate-not
  rotate: false
  xy: 1366, 108
  size: 43, 48
  orig: 48, 48
  offset: 3, 0
  index: -1
sprites/blueprints/logic_gate-or
  rotate: false
  xy: 1999, 169
  size: 48, 42
  orig: 48, 48
  offset: 0, 6
  index: -1
sprites/blueprints/logic_gate-xor
  rotate: false
  xy: 605, 61
  size: 48, 44
  orig: 48, 48
  offset: 0, 4
  index: -1
sprites/blueprints/miner
  rotate: false
  xy: 1514, 207
  size: 47, 48
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/blueprints/miner-chainable
  rotate: false
  xy: 1612, 207
  size: 47, 48
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/blueprints/mixer
  rotate: false
  xy: 745, 207
  size: 89, 48
  orig: 96, 48
  offset: 3, 0
  index: -1
sprites/blueprints/painter
  rotate: false
  xy: 366, 157
  size: 96, 48
  orig: 96, 48
  offset: 0, 0
  index: -1
sprites/blueprints/painter-double
  rotate: false
  xy: 187, 61
  size: 96, 94
  orig: 96, 96
  offset: 0, 2
  index: -1
sprites/blueprints/painter-mirrored
  rotate: false
  xy: 464, 157
  size: 96, 48
  orig: 96, 48
  offset: 0, 0
  index: -1
sprites/blueprints/painter-quad
  rotate: false
  xy: 1, 20
  size: 181, 48
  orig: 192, 48
  offset: 3, 0
  index: -1
sprites/blueprints/reader
  rotate: false
  xy: 926, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/rotater
  rotate: false
  xy: 1025, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/rotater-ccw
  rotate: false
  xy: 1165, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/rotater-rotate180
  rotate: false
  xy: 1026, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/stacker
  rotate: false
  xy: 656, 157
  size: 89, 48
  orig: 96, 48
  offset: 3, 0
  index: -1
sprites/blueprints/storage
  rotate: false
  xy: 383, 59
  size: 84, 96
  orig: 96, 96
  offset: 6, 0
  index: -1
sprites/blueprints/transistor
  rotate: false
  xy: 1682, 61
  size: 35, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/transistor-mirrored
  rotate: false
  xy: 1811, 61
  size: 34, 48
  orig: 48, 48
  offset: 14, 0
  index: -1
sprites/blueprints/trash
  rotate: false
  xy: 1076, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/underground_belt_entry
  rotate: false
  xy: 184, 21
  size: 48, 38
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/underground_belt_entry-tier2
  rotate: false
  xy: 655, 62
  size: 48, 43
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/underground_belt_exit
  rotate: false
  xy: 234, 21
  size: 48, 38
  orig: 48, 48
  offset: 0, 10
  index: -1
sprites/blueprints/underground_belt_exit-tier2
  rotate: false
  xy: 284, 21
  size: 48, 38
  orig: 48, 48
  offset: 0, 10
  index: -1
sprites/blueprints/virtual_processor
  rotate: false
  xy: 1175, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/virtual_processor-painter
  rotate: false
  xy: 1953, 207
  size: 44, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/blueprints/virtual_processor-rotater
  rotate: false
  xy: 1700, 111
  size: 21, 48
  orig: 48, 48
  offset: 14, 0
  index: -1
sprites/blueprints/virtual_processor-stacker
  rotate: false
  xy: 1412, 158
  size: 44, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/blueprints/virtual_processor-unstacker
  rotate: false
  xy: 1315, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/blueprints/wire_tunnel
  rotate: false
  xy: 1415, 208
  size: 48, 47
  orig: 48, 48
  offset: 0, 1
  index: -1
sprites/buildings/analyzer
  rotate: false
  xy: 1015, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/balancer
  rotate: false
  xy: 737, 107
  size: 87, 48
  orig: 96, 48
  offset: 5, 0
  index: -1
sprites/buildings/balancer-merger
  rotate: false
  xy: 1275, 108
  size: 47, 47
  orig: 48, 48
  offset: 1, 1
  index: -1
sprites/buildings/balancer-merger-inverse
  rotate: false
  xy: 1365, 208
  size: 48, 47
  orig: 48, 48
  offset: 0, 1
  index: -1
sprites/buildings/balancer-splitter
  rotate: false
  xy: 1465, 208
  size: 47, 47
  orig: 48, 48
  offset: 1, 1
  index: -1
sprites/buildings/balancer-splitter-inverse
  rotate: false
  xy: 1225, 108
  size: 48, 47
  orig: 48, 48
  offset: 0, 1
  index: -1
sprites/buildings/belt_left
  rotate: false
  xy: 900, 61
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/belt/built/left_0
  rotate: false
  xy: 900, 61
  size: 44, 44
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/belt_right
  rotate: false
  xy: 992, 61
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/right_0
  rotate: false
  xy: 992, 61
  size: 44, 44
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/buildings/belt_top
  rotate: false
  xy: 1324, 107
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/belt/built/forward_0
  rotate: false
  xy: 1324, 107
  size: 40, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/buildings/comparator
  rotate: false
  xy: 1710, 210
  size: 48, 45
  orig: 48, 48
  offset: 0, 3
  index: -1
sprites/buildings/constant_signal
  rotate: false
  xy: 1688, 161
  size: 36, 44
  orig: 48, 48
  offset: 6, 4
  index: -1
sprites/buildings/cutter
  rotate: false
  xy: 836, 157
  size: 87, 48
  orig: 96, 48
  offset: 5, 0
  index: -1
sprites/buildings/cutter-quad
  rotate: false
  xy: 370, 207
  size: 177, 48
  orig: 192, 48
  offset: 7, 0
  index: -1
sprites/buildings/display
  rotate: false
  xy: 1038, 59
  size: 44, 46
  orig: 48, 48
  offset: 2, 0
  index: -1
sprites/buildings/filter
  rotate: false
  xy: 555, 107
  size: 90, 48
  orig: 96, 48
  offset: 5, 0
  index: -1
sprites/buildings/hub
  rotate: false
  xy: 1, 70
  size: 184, 185
  orig: 192, 192
  offset: 4, 3
  index: -1
sprites/buildings/item_producer
  rotate: false
  xy: 1906, 207
  size: 45, 48
  orig: 48, 48
  offset: 2, 0
  index: -1
sprites/buildings/lever
  rotate: false
  xy: 1958, 73
  size: 34, 40
  orig: 48, 48
  offset: 7, 6
  index: -1
sprites/buildings/logic_gate
  rotate: false
  xy: 755, 64
  size: 48, 41
  orig: 48, 48
  offset: 0, 7
  index: -1
sprites/buildings/logic_gate-not
  rotate: false
  xy: 1411, 108
  size: 43, 48
  orig: 48, 48
  offset: 3, 0
  index: -1
sprites/buildings/logic_gate-or
  rotate: false
  xy: 705, 63
  size: 48, 42
  orig: 48, 48
  offset: 0, 6
  index: -1
sprites/buildings/logic_gate-xor
  rotate: false
  xy: 1760, 211
  size: 48, 44
  orig: 48, 48
  offset: 0, 4
  index: -1
sprites/buildings/miner
  rotate: false
  xy: 1563, 207
  size: 47, 48
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/buildings/miner-chainable
  rotate: false
  xy: 1661, 207
  size: 47, 48
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/buildings/mixer
  rotate: false
  xy: 647, 107
  size: 88, 48
  orig: 96, 48
  offset: 4, 0
  index: -1
sprites/buildings/painter
  rotate: false
  xy: 549, 207
  size: 96, 48
  orig: 96, 48
  offset: 0, 0
  index: -1
sprites/buildings/painter-double
  rotate: false
  xy: 285, 61
  size: 96, 94
  orig: 96, 96
  offset: 0, 2
  index: -1
sprites/buildings/painter-mirrored
  rotate: false
  xy: 647, 207
  size: 96, 48
  orig: 96, 48
  offset: 0, 0
  index: -1
sprites/buildings/painter-quad
  rotate: false
  xy: 187, 207
  size: 181, 48
  orig: 192, 48
  offset: 3, 0
  index: -1
sprites/buildings/reader
  rotate: false
  xy: 1115, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/rotater
  rotate: false
  xy: 976, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/rotater-ccw
  rotate: false
  xy: 1075, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/rotater-rotate180
  rotate: false
  xy: 1215, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/stacker
  rotate: false
  xy: 836, 207
  size: 88, 48
  orig: 96, 48
  offset: 4, 0
  index: -1
sprites/buildings/storage
  rotate: false
  xy: 469, 59
  size: 84, 96
  orig: 96, 96
  offset: 6, 0
  index: -1
sprites/buildings/transistor
  rotate: false
  xy: 1819, 113
  size: 35, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/transistor-mirrored
  rotate: false
  xy: 1847, 63
  size: 34, 48
  orig: 48, 48
  offset: 14, 0
  index: -1
sprites/buildings/trash
  rotate: false
  xy: 1265, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/underground_belt_entry
  rotate: false
  xy: 334, 21
  size: 47, 38
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/buildings/underground_belt_entry-tier2
  rotate: false
  xy: 805, 63
  size: 47, 42
  orig: 48, 48
  offset: 1, 0
  index: -1
sprites/buildings/underground_belt_exit
  rotate: false
  xy: 383, 19
  size: 47, 38
  orig: 48, 48
  offset: 1, 10
  index: -1
sprites/buildings/underground_belt_exit-tier2
  rotate: false
  xy: 432, 19
  size: 47, 38
  orig: 48, 48
  offset: 1, 10
  index: -1
sprites/buildings/virtual_processor
  rotate: false
  xy: 1126, 107
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/virtual_processor-painter
  rotate: false
  xy: 1366, 158
  size: 44, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/buildings/virtual_processor-rotater
  rotate: false
  xy: 1994, 69
  size: 21, 48
  orig: 48, 48
  offset: 14, 0
  index: -1
sprites/buildings/virtual_processor-stacker
  rotate: false
  xy: 1458, 158
  size: 44, 48
  orig: 48, 48
  offset: 4, 0
  index: -1
sprites/buildings/virtual_processor-unstacker
  rotate: false
  xy: 1225, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/buildings/wire_tunnel
  rotate: false
  xy: 1810, 209
  size: 47, 46
  orig: 48, 48
  offset: 1, 1
  index: -1
sprites/colors/blue
  rotate: false
  xy: 184, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/cyan
  rotate: false
  xy: 204, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/green
  rotate: false
  xy: 224, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/purple
  rotate: false
  xy: 244, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/red
  rotate: false
  xy: 264, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/uncolored
  rotate: false
  xy: 284, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/white
  rotate: false
  xy: 304, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/colors/yellow
  rotate: false
  xy: 324, 1
  size: 18, 18
  orig: 18, 18
  offset: 0, 0
  index: -1
sprites/debug/acceptor_slot
  rotate: false
  xy: 1640, 124
  size: 4, 4
  orig: 4, 4
  offset: 0, 0
  index: -1
sprites/debug/ejector_slot
  rotate: false
  xy: 1854, 19
  size: 4, 4
  orig: 4, 4
  offset: 0, 0
  index: -1
sprites/misc/hub_direction_indicator
  rotate: false
  xy: 362, 3
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/misc/processor_disabled
  rotate: false
  xy: 2017, 40
  size: 28, 29
  orig: 32, 32
  offset: 2, 1
  index: -1
sprites/misc/processor_disconnected
  rotate: false
  xy: 1879, 13
  size: 23, 30
  orig: 32, 32
  offset: 5, 0
  index: -1
sprites/misc/reader_overlay
  rotate: false
  xy: 1883, 45
  size: 36, 25
  orig: 48, 48
  offset: 6, 11
  index: -1
sprites/misc/slot_bad_arrow
  rotate: false
  xy: 103, 5
  size: 13, 13
  orig: 13, 13
  offset: 0, 0
  index: -1
sprites/misc/slot_good_arrow
  rotate: false
  xy: 118, 5
  size: 13, 13
  orig: 13, 13
  offset: 0, 0
  index: -1
sprites/misc/storage_overlay
  rotate: false
  xy: 2017, 102
  size: 30, 15
  orig: 30, 15
  offset: 0, 0
  index: -1
sprites/misc/waypoint
  rotate: false
  xy: 73, 2
  size: 14, 16
  orig: 16, 16
  offset: 1, 0
  index: -1
sprites/misc/waypoint_wires
  rotate: false
  xy: 434, 1
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/boolean_false
  rotate: false
  xy: 89, 3
  size: 12, 15
  orig: 16, 16
  offset: 2, 0
  index: -1
sprites/wires/boolean_true
  rotate: false
  xy: 470, 2
  size: 9, 15
  orig: 16, 16
  offset: 3, 0
  index: -1
sprites/wires/display/blue
  rotate: false
  xy: 1, 2
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/display/cyan
  rotate: false
  xy: 344, 3
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/display/green
  rotate: false
  xy: 19, 2
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/display/purple
  rotate: false
  xy: 398, 1
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/display/red
  rotate: false
  xy: 416, 1
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/display/white
  rotate: false
  xy: 452, 1
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/display/yellow
  rotate: false
  xy: 55, 2
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/lever_on
  rotate: false
  xy: 1920, 73
  size: 36, 40
  orig: 48, 48
  offset: 6, 6
  index: -1
sprites/wires/logical_acceptor
  rotate: false
  xy: 1854, 25
  size: 23, 36
  orig: 48, 48
  offset: 13, 12
  index: -1
sprites/wires/logical_ejector
  rotate: false
  xy: 1921, 14
  size: 22, 23
  orig: 48, 48
  offset: 14, 25
  index: -1
sprites/wires/network_conflict
  rotate: false
  xy: 380, 1
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1
sprites/wires/network_empty
  rotate: false
  xy: 1904, 27
  size: 15, 16
  orig: 16, 16
  offset: 1, 0
  index: -1
sprites/wires/overlay_tile
  rotate: false
  xy: 1921, 39
  size: 32, 32
  orig: 32, 32
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_cross
  rotate: false
  xy: 1065, 207
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_forward
  rotate: false
  xy: 649, 11
  size: 10, 48
  orig: 48, 48
  offset: 19, 0
  index: -1
sprites/wires/sets/conflict_split
  rotate: false
  xy: 1504, 130
  size: 48, 29
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_turn
  rotate: false
  xy: 2017, 71
  size: 29, 29
  orig: 48, 48
  offset: 19, 0
  index: -1
sprites/wires/sets/first_cross
  rotate: false
  xy: 975, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/sets/first_forward
  rotate: false
  xy: 1765, 110
  size: 10, 48
  orig: 48, 48
  offset: 19, 0
  index: -1
sprites/wires/sets/first_split
  rotate: false
  xy: 1554, 130
  size: 48, 29
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/sets/first_turn
  rotate: false
  xy: 1955, 42
  size: 29, 29
  orig: 48, 48
  offset: 19, 0
  index: -1
sprites/wires/sets/second_cross
  rotate: false
  xy: 1125, 157
  size: 48, 48
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/sets/second_forward
  rotate: false
  xy: 2036, 119
  size: 10, 48
  orig: 48, 48
  offset: 19, 0
  index: -1
sprites/wires/sets/second_split
  rotate: false
  xy: 1604, 130
  size: 48, 29
  orig: 48, 48
  offset: 0, 0
  index: -1
sprites/wires/sets/second_turn
  rotate: false
  xy: 1986, 38
  size: 29, 29
  orig: 48, 48
  offset: 19, 0
  index: -1
sprites/wires/wires_preview
  rotate: false
  xy: 37, 2
  size: 16, 16
  orig: 16, 16
  offset: 0, 0
  index: -1


/***/ }),

/***/ "./res_built/atlas/atlas0_lq.json":
/*!****************************************!*\
  !*** ./res_built/atlas/atlas0_lq.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./res_built/atlas/atlas0_lq.png":
/*!***************************************!*\
  !*** ./res_built/atlas/atlas0_lq.png ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports) {



/***/ }),

/***/ "./res_built/atlas/atlas0_mq.atlas":
/*!*****************************************!*\
  !*** ./res_built/atlas/atlas0_mq.atlas ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports) {


atlas0_mq.png
size: 2048, 1024
format: RGBA8888
filter: Nearest, Nearest
repeat: none
sprites/belt/built/forward_1
  rotate: false
  xy: 823, 127
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_10
  rotate: false
  xy: 1672, 531
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_11
  rotate: false
  xy: 1752, 531
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_12
  rotate: false
  xy: 898, 287
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_13
  rotate: false
  xy: 903, 189
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_2
  rotate: false
  xy: 875, 29
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_3
  rotate: false
  xy: 893, 533
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_4
  rotate: false
  xy: 973, 535
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_5
  rotate: false
  xy: 1053, 535
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_6
  rotate: false
  xy: 1133, 535
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_7
  rotate: false
  xy: 1213, 535
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_8
  rotate: false
  xy: 1293, 535
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_9
  rotate: false
  xy: 1592, 530
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/left_1
  rotate: false
  xy: 625, 127
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_10
  rotate: false
  xy: 964, 444
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_11
  rotate: false
  xy: 1053, 446
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_12
  rotate: false
  xy: 1142, 446
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_13
  rotate: false
  xy: 1231, 446
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_2
  rotate: false
  xy: 714, 125
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_3
  rotate: false
  xy: 624, 38
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_4
  rotate: false
  xy: 713, 36
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_5
  rotate: false
  xy: 1414, 539
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_6
  rotate: false
  xy: 1503, 539
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_7
  rotate: false
  xy: 875, 444
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_8
  rotate: false
  xy: 1852, 519
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_9
  rotate: false
  xy: 1941, 468
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/right_1
  rotate: false
  xy: 1320, 446
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_10
  rotate: false
  xy: 1044, 70
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_11
  rotate: false
  xy: 1144, 179
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_12
  rotate: false
  xy: 1233, 179
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_13
  rotate: false
  xy: 1322, 179
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_2
  rotate: false
  xy: 955, 71
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_3
  rotate: false
  xy: 1117, 357
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_4
  rotate: false
  xy: 1206, 357
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_5
  rotate: false
  xy: 1295, 357
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_6
  rotate: false
  xy: 1121, 268
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_7
  rotate: false
  xy: 1210, 268
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_8
  rotate: false
  xy: 1299, 268
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_9
  rotate: false
  xy: 1055, 159
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/blueprints/analyzer
  rotate: false
  xy: 1446, 824
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/balancer
  rotate: false
  xy: 922, 726
  size: 172, 96
  orig: 192, 96
  offset: 11, 0
  index: -1
sprites/blueprints/balancer-merger
  rotate: false
  xy: 761, 631
  size: 93, 93
  orig: 96, 96
  offset: 3, 2
  index: -1
sprites/blueprints/balancer-merger-inverse
  rotate: false
  xy: 1952, 729
  size: 95, 93
  orig: 96, 96
  offset: 0, 2
  index: -1
sprites/blueprints/balancer-splitter
  rotate: false
  xy: 856, 631
  size: 93, 93
  orig: 96, 96
  offset: 3, 2
  index: -1
sprites/blueprints/balancer-splitter-inverse
  rotate: false
  xy: 1952, 634
  size: 95, 93
  orig: 96, 96
  offset: 0, 2
  index: -1
sprites/blueprints/belt_left
  rotate: false
  xy: 723, 273
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/belt_right
  rotate: false
  xy: 446, 89
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/blueprints/belt_top
  rotate: false
  xy: 723, 362
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/blueprints/comparator
  rotate: false
  xy: 369, 635
  size: 96, 89
  orig: 96, 96
  offset: 0, 7
  index: -1
sprites/blueprints/constant_signal
  rotate: false
  xy: 802, 27
  size: 71, 87
  orig: 96, 96
  offset: 13, 9
  index: -1
sprites/blueprints/cutter
  rotate: false
  xy: 1272, 824
  size: 172, 96
  orig: 192, 96
  offset: 11, 0
  index: -1
sprites/blueprints/cutter-quad
  rotate: false
  xy: 1, 455
  size: 351, 96
  orig: 384, 96
  offset: 16, 0
  index: -1
sprites/blueprints/display
  rotate: false
  xy: 540, 270
  size: 86, 91
  orig: 96, 96
  offset: 5, 0
  index: -1
sprites/blueprints/filter
  rotate: false
  xy: 563, 726
  size: 180, 96
  orig: 192, 96
  offset: 10, 0
  index: -1
sprites/blueprints/item_producer
  rotate: false
  xy: 1803, 629
  size: 88, 95
  orig: 96, 96
  offset: 5, 1
  index: -1
sprites/blueprints/lever
  rotate: false
  xy: 739, 464
  size: 68, 79
  orig: 96, 96
  offset: 14, 12
  index: -1
sprites/blueprints/logic_gate
  rotate: false
  xy: 362, 551
  size: 96, 82
  orig: 96, 96
  offset: 0, 14
  index: -1
sprites/blueprints/logic_gate-not
  rotate: false
  xy: 361, 93
  size: 83, 96
  orig: 96, 96
  offset: 7, 0
  index: -1
sprites/blueprints/logic_gate-or
  rotate: false
  xy: 460, 550
  size: 96, 83
  orig: 96, 96
  offset: 0, 13
  index: -1
sprites/blueprints/logic_gate-xor
  rotate: false
  xy: 354, 463
  size: 96, 86
  orig: 96, 96
  offset: 0, 10
  index: -1
sprites/blueprints/miner
  rotate: false
  xy: 1429, 628
  size: 92, 96
  orig: 96, 96
  offset: 3, 0
  index: -1
sprites/blueprints/miner-chainable
  rotate: false
  xy: 1523, 628
  size: 92, 96
  orig: 96, 96
  offset: 3, 0
  index: -1
sprites/blueprints/mixer
  rotate: false
  xy: 1118, 922
  size: 175, 96
  orig: 192, 96
  offset: 8, 0
  index: -1
sprites/blueprints/painter
  rotate: false
  xy: 730, 922
  size: 192, 96
  orig: 192, 96
  offset: 0, 0
  index: -1
sprites/blueprints/painter-double
  rotate: false
  xy: 1, 266
  size: 192, 187
  orig: 192, 192
  offset: 0, 5
  index: -1
sprites/blueprints/painter-mirrored
  rotate: false
  xy: 721, 824
  size: 192, 96
  orig: 192, 96
  offset: 0, 0
  index: -1
sprites/blueprints/painter-quad
  rotate: false
  xy: 1, 553
  size: 359, 96
  orig: 384, 96
  offset: 8, 0
  index: -1
sprites/blueprints/reader
  rotate: false
  xy: 1838, 824
  size: 95, 96
  orig: 96, 96
  offset: 1, 0
  index: -1
sprites/blueprints/rotater
  rotate: false
  xy: 1742, 922
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/rotater-ccw
  rotate: false
  xy: 1367, 726
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/rotater-rotate180
  rotate: false
  xy: 1642, 824
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/stacker
  rotate: false
  xy: 745, 726
  size: 175, 96
  orig: 192, 96
  offset: 8, 0
  index: -1
sprites/blueprints/storage
  rotate: false
  xy: 195, 261
  size: 165, 192
  orig: 192, 192
  offset: 14, 0
  index: -1
sprites/blueprints/transistor
  rotate: false
  xy: 978, 346
  size: 68, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/transistor-mirrored
  rotate: false
  xy: 1048, 346
  size: 67, 96
  orig: 96, 96
  offset: 29, 0
  index: -1
sprites/blueprints/trash
  rotate: false
  xy: 1465, 726
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/underground_belt_entry
  rotate: false
  xy: 97, 1
  size: 93, 75
  orig: 96, 96
  offset: 2, 0
  index: -1
sprites/blueprints/underground_belt_entry-tier2
  rotate: false
  xy: 550, 461
  size: 93, 84
  orig: 96, 96
  offset: 2, 0
  index: -1
sprites/blueprints/underground_belt_exit
  rotate: false
  xy: 1952, 557
  size: 93, 75
  orig: 96, 96
  offset: 2, 21
  index: -1
sprites/blueprints/underground_belt_exit-tier2
  rotate: false
  xy: 1, 1
  size: 94, 75
  orig: 96, 96
  offset: 2, 21
  index: -1
sprites/blueprints/virtual_processor
  rotate: false
  xy: 565, 630
  size: 96, 94
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/virtual_processor-painter
  rotate: false
  xy: 362, 365
  size: 87, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/blueprints/virtual_processor-rotater
  rotate: false
  xy: 1373, 535
  size: 39, 96
  orig: 96, 96
  offset: 29, 0
  index: -1
sprites/blueprints/virtual_processor-stacker
  rotate: false
  xy: 451, 364
  size: 87, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/blueprints/virtual_processor-unstacker
  rotate: false
  xy: 1938, 922
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/blueprints/wire_tunnel
  rotate: false
  xy: 1334, 633
  size: 93, 91
  orig: 96, 96
  offset: 2, 3
  index: -1
sprites/buildings/analyzer
  rotate: false
  xy: 1644, 922
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/buildings/balancer
  rotate: false
  xy: 1471, 922
  size: 171, 96
  orig: 192, 96
  offset: 11, 0
  index: -1
sprites/buildings/balancer-merger
  rotate: false
  xy: 1144, 633
  size: 93, 91
  orig: 96, 96
  offset: 3, 3
  index: -1
sprites/buildings/balancer-merger-inverse
  rotate: false
  xy: 1048, 633
  size: 94, 91
  orig: 96, 96
  offset: 0, 3
  index: -1
sprites/buildings/balancer-splitter
  rotate: false
  xy: 1239, 633
  size: 93, 91
  orig: 96, 96
  offset: 3, 3
  index: -1
sprites/buildings/balancer-splitter-inverse
  rotate: false
  xy: 951, 633
  size: 95, 91
  orig: 96, 96
  offset: 0, 3
  index: -1
sprites/buildings/belt_left
  rotate: false
  xy: 456, 178
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/belt/built/left_0
  rotate: false
  xy: 456, 178
  size: 87, 87
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/buildings/belt_right
  rotate: false
  xy: 535, 83
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/right_0
  rotate: false
  xy: 535, 83
  size: 87, 87
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/buildings/belt_top
  rotate: false
  xy: 545, 172
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/belt/built/forward_0
  rotate: false
  xy: 545, 172
  size: 78, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/buildings/comparator
  rotate: false
  xy: 467, 635
  size: 96, 89
  orig: 96, 96
  offset: 0, 7
  index: -1
sprites/buildings/constant_signal
  rotate: false
  xy: 983, 160
  size: 70, 86
  orig: 96, 96
  offset: 13, 10
  index: -1
sprites/buildings/cutter
  rotate: false
  xy: 1096, 726
  size: 171, 96
  orig: 192, 96
  offset: 11, 0
  index: -1
sprites/buildings/cutter-quad
  rotate: false
  xy: 369, 824
  size: 350, 96
  orig: 384, 96
  offset: 16, 0
  index: -1
sprites/buildings/display
  rotate: false
  xy: 812, 274
  size: 84, 90
  orig: 96, 96
  offset: 6, 0
  index: -1
sprites/buildings/filter
  rotate: false
  xy: 915, 824
  size: 179, 96
  orig: 192, 96
  offset: 11, 0
  index: -1
sprites/buildings/hub
  rotate: false
  xy: 1, 651
  size: 366, 367
  orig: 384, 384
  offset: 9, 7
  index: -1
sprites/buildings/item_producer
  rotate: false
  xy: 451, 267
  size: 87, 95
  orig: 96, 96
  offset: 5, 1
  index: -1
sprites/buildings/lever
  rotate: false
  xy: 1384, 367
  size: 66, 77
  orig: 96, 96
  offset: 15, 13
  index: -1
sprites/buildings/logic_gate
  rotate: false
  xy: 558, 547
  size: 96, 81
  orig: 96, 96
  offset: 0, 15
  index: -1
sprites/buildings/logic_gate-not
  rotate: false
  xy: 809, 533
  size: 82, 96
  orig: 96, 96
  offset: 8, 0
  index: -1
sprites/buildings/logic_gate-or
  rotate: false
  xy: 656, 545
  size: 96, 83
  orig: 96, 96
  offset: 0, 13
  index: -1
sprites/buildings/logic_gate-xor
  rotate: false
  xy: 452, 462
  size: 96, 86
  orig: 96, 96
  offset: 0, 10
  index: -1
sprites/buildings/miner
  rotate: false
  xy: 1617, 629
  size: 91, 95
  orig: 96, 96
  offset: 3, 1
  index: -1
sprites/buildings/miner-chainable
  rotate: false
  xy: 1710, 629
  size: 91, 95
  orig: 96, 96
  offset: 3, 1
  index: -1
sprites/buildings/mixer
  rotate: false
  xy: 1096, 824
  size: 174, 96
  orig: 192, 96
  offset: 9, 0
  index: -1
sprites/buildings/painter
  rotate: false
  xy: 369, 726
  size: 192, 96
  orig: 192, 96
  offset: 0, 0
  index: -1
sprites/buildings/painter-double
  rotate: false
  xy: 1, 78
  size: 192, 186
  orig: 192, 192
  offset: 0, 6
  index: -1
sprites/buildings/painter-mirrored
  rotate: false
  xy: 924, 922
  size: 192, 96
  orig: 192, 96
  offset: 0, 0
  index: -1
sprites/buildings/painter-quad
  rotate: false
  xy: 369, 922
  size: 359, 96
  orig: 384, 96
  offset: 8, 0
  index: -1
sprites/buildings/reader
  rotate: false
  xy: 1661, 726
  size: 95, 96
  orig: 96, 96
  offset: 1, 0
  index: -1
sprites/buildings/rotater
  rotate: false
  xy: 1935, 824
  size: 95, 96
  orig: 96, 96
  offset: 1, 0
  index: -1
sprites/buildings/rotater-ccw
  rotate: false
  xy: 1758, 726
  size: 95, 96
  orig: 96, 96
  offset: 1, 0
  index: -1
sprites/buildings/rotater-rotate180
  rotate: false
  xy: 1855, 726
  size: 95, 96
  orig: 96, 96
  offset: 1, 0
  index: -1
sprites/buildings/stacker
  rotate: false
  xy: 1295, 922
  size: 174, 96
  orig: 192, 96
  offset: 9, 0
  index: -1
sprites/buildings/storage
  rotate: false
  xy: 195, 68
  size: 164, 191
  orig: 192, 192
  offset: 14, 0
  index: -1
sprites/buildings/transistor
  rotate: false
  xy: 983, 248
  size: 68, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/buildings/transistor-mirrored
  rotate: false
  xy: 1053, 248
  size: 66, 96
  orig: 96, 96
  offset: 30, 0
  index: -1
sprites/buildings/trash
  rotate: false
  xy: 1740, 824
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/buildings/underground_belt_entry
  rotate: false
  xy: 629, 292
  size: 92, 74
  orig: 96, 96
  offset: 3, 0
  index: -1
sprites/buildings/underground_belt_entry-tier2
  rotate: false
  xy: 645, 460
  size: 92, 83
  orig: 96, 96
  offset: 3, 0
  index: -1
sprites/buildings/underground_belt_exit
  rotate: false
  xy: 628, 216
  size: 92, 74
  orig: 96, 96
  offset: 3, 22
  index: -1
sprites/buildings/underground_belt_exit-tier2
  rotate: false
  xy: 362, 191
  size: 92, 74
  orig: 96, 96
  offset: 3, 22
  index: -1
sprites/buildings/virtual_processor
  rotate: false
  xy: 663, 630
  size: 96, 94
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/buildings/virtual_processor-painter
  rotate: false
  xy: 362, 267
  size: 87, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/buildings/virtual_processor-rotater
  rotate: false
  xy: 823, 369
  size: 38, 96
  orig: 96, 96
  offset: 30, 0
  index: -1
sprites/buildings/virtual_processor-stacker
  rotate: false
  xy: 540, 363
  size: 87, 96
  orig: 96, 96
  offset: 9, 0
  index: -1
sprites/buildings/virtual_processor-unstacker
  rotate: false
  xy: 1563, 726
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/buildings/wire_tunnel
  rotate: false
  xy: 629, 368
  size: 92, 90
  orig: 96, 96
  offset: 3, 3
  index: -1
sprites/colors/blue
  rotate: false
  xy: 1144, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/cyan
  rotate: false
  xy: 1182, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/green
  rotate: false
  xy: 1220, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/purple
  rotate: false
  xy: 1258, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/red
  rotate: false
  xy: 1296, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/uncolored
  rotate: false
  xy: 1334, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/white
  rotate: false
  xy: 1372, 143
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/colors/yellow
  rotate: false
  xy: 1133, 107
  size: 36, 34
  orig: 36, 36
  offset: 0, 0
  index: -1
sprites/debug/acceptor_slot
  rotate: false
  xy: 446, 181
  size: 8, 8
  orig: 8, 8
  offset: 0, 0
  index: -1
sprites/debug/ejector_slot
  rotate: false
  xy: 968, 434
  size: 8, 8
  orig: 8, 8
  offset: 0, 0
  index: -1
sprites/misc/hub_direction_indicator
  rotate: false
  xy: 1381, 109
  size: 32, 32
  orig: 32, 32
  offset: 0, 0
  index: -1
sprites/misc/processor_disabled
  rotate: false
  xy: 754, 573
  size: 53, 55
  orig: 64, 64
  offset: 6, 3
  index: -1
sprites/misc/processor_disconnected
  rotate: false
  xy: 922, 385
  size: 44, 57
  orig: 64, 64
  offset: 11, 2
  index: -1
sprites/misc/reader_overlay
  rotate: false
  xy: 820, 225
  size: 70, 47
  orig: 96, 96
  offset: 13, 24
  index: -1
sprites/misc/slot_bad_arrow
  rotate: false
  xy: 946, 163
  size: 24, 24
  orig: 26, 26
  offset: 1, 1
  index: -1
sprites/misc/slot_good_arrow
  rotate: false
  xy: 754, 545
  size: 24, 26
  orig: 26, 26
  offset: 1, 0
  index: -1
sprites/misc/storage_overlay
  rotate: false
  xy: 955, 39
  size: 60, 30
  orig: 60, 30
  offset: 0, 0
  index: -1
sprites/misc/waypoint
  rotate: false
  xy: 1453, 505
  size: 26, 32
  orig: 32, 32
  offset: 3, 0
  index: -1
sprites/misc/waypoint_wires
  rotate: false
  xy: 1074, 36
  size: 32, 32
  orig: 32, 32
  offset: 0, 0
  index: -1
sprites/wires/boolean_false
  rotate: false
  xy: 1017, 41
  size: 21, 28
  orig: 32, 32
  offset: 6, 1
  index: -1
sprites/wires/boolean_true
  rotate: false
  xy: 2032, 892
  size: 15, 28
  orig: 32, 32
  offset: 7, 1
  index: -1
sprites/wires/display/blue
  rotate: false
  xy: 1133, 72
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/display/cyan
  rotate: false
  xy: 1171, 108
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/display/green
  rotate: false
  xy: 1206, 108
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/display/purple
  rotate: false
  xy: 1241, 108
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/display/red
  rotate: false
  xy: 1276, 108
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/display/white
  rotate: false
  xy: 1311, 108
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/display/yellow
  rotate: false
  xy: 1346, 108
  size: 33, 33
  orig: 33, 33
  offset: 0, 0
  index: -1
sprites/wires/lever_on
  rotate: false
  xy: 1388, 288
  size: 69, 77
  orig: 96, 96
  offset: 13, 13
  index: -1
sprites/wires/logical_acceptor
  rotate: false
  xy: 1409, 446
  size: 42, 71
  orig: 96, 96
  offset: 28, 25
  index: -1
sprites/wires/logical_ejector
  rotate: false
  xy: 903, 142
  size: 41, 45
  orig: 96, 96
  offset: 29, 51
  index: -1
sprites/wires/network_conflict
  rotate: false
  xy: 1040, 38
  size: 32, 30
  orig: 32, 32
  offset: 0, 1
  index: -1
sprites/wires/network_empty
  rotate: false
  xy: 1108, 36
  size: 28, 32
  orig: 32, 32
  offset: 3, 0
  index: -1
sprites/wires/overlay_tile
  rotate: false
  xy: 809, 467
  size: 64, 64
  orig: 64, 64
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_cross
  rotate: false
  xy: 1269, 726
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_forward
  rotate: false
  xy: 803, 116
  size: 18, 96
  orig: 96, 96
  offset: 39, 0
  index: -1
sprites/wires/sets/conflict_split
  rotate: false
  xy: 192, 9
  size: 96, 57
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/conflict_turn
  rotate: false
  xy: 1893, 667
  size: 57, 57
  orig: 96, 96
  offset: 39, 0
  index: -1
sprites/wires/sets/first_cross
  rotate: false
  xy: 1544, 824
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/first_forward
  rotate: false
  xy: 803, 366
  size: 18, 96
  orig: 96, 96
  offset: 39, 0
  index: -1
sprites/wires/sets/first_split
  rotate: false
  xy: 290, 9
  size: 96, 57
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/first_turn
  rotate: false
  xy: 1893, 608
  size: 57, 57
  orig: 96, 96
  offset: 39, 0
  index: -1
sprites/wires/sets/second_cross
  rotate: false
  xy: 1840, 922
  size: 96, 96
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/second_forward
  rotate: false
  xy: 1832, 531
  size: 18, 96
  orig: 96, 96
  offset: 39, 0
  index: -1
sprites/wires/sets/second_split
  rotate: false
  xy: 722, 214
  size: 96, 57
  orig: 96, 96
  offset: 0, 0
  index: -1
sprites/wires/sets/second_turn
  rotate: false
  xy: 863, 385
  size: 57, 57
  orig: 96, 96
  offset: 39, 0
  index: -1
sprites/wires/wires_preview
  rotate: false
  xy: 1138, 38
  size: 32, 32
  orig: 32, 32
  offset: 0, 0
  index: -1


/***/ }),

/***/ "./res_built/atlas/atlas0_mq.json":
/*!****************************************!*\
  !*** ./res_built/atlas/atlas0_mq.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./res_built/atlas/atlas0_mq.png":
/*!***************************************!*\
  !*** ./res_built/atlas/atlas0_mq.png ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports) {



/***/ }),

/***/ "./src/js/application.js":
/*!*******************************!*\
  !*** ./src/js/application.js ***!
  \*******************************/
/*! exports provided: Application */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Application", function() { return Application; });
/* harmony import */ var _core_animation_frame__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./core/animation_frame */ "./src/js/core/animation_frame.js");
/* harmony import */ var _core_background_resources_loader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./core/background_resources_loader */ "./src/js/core/background_resources_loader.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_game_state__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./core/game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _core_globals__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./core/globals */ "./src/js/core/globals.js");
/* harmony import */ var _core_input_distributor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./core/input_distributor */ "./src/js/core/input_distributor.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_state_manager__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./core/state_manager */ "./src/js/core/state_manager.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _platform_ad_provider__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./platform/ad_provider */ "./src/js/platform/ad_provider.js");
/* harmony import */ var _platform_ad_providers_no_ad_provider__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./platform/ad_providers/no_ad_provider */ "./src/js/platform/ad_providers/no_ad_provider.js");
/* harmony import */ var _platform_analytics__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./platform/analytics */ "./src/js/platform/analytics.js");
/* harmony import */ var _platform_browser_google_analytics__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./platform/browser/google_analytics */ "./src/js/platform/browser/google_analytics.js");
/* harmony import */ var _platform_browser_sound__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./platform/browser/sound */ "./src/js/platform/browser/sound.js");
/* harmony import */ var _platform_browser_wrapper__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./platform/browser/wrapper */ "./src/js/platform/browser/wrapper.js");
/* harmony import */ var _platform_electron_wrapper__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./platform/electron/wrapper */ "./src/js/platform/electron/wrapper.js");
/* harmony import */ var _platform_wrapper__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./platform/wrapper */ "./src/js/platform/wrapper.js");
/* harmony import */ var _profile_application_settings__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./profile/application_settings */ "./src/js/profile/application_settings.js");
/* harmony import */ var _savegame_savegame_manager__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./savegame/savegame_manager */ "./src/js/savegame/savegame_manager.js");
/* harmony import */ var _states_about__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./states/about */ "./src/js/states/about.js");
/* harmony import */ var _states_changelog__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./states/changelog */ "./src/js/states/changelog.js");
/* harmony import */ var _states_ingame__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./states/ingame */ "./src/js/states/ingame.js");
/* harmony import */ var _states_keybindings__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./states/keybindings */ "./src/js/states/keybindings.js");
/* harmony import */ var _states_main_menu__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./states/main_menu */ "./src/js/states/main_menu.js");
/* harmony import */ var _states_mobile_warning__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./states/mobile_warning */ "./src/js/states/mobile_warning.js");
/* harmony import */ var _states_preload__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./states/preload */ "./src/js/states/preload.js");
/* harmony import */ var _states_settings__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./states/settings */ "./src/js/states/settings.js");
/* harmony import */ var _platform_browser_game_analytics__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./platform/browser/game_analytics */ "./src/js/platform/browser/game_analytics.js");
/* harmony import */ var _core_restriction_manager__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./core/restriction_manager */ "./src/js/core/restriction_manager.js");

































/**
 * @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
 * @typedef {import("./platform/sound").SoundInterface} SoundInterface
 * @typedef {import("./platform/storage").StorageInterface} StorageInterface
 */

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_7__["createLogger"])("application");

// Set the name of the hidden property and the change event for visibility
let pageHiddenPropName, pageVisibilityEventName;
if (typeof document.hidden !== "undefined") {
    // Opera 12.10 and Firefox 18 and later support
    pageHiddenPropName = "hidden";
    pageVisibilityEventName = "visibilitychange";
    // @ts-ignore
} else if (typeof document.msHidden !== "undefined") {
    pageHiddenPropName = "msHidden";
    pageVisibilityEventName = "msvisibilitychange";
    // @ts-ignore
} else if (typeof document.webkitHidden !== "undefined") {
    pageHiddenPropName = "webkitHidden";
    pageVisibilityEventName = "webkitvisibilitychange";
}

class Application {
    constructor() {
        window.assert(!_core_globals__WEBPACK_IMPORTED_MODULE_4__["GLOBAL_APP"], "Tried to construct application twice");
        logger.log("Creating application, platform =", Object(_core_utils__WEBPACK_IMPORTED_MODULE_10__["getPlatformName"])());
        Object(_core_globals__WEBPACK_IMPORTED_MODULE_4__["setGlobalApp"])(this);

        this.unloaded = false;

        // Global stuff
        this.settings = new _profile_application_settings__WEBPACK_IMPORTED_MODULE_20__["ApplicationSettings"](this);
        this.ticker = new _core_animation_frame__WEBPACK_IMPORTED_MODULE_0__["AnimationFrame"]();
        this.stateMgr = new _core_state_manager__WEBPACK_IMPORTED_MODULE_8__["StateManager"](this);
        this.savegameMgr = new _savegame_savegame_manager__WEBPACK_IMPORTED_MODULE_21__["SavegameManager"](this);
        this.inputMgr = new _core_input_distributor__WEBPACK_IMPORTED_MODULE_5__["InputDistributor"](this);
        this.backgroundResourceLoader = new _core_background_resources_loader__WEBPACK_IMPORTED_MODULE_1__["BackgroundResourcesLoader"](this);

        // Restrictions (Like demo etc)
        this.restrictionMgr = new _core_restriction_manager__WEBPACK_IMPORTED_MODULE_31__["RestrictionManager"](this);

        // Platform dependent stuff

        /** @type {StorageInterface} */
        this.storage = null;

        /** @type {SoundInterface} */
        this.sound = null;

        /** @type {PlatformWrapperInterface} */
        this.platformWrapper = null;

        /** @type {AdProviderInterface} */
        this.adProvider = null;

        /** @type {AnalyticsInterface} */
        this.analytics = null;

        /** @type {GameAnalyticsInterface} */
        this.gameAnalytics = null;

        this.initPlatformDependentInstances();

        // Track if the window is focused (only relevant for browser)
        this.focused = true;

        // Track if the window is visible
        this.pageVisible = true;

        // Track if the app is paused (cordova)
        this.applicationPaused = false;

        /** @type {TypedTrackedState<boolean>} */
        this.trackedIsRenderable = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_9__["TrackedState"](this.onAppRenderableStateChanged, this);

        // Dimensions
        this.screenWidth = 0;
        this.screenHeight = 0;

        // Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova
        this.lastResizeCheck = null;

        // Store the mouse position, or null if not available
        /** @type {Vector|null} */
        this.mousePosition = null;
    }

    /**
     * Initializes all platform instances
     */
    initPlatformDependentInstances() {
        logger.log("Creating platform dependent instances (standalone=", false, ")");

        if (false) {} else {
            this.platformWrapper = new _platform_browser_wrapper__WEBPACK_IMPORTED_MODULE_17__["PlatformWrapperImplBrowser"](this);
        }

        // Start with empty ad provider
        this.adProvider = new _platform_ad_providers_no_ad_provider__WEBPACK_IMPORTED_MODULE_13__["NoAdProvider"](this);
        this.sound = new _platform_browser_sound__WEBPACK_IMPORTED_MODULE_16__["SoundImplBrowser"](this);
        this.analytics = new _platform_browser_google_analytics__WEBPACK_IMPORTED_MODULE_15__["GoogleAnalyticsImpl"](this);
        this.gameAnalytics = new _platform_browser_game_analytics__WEBPACK_IMPORTED_MODULE_30__["ShapezGameAnalytics"](this);
    }

    /**
     * Registers all game states
     */
    registerStates() {
        /** @type {Array<typeof GameState>} */
        const states = [
            _states_preload__WEBPACK_IMPORTED_MODULE_28__["PreloadState"],
            _states_mobile_warning__WEBPACK_IMPORTED_MODULE_27__["MobileWarningState"],
            _states_main_menu__WEBPACK_IMPORTED_MODULE_26__["MainMenuState"],
            _states_ingame__WEBPACK_IMPORTED_MODULE_24__["InGameState"],
            _states_settings__WEBPACK_IMPORTED_MODULE_29__["SettingsState"],
            _states_keybindings__WEBPACK_IMPORTED_MODULE_25__["KeybindingsState"],
            _states_about__WEBPACK_IMPORTED_MODULE_22__["AboutState"],
            _states_changelog__WEBPACK_IMPORTED_MODULE_23__["ChangelogState"],
        ];

        for (let i = 0; i < states.length; ++i) {
            this.stateMgr.register(states[i]);
        }
    }

    /**
     * Registers all event listeners
     */
    registerEventListeners() {
        window.addEventListener("focus", this.onFocus.bind(this));
        window.addEventListener("blur", this.onBlur.bind(this));

        window.addEventListener("resize", () => this.checkResize(), true);
        window.addEventListener("orientationchange", () => this.checkResize(), true);

        if ( true && !_core_config__WEBPACK_IMPORTED_MODULE_2__["IS_MOBILE"]) {
            window.addEventListener("mousemove", this.handleMousemove.bind(this));
            window.addEventListener("mouseout", this.handleMousemove.bind(this));
            window.addEventListener("mouseover", this.handleMousemove.bind(this));
            window.addEventListener("mouseleave", this.handleMousemove.bind(this));
        }

        // Unload events
        window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);
        window.addEventListener("unload", this.onUnload.bind(this), true);

        document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false);

        // Track touches so we can update the focus appropriately
        document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true);
        document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true);
    }

    /**
     * Checks the focus after a touch
     * @param {TouchEvent} event
     */
    updateFocusAfterUserInteraction(event) {
        const target = /** @type {HTMLElement} */ (event.target);
        if (!target || !target.tagName) {
            // Safety check
            logger.warn("Invalid touchstart/touchend event:", event);
            return;
        }

        // When clicking an element which is not the currently focused one, defocus it
        if (target !== document.activeElement) {
            // @ts-ignore
            if (document.activeElement.blur) {
                // @ts-ignore
                document.activeElement.blur();
            }
        }

        // If we click an input field, focus it now
        if (target.tagName.toLowerCase() === "input") {
            // We *really* need the focus
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_10__["waitNextFrame"])().then(() => target.focus());
        }
    }

    /**
     * Handles a page visibility change event
     * @param {Event} event
     */
    handleVisibilityChange(event) {
        window.focus();
        const pageVisible = !document[pageHiddenPropName];
        if (pageVisible !== this.pageVisible) {
            this.pageVisible = pageVisible;
            logger.log("Visibility changed:", this.pageVisible);
            this.trackedIsRenderable.set(this.isRenderable());
        }
    }

    /**
     * Handles a mouse move event
     * @param {MouseEvent} event
     */
    handleMousemove(event) {
        this.mousePosition = new _core_vector__WEBPACK_IMPORTED_MODULE_11__["Vector"](event.clientX, event.clientY);
    }

    /**
     * Internal on focus handler
     */
    onFocus() {
        this.focused = true;
    }

    /**
     * Internal blur handler
     */
    onBlur() {
        this.focused = false;
    }

    /**
     * Returns if the app is currently visible
     */
    isRenderable() {
        return !this.applicationPaused && this.pageVisible;
    }

    onAppRenderableStateChanged(renderable) {
        logger.log("Application renderable:", renderable);
        window.focus();
        const currentState = this.stateMgr.getCurrentState();
        if (!renderable) {
            if (currentState) {
                currentState.onAppPause();
            }
        } else {
            if (currentState) {
                currentState.onAppResume();
            }
            this.checkResize();
        }

        this.sound.onPageRenderableStateChanged(renderable);
    }

    /**
     * Internal unload handler
     */
    onUnload(event) {
        if (!this.unloaded) {
            Object(_core_logging__WEBPACK_IMPORTED_MODULE_7__["logSection"])("UNLOAD HANDLER", "#f77");
            this.unloaded = true;
            const currentState = this.stateMgr.getCurrentState();
            if (currentState) {
                currentState.onBeforeExit();
            }
            this.deinitialize();
        }
    }

    /**
     * Internal before-unload handler
     */
    onBeforeUnload(event) {
        Object(_core_logging__WEBPACK_IMPORTED_MODULE_7__["logSection"])("BEFORE UNLOAD HANDLER", "#f77");
        const currentState = this.stateMgr.getCurrentState();

        if ( true && currentState && currentState.getHasUnloadConfirmation()) {
            if (true) {
                // Need to show a "Are you sure you want to exit"
                event.preventDefault();
                event.returnValue = "Are you sure you want to exit?";
            }
        }
    }

    /**
     * Boots the application
     */
    boot() {
        console.log("Booting ...");
        this.registerStates();
        this.registerEventListeners();

        _core_loader__WEBPACK_IMPORTED_MODULE_6__["Loader"].linkAppAfterBoot(this);

        // Check for mobile
        if (_core_config__WEBPACK_IMPORTED_MODULE_2__["IS_MOBILE"]) {
            this.stateMgr.moveToState("MobileWarningState");
        } else {
            this.stateMgr.moveToState("PreloadState");
        }

        // Starting rendering
        this.ticker.frameEmitted.add(this.onFrameEmitted, this);
        this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this);
        this.ticker.start();

        window.focus();
    }

    /**
     * Deinitializes the application
     */
    deinitialize() {
        return this.sound.deinitialize();
    }

    /**
     * Background frame update callback
     * @param {number} dt
     */
    onBackgroundFrame(dt) {
        if (this.isRenderable()) {
            return;
        }

        const currentState = this.stateMgr.getCurrentState();
        if (currentState) {
            currentState.onBackgroundTick(dt);
        }
    }

    /**
     * Frame update callback
     * @param {number} dt
     */
    onFrameEmitted(dt) {
        if (!this.isRenderable()) {
            return;
        }

        const time = performance.now();

        // Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!)
        if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) {
            this.checkResize();
            this.lastResizeCheck = time;
        }

        const currentState = this.stateMgr.getCurrentState();
        if (currentState) {
            currentState.onRender(dt);
        }
    }

    /**
     * Checks if the app resized. Only does this once in a while
     * @param {boolean} forceUpdate Forced update of the dimensions
     */
    checkResize(forceUpdate = false) {
        const w = window.innerWidth;
        const h = window.innerHeight;
        if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) {
            this.screenWidth = w;
            this.screenHeight = h;
            const currentState = this.stateMgr.getCurrentState();
            if (currentState) {
                currentState.onResized(this.screenWidth, this.screenHeight);
            }

            const scale = this.getEffectiveUiScale();
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_10__["waitNextFrame"])().then(() => document.documentElement.style.setProperty("--ui-scale", `${scale}`));
            window.focus();
        }
    }

    /**
     * Returns the effective ui sclae
     */
    getEffectiveUiScale() {
        return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue();
    }

    /**
     * Callback after ui scale has changed
     */
    updateAfterUiScaleChanged() {
        this.checkResize(true);
    }
}


/***/ }),

/***/ "./src/js/built-temp/base-cz.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-cz.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-da.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-da.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-de.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-de.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-en.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-en.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-es.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-es.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-fr.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-fr.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-hu.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-hu.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-ja.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-ja.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-kor.json":
/*!*****************************************!*\
  !*** ./src/js/built-temp/base-kor.json ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-nl.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-nl.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-no.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-no.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-pl.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-pl.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-pt-BR.json":
/*!*******************************************!*\
  !*** ./src/js/built-temp/base-pt-BR.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-pt-PT.json":
/*!*******************************************!*\
  !*** ./src/js/built-temp/base-pt-PT.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-ru.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-ru.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-sv.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-sv.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-tr.json":
/*!****************************************!*\
  !*** ./src/js/built-temp/base-tr.json ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-zh-CN.json":
/*!*******************************************!*\
  !*** ./src/js/built-temp/base-zh-CN.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/base-zh-TW.json":
/*!*******************************************!*\
  !*** ./src/js/built-temp/base-zh-TW.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64(" "));
    })()

/***/ }),

/***/ "./src/js/built-temp/sfx.json":
/*!************************************!*\
  !*** ./src/js/built-temp/sfx.json ***!
  \************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABGBECuBOA2BnSAuMBtcEqIGYA8A6AWwAcBmSLAXQBotJFTYBLAFwFNUxRsoAjAIYATAOYcA+gDsA9mxa4WAY0HyZU7pj4QADPW1gAnADYA7IQCsAJgAc5ACz2zARis7nNrBDpeoSmaQAnpq+EM7OOnqhYB46hDbOxuRWxhbkOsauVr4+fJDCHIhssDKBEvzQLPDCLFKiIQZWblEGTuZWETY29uRppvbO9hY5+tj5LILwMqISHLAlsA3a5Olxkesb6xajfM6GFoQ6pobkqUcnqR4jvuOT0xIyANZLfI6RO9hdcUO9g6bkhiahmc1zy8A4ADcOPAJP4yODOC9sBYLJFDptNs4PhAOntCOQbKZTCiIm4BqCxqR4IIlJJ+NC2EiIDZjGiMZsrNiwEl4jp9lYCXzrE5hnxcpTqbTypVqrV6mgtNpDKY2ez1vYuelDPEBilDN1XMZ9RSIDAWLD4Mpngrop1VWqdJzba5CMqdNZnBYBoZ7PrjCaoJVZvMZIsbQZwoZ7WqnQZ0gd-kbMkNXPYVaLsOLTUHEAB3FiIAAWEkLLAKTJiqzWDsdXJsB29qPCK1MLJs2TFHzNEjzBeLRZkuYrgwi1Ydse04SshFbwM9xicVgsCQz3iwAF9RoxYEokYwCCQKFQ1+ugA"));
    })()

/***/ }),

/***/ "./src/js/changelog.js":
/*!*****************************!*\
  !*** ./src/js/changelog.js ***!
  \*****************************/
/*! exports provided: CHANGELOG */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CHANGELOG", function() { return CHANGELOG; });
const CHANGELOG = [
    {
        version: "1.2.2",
        date: "07.12.2020",
        entries: [
            "Fix item readers and some other buildings slowing up belts, especially if they stalled (inspired by Keterr's fix)",
            "Added the ability to edit constant signals by left clicking them",
            "Prevent items from being rendered on each other when a belt stalls (inspired by Keterr)",
            "You can now add markers in the wire layer (partially by daanbreur)",
            "Allow to cycle backwards in the toolbar with SHIFT + Tab (idea by EmeraldBlock)",
            "Allow to cycle variants backwards with SHIFT + T",
            "Upgrade numbers now use roman numerals until tier 50 (by LeopoldTal)",
            "Add button to unpin shapes from the left side (by artemisSystem)",
            "Fix middle mouse button also placing blueprints (by Eiim)",
            "Hide wires grid when using the 'Disable Grid' setting (by EmeraldBlock)",
            "Fix UI using multiple different save icons",
            "Updated translations (Thanks to all contributors!)",
        ],
    },
    {
        version: "1.2.1",
        date: "31.10.2020",
        entries: [
            "Fixed stacking bug for level 26 which required restarting the game",
            "Fix reward notification being too long sometimes (by LeopoldTal)",
            "Use locale decimal separator on belt reader display (by LeopoldTal)",
            "Vastly improved performance when saving games (by LeopoldTal)",
            "Prevent some antivirus programs blocking the opening of external links (by LeopoldTal)",
            "Match tutorials to the correct painter variants (by LeopoldTal)",
            "Prevent throughput goals containing fractional numbers (by CEbbinghaus)",
            "Updated translations and added Hungarian",
        ],
    },
    {
        version: "1.2.0",
        date: "09.10.2020",
        entries: [
            "⚠️⚠️This update is HUGE, view the full changelog <a href='https://shapez.io/wires/' target='_blank'>here</a>! ⚠️⚠️",
        ],
    },
    {
        version: "1.1.18",
        date: "27.06.2020",
        entries: [
            "Huge performance improvements - up to double fps and tick-rate! This will wipe out all current items on belts.",
            "Reduce story shapes required until unlocking blueprints",
            "Allow clicking on variants to select them",
            "Add 'copy key' button to shape viewer",
            "Add more FPS to the belt animation and fix belt animation seeming to go 'backwards' on high belt speeds",
            "Fix deconstruct sound being played when right clicking hub",
            "Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)",
            "Update belt placement performance on huge factories (by Phlosioneer)",
            "Fix duplicate waypoints with a shape not rendering (by hexy)",
            "Fix smart tunnel placement deleting wrong tunnels (by mordof)",
            "Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)",
            "Added chinese (traditional) translation",
            "Updated translations",
        ],
    },
    {
        version: "1.1.17",
        date: "22.06.2020",
        entries: [
            "Color blind mode! You can now activate it in the settings and it will show you which color is below your cursor (Either resource or on the belt)",
            "Add info buttons to all shapes so you can figure out how they are built! (And also, which colors they have)",
            "Allow configuring autosave interval and disabling it in the settings",
            "The smart-tunnel placement has been reworked to properly replace belts. Thus the setting has been turned on again by default",
            "The soundtrack now has a higher quality on the standalone version than the web version",
            "Add setting to disable cut/delete warnings (by hexy)",
            "Fix bug where belts in blueprints don't orient correctly (by hexy)",
            "Fix camera moving weird after dragging and holding (by hexy)",
            "Fix keybinding for pipette showing while pasting blueprints",
            "Improve visibility of shape background in dark mode",
            "Added sound when destroying a building",
            "Added swedish translation",
            "Update tutorial image for tier 2 tunnels to explain mix/match (by jimmyshadow1)",
        ],
    },
    {
        version: "1.1.16",
        date: "21.06.2020",
        entries: [
            "You can now pickup buildings below your cursor with 'Q'!",
            "The game soundtrack has been extended! There are now 4 songs with over 13 minutes of playtime from <a href='https://soundcloud.com/pettersumelius' target='blank'>Peppsen</a>!",
            "Refactor keybindings overlay to show more appropriate keybindings",
            "Show keybindings for area-select in the upper left instead",
            "Automatically deselect area when selecting a new building",
            "Raise markers limit from 14 characters to 71 (by Joker-vD)",
            "Optimize performance by caching extractor items (by Phlosioneer)",
            "Added setting to enable compact building infos, which only show ratios and hide the image / description",
            "Apply dark theme to menu as well (by dengr1065)",
            "Fix belt planner not placing the last belt",
            "Fix buildings getting deleted when right clicking while placing a blueprint",
            "Fix for exporting screenshots for huge bases (It was showing an empty file) (by xSparfuchs)",
            "Fix buttons not responding when using right click directly after left click (by davidburhans)",
            "Fix hub marker being hidden by building info panel",
            "Disable dialog background blur since it can cause performance issues",
            "Added simplified chinese translations",
            "Update translations (Thanks to all translators!)",
        ],
    },
    {
        version: "1.1.15",
        date: "17.06.2020",
        entries: [
            "You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)",
            "Added continue button to main menu and add separate 'New game' button (by jaysc)",
            "Added setting to disable smart tunnel placement introduced with the last update",
            "Added setting to disable vignette",
            "Update translations",
        ],
    },
    {
        version: "1.1.14",
        date: "16.06.2020",
        entries: [
            "There is now an indicator (compass) to the HUB for the HUB Marker!",
            "You can now include shape short keys in markers to render shape icons instead of text!",
            "Added mirrored variant of the painter",
            "When placing tunnels, unnecessary belts inbetween are now removed!",
            "You can now drag tunnels and they will automatically expand! (Just try it out, its intuitive)",
        ],
    },
    {
        version: "1.1.13",
        date: "15.06.2020",
        entries: [
            "Added shift modifier for faster pan (by jaysc)",
            "Added Japanese translations",
            "Added Portuguese (Portugal) translations",
            "Updated icon for Spanish (Latin America) - It was showing a Spanish flag before",
            "Updated existing translations",
        ],
    },
    {
        version: "1.1.12",
        date: "14.06.2020",
        entries: [
            "Huge performance improvements! The game should now run up to 60% faster!",
            "Added norwegian translation",
        ],
    },
    {
        version: "1.1.11",
        date: "13.06.2020",
        entries: [
            "Pinned shapes are now smart, they dynamically update their goal and also unpin when no longer required. Completed objectives are now rendered transparent.",
            "You can now cut areas, and also paste the last blueprint again! (by hexy)",
            "You can now export your whole base as an image by pressing F3!",
            "Improve upgrade number rounding, so there are no goals like '37.4k', instead it will now be '35k'",
            "You can now configure the camera movement speed when using WASD (by mini-bomba)",
            "Selecting an area now is relative to the world and thus does not move when moving the screen (by Dimava)",
            "Allow higher tick-rates up to 500hz (This will burn your PC!)",
            "Fix bug regarding number rounding",
            "Fix dialog text being hardly readable in dark theme",
            "Fix app not starting when the savegames were corrupted - there is now a better error message as well.",
            "Further translation updates - Big thanks to all contributors!",
        ],
    },
    {
        version: "1.1.10",
        date: "12.06.2020",
        entries: [
            "There are now linux builds on steam! Please report any issues in the Discord!",
            "Steam cloud saves are now available!",
            "Added and update more translations (Big thank you to all translators!)",
            "Prevent invalid connection if existing underground tunnel entrance exists (by jaysc)",
        ],
    },
    {
        version: "1.1.9",
        date: "11.06.2020",
        entries: [
            "Support for translations! Interested in helping out? Check out the <a target='_blank'  href='https://github.com/tobspr/shapez.io/tree/master/translations'>translation guide</a>!",
            "Update stacker artwork to clarify how it works",
            "Update keybinding hints on the top left to be more accurate",
            "Make it more clear when blueprints are unlocked when trying to use them",
            "Fix pinned shape icons not being visible in dark mode",
            "Fix being able to select buildings via hotkeys in map overview mode",
            "Make shapes unpinnable in the upgrades tab (By hexy)",
        ],
    },
    {
        version: "1.1.8",
        date: "07.06.2020",
        entries: [
            "You can now purchase the standalone on steam! <a href='https://steam.shapez.io' target='blank'>View steam page</a>",
            "Added ability to create markers in the demo, but only two.",
            "Contest #01 has ended! I'll now work through the entries, select the 5 I like most and present them to the community to vote for!",
        ],
    },
    {
        version: "1.1.7",
        date: "04.06.2020",
        entries: ["HOTFIX: Fix savegames not showing up on the standalone version"],
    },
    {
        version: "1.1.6",
        date: "04.06.2020",
        entries: [
            "The steam release will happen on the <strong>7th of June</strong> - Be sure to add it to your wishlist! <a href='https://steam.shapez.io' target='blank'>View on steam</a>",
            "Fixed level complete dialog being blurred when the shop was opened before",
            "Standalone: Increased icon visibility for windows builds",
            "Web version: Fixed firefox not loading the game when browsing in private mode",
        ],
    },

    {
        version: "1.1.5",
        date: "03.06.2020",
        entries: ["Added weekly contests!"],
    },
    {
        version: "1.1.4",
        date: "01.06.2020",
        entries: ["Add 'interactive' tutorial for the first level to improve onboarding experience"],
    },
    {
        version: "1.1.3",
        date: "01.06.2020",
        entries: [
            "Added setting to configure zoom / mouse wheel / touchpad sensitivity",
            "Fix belts being too slow when copied via blueprint (by Dimava)",
            "Allow binding mouse buttons to actions (by Dimava)",
            "Increase readability of certain HUD elements",
        ],
    },
    {
        version: "1.1.2",
        date: "30.05.2020",
        entries: [
            "The official trailer is now ready! Check it out <a href='https://www.youtube.com/watch?v=KyorY1uIqiQ' target='_blank'>here</a>!",
            "The <a href='https://steam.shapez.io' target='_blank'>steam page</a> is now live!",
            "Experimental linux builds are now available! Please give me feedback on them in the Discord",
            "Allow hovering pinned shapes to enlarge them",
            "Allow deselecting blueprints with right click and 'Q'",
            "Move default key for deleting from 'X' to 'DEL'",
            "Show confirmation when deleting more than 100 buildings",
            "Reintroduce 'SPACE' keybinding to center on map",
            "Improved keybinding hints",
            "Fixed some keybindings showing as 'undefined'",
        ],
    },
    {
        version: "1.1.1",
        date: "28.05.2020",
        entries: ["Fix crash when 'Show Hints' setting was turned off"],
    },
    {
        version: "1.1.0",
        date: "28.05.2020",
        entries: [
            "BLUEPRINTS! They are unlocked at level 12 and cost a special shape to build.",
            "MAP MARKERS! Press 'M' to create a waypoint and be able to jump to it",
            "Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.",
            "Allow holding SHIFT to rotate counter clockwise",
            "Added confirmation when deleting more than 500 buildings at a time",
            "Added background to toolbar to increase contrast",
            "Further decerase requirements of first levels",
            "Pinned shapes now are saved",
            "Allow placing extractors anywhere again, but they don't work at all if not placed on a resource",
            "Show dialog explaining some keybindings after completing level 4",
            "Fix keys being stuck when opening a dialog",
            "Swapped shape order for painting upgrades",
            "Allow changing all keybindings, including CTRL, ALT and SHIFT (by Dimava)",
            "Fix cycling through keybindings selecting locked buildings as well (by Dimava)",
            "There is now a github action, checking all pull requests with eslint. (by mrHedgehog)",
        ],
    },
    {
        version: "1.0.4",
        date: "26.05.2020",
        entries: [
            "Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'",
            "Add dialog after completing level 2 to check out the upgrades tab.",
            "Allow changing the keybindings in the demo version",
        ],
    },
    {
        version: "1.0.3",
        date: "24.05.2020",
        entries: [
            "Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.",
        ],
    },
    {
        version: "1.0.2",
        date: "23.05.2020",
        entries: [
            "Introduced changelog",
            "Removed 'early access' label because the game isn't actually early access - its in a pretty good state already! (No worries, a lot more updates will follow!)",
            "Added a 'Show hint' button which shows a small video for almost all levels to help out",
            "Now showing proper descriptions when completing levels, with instructions on what the gained reward does.",
            "Show a landing page on mobile devices about the game not being ready to be played on mobile yet",
            "Fix painters and mixers being affected by the shape processors upgrade and not the painter one",
            "Added 'multiplace' setting which is equivalent to holding SHIFT all the time",
            "Added keybindings to zoom in / zoom out",
            "Tunnels now also show connection lines to tunnel exits, instead of just tunnel entries",
            "Lots of minor fixes and improvements",
        ],
    },
    {
        version: "1.0.1",
        date: "21.05.2020",
        entries: ["Initial release!"],
    },
];


/***/ }),

/***/ "./src/js/core/animation_frame.js":
/*!****************************************!*\
  !*** ./src/js/core/animation_frame.js ***!
  \****************************************/
/*! exports provided: AnimationFrame */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AnimationFrame", function() { return AnimationFrame; });
/* harmony import */ var _signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./signal */ "./src/js/core/signal.js");
/* harmony import */ var _webworkers_background_animation_frame_emittter_worker__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../webworkers/background_animation_frame_emittter.worker */ "./src/js/webworkers/background_animation_frame_emittter.worker.js");
/* harmony import */ var _webworkers_background_animation_frame_emittter_worker__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_webworkers_background_animation_frame_emittter_worker__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");


// @ts-ignore



const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("animation_frame");

const maxDtMs = 1000;
const resetDtMs = 16;

class AnimationFrame {
    constructor() {
        this.frameEmitted = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
        this.bgFrameEmitted = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();

        this.lastTime = performance.now();
        this.bgLastTime = performance.now();

        this.boundMethod = this.handleAnimationFrame.bind(this);

        this.backgroundWorker = new _webworkers_background_animation_frame_emittter_worker__WEBPACK_IMPORTED_MODULE_1___default.a();
        this.backgroundWorker.addEventListener("error", err => {
            logger.error("Error in background fps worker:", err);
        });
        this.backgroundWorker.addEventListener("message", this.handleBackgroundTick.bind(this));
    }

    handleBackgroundTick() {
        const time = performance.now();

        let dt = time - this.bgLastTime;

        if (dt > maxDtMs) {
            dt = resetDtMs;
        }

        this.bgFrameEmitted.dispatch(dt);
        this.bgLastTime = time;
    }

    start() {
        window.assert(window.requestAnimationFrame, "requestAnimationFrame is not supported!");
        this.handleAnimationFrame();
    }

    handleAnimationFrame(time) {
        let dt = time - this.lastTime;

        if (dt > maxDtMs) {
            dt = resetDtMs;
        }

        this.frameEmitted.dispatch(dt);
        this.lastTime = time;

        window.requestAnimationFrame(this.boundMethod);
    }
}


/***/ }),

/***/ "./src/js/core/assert.js":
/*!*******************************!*\
  !*** ./src/js/core/assert.js ***!
  \*******************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");


const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("assert");

let assertionErrorShown = false;

function initAssert() {
    /**
     * Expects a given condition to be true
     * @param {Boolean} condition
     * @param  {...String} failureMessage
     */
    // @ts-ignore
    window.assert = function (condition, ...failureMessage) {
        if (!condition) {
            logger.error("assertion failed:", ...failureMessage);
            if (!assertionErrorShown) {
                // alert("Assertion failed (the game will try to continue to run): \n\n" + failureMessage);
                assertionErrorShown = true;
            }
            throw new Error("AssertionError: " + failureMessage.join(" "));
        }
    };
}

initAssert();


/***/ }),

/***/ "./src/js/core/async_compression.js":
/*!******************************************!*\
  !*** ./src/js/core/async_compression.js ***!
  \******************************************/
/*! exports provided: compressionPrefix, asyncCompressor */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "compressionPrefix", function() { return compressionPrefix; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "asyncCompressor", function() { return asyncCompressor; });
/* harmony import */ var _webworkers_compression_worker__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../webworkers/compression.worker */ "./src/js/webworkers/compression.worker.js");
/* harmony import */ var _webworkers_compression_worker__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_webworkers_compression_worker__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
// @ts-ignore





const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("async_compression");

let compressionPrefix = String.fromCodePoint(1);

function checkCryptPrefix(prefix) {
    try {
        window.localStorage.setItem("prefix_test", prefix);
        window.localStorage.removeItem("prefix_test");
        return true;
    } catch (ex) {
        logger.warn("Prefix '" + prefix + "' not available");
        return false;
    }
}

if (!checkCryptPrefix(compressionPrefix)) {
    logger.warn("Switching to basic prefix");
    compressionPrefix = " ";
    if (!checkCryptPrefix(compressionPrefix)) {
        logger.warn("Prefix not available, ls seems to be unavailable");
    }
}

/**
 * @typedef {{
 *   errorHandler: function(any) : void,
 *   resolver: function(any) : void,
 *   startTime: number
 * }} JobEntry
 */

class AsynCompression {
    constructor() {
        this.worker = new _webworkers_compression_worker__WEBPACK_IMPORTED_MODULE_0___default.a();

        this.currentJobId = 1000;

        /** @type {Object.<number, JobEntry>} */
        this.currentJobs = {};

        this.worker.addEventListener("message", event => {
            const { jobId, result } = event.data;
            const jobData = this.currentJobs[jobId];
            if (!jobData) {
                logger.error("Failed to resolve job result, job id", jobId, "is not known");
                return;
            }

            const duration = performance.now() - jobData.startTime;
            logger.log(
                "Got job",
                jobId,
                "response within",
                Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round2Digits"])(duration),
                "ms: ",
                result.length,
                "bytes"
            );
            const resolver = jobData.resolver;
            delete this.currentJobs[jobId];
            resolver(result);
        });

        this.worker.addEventListener("error", err => {
            logger.error("Got error from webworker:", err, "aborting all jobs");
            const failureCalls = [];
            for (const jobId in this.currentJobs) {
                failureCalls.push(this.currentJobs[jobId].errorHandler);
            }
            this.currentJobs = {};
            for (let i = 0; i < failureCalls.length; ++i) {
                failureCalls[i](err);
            }
        });
    }

    /**
     * Compresses any object
     * @param {any} obj
     */
    compressObjectAsync(obj) {
        logger.log("Compressing object async (optimized)");
        return this.internalQueueJob("compressObject", {
            obj,
            compressionPrefix,
        });
    }

    /**
     * Queues a new job
     * @param {string} job
     * @param {any} data
     * @returns {Promise<any>}
     */
    internalQueueJob(job, data) {
        const jobId = ++this.currentJobId;
        return new Promise((resolve, reject) => {
            const errorHandler = err => {
                logger.error("Failed to compress job", jobId, ":", err);
                reject(err);
            };
            this.currentJobs[jobId] = {
                errorHandler,
                resolver: resolve,
                startTime: performance.now(),
            };

            logger.log("Posting job", job, "/", jobId);
            this.worker.postMessage({ jobId, job, data });
        });
    }
}

const asyncCompressor = new AsynCompression();


/***/ }),

/***/ "./src/js/core/atlas_definitions.js":
/*!******************************************!*\
  !*** ./src/js/core/atlas_definitions.js ***!
  \******************************************/
/*! exports provided: AtlasDefinition, atlasFiles */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AtlasDefinition", function() { return AtlasDefinition; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "atlasFiles", function() { return atlasFiles; });
/**
 * @typedef {{ w: number, h: number }} Size
 * @typedef {{ x: number, y: number }} Position
 * @typedef {{
 *   frame: Position & Size,
 *   rotated: boolean,
 *   spriteSourceSize: Position & Size,
 *   sourceSize: Size,
 *   trimmed: boolean
 * }} SpriteDefinition
 *
 * @typedef {{
 *   app: string,
 *   version: string,
 *   image: string,
 *   format: string,
 *   size: Size,
 *   scale: string,
 *   smartupdate: string
 * }} AtlasMeta
 *
 * @typedef {{
 *   frames: Object.<string, SpriteDefinition>,
 *   meta: AtlasMeta
 * }} SourceData
 */

class AtlasDefinition {
    /**
     * @param {SourceData} sourceData
     */
    constructor({ frames, meta }) {
        this.meta = meta;
        this.sourceData = frames;
        this.sourceFileName = meta.image;
    }

    getFullSourcePath() {
        return this.sourceFileName;
    }
}

/** @type {AtlasDefinition[]} **/
const atlasFiles = __webpack_require__("./res_built/atlas sync .*\\.json/")
    .keys()
    .map(f => f.replace(/^\.\//gi, ""))
    .map(f => __webpack_require__("./res_built/atlas sync recursive ^\\.\\/.*$")("./" + f))
    .map(data => new AtlasDefinition(data));


/***/ }),

/***/ "./src/js/core/background_resources_loader.js":
/*!****************************************************!*\
  !*** ./src/js/core/background_resources_loader.js ***!
  \****************************************************/
/*! exports provided: BackgroundResourcesLoader */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BackgroundResourcesLoader", function() { return BackgroundResourcesLoader; });
/* harmony import */ var _loader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./loader */ "./src/js/core/loader.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./signal */ "./src/js/core/signal.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _atlas_definitions__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./atlas_definitions */ "./src/js/core/atlas_definitions.js");
/* harmony import */ var _game_meta_building_registry__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../game/meta_building_registry */ "./src/js/game/meta_building_registry.js");
/* harmony import */ var _cachebust__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./cachebust */ "./src/js/core/cachebust.js");










const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("background_loader");

const essentialMainMenuSprites = [
    "logo.png",
    ...["logo.png"].filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0),
];
const essentialMainMenuSounds = [
    _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].uiClick,
    _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].uiError,
    _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].dialogError,
    _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].dialogOk,
    _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].swishShow,
    _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].swishHide,
];

const essentialBareGameAtlases = _atlas_definitions__WEBPACK_IMPORTED_MODULE_4__["atlasFiles"];
const essentialBareGameSprites = ["logo.png"].filter(src => src.indexOf(".gif") < 0);
const essentialBareGameSounds = [_platform_sound__WEBPACK_IMPORTED_MODULE_3__["MUSIC"].theme];

const additionalGameSprites = [];
// @ts-ignore
const additionalGameSounds = [...Object.values(_platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"]), ...Object.values(_platform_sound__WEBPACK_IMPORTED_MODULE_3__["MUSIC"])];

class BackgroundResourcesLoader {
    /**
     *
     * @param {Application} app
     */
    constructor(app) {
        this.app = app;

        this.registerReady = false;
        this.mainMenuReady = false;
        this.bareGameReady = false;
        this.additionalReady = false;

        this.signalMainMenuLoaded = new _signal__WEBPACK_IMPORTED_MODULE_2__["Signal"]();
        this.signalBareGameLoaded = new _signal__WEBPACK_IMPORTED_MODULE_2__["Signal"]();
        this.signalAdditionalLoaded = new _signal__WEBPACK_IMPORTED_MODULE_2__["Signal"]();

        this.numAssetsLoaded = 0;
        this.numAssetsToLoadTotal = 0;

        // Avoid loading stuff twice
        this.spritesLoaded = [];
        this.soundsLoaded = [];
    }

    getNumAssetsLoaded() {
        return this.numAssetsLoaded;
    }

    getNumAssetsTotal() {
        return this.numAssetsToLoadTotal;
    }

    getPromiseForMainMenu() {
        if (this.mainMenuReady) {
            return Promise.resolve();
        }

        return new Promise(resolve => {
            this.signalMainMenuLoaded.add(resolve);
        });
    }

    getPromiseForBareGame() {
        if (this.bareGameReady) {
            return Promise.resolve();
        }

        return new Promise(resolve => {
            this.signalBareGameLoaded.add(resolve);
        });
    }

    startLoading() {
        this.internalStartLoadingEssentialsForMainMenu();
    }

    internalStartLoadingEssentialsForMainMenu() {
        logger.log("⏰ Start load: main menu");
        this.internalLoadSpritesAndSounds(essentialMainMenuSprites, essentialMainMenuSounds)
            .catch(err => {
                logger.warn("⏰ Failed to load essentials for main menu:", err);
            })
            .then(() => {
                logger.log("⏰ Finish load: main menu");
                this.mainMenuReady = true;
                this.signalMainMenuLoaded.dispatch();
                this.internalStartLoadingEssentialsForBareGame();
            });
    }

    internalStartLoadingEssentialsForBareGame() {
        logger.log("⏰ Start load: bare game");
        this.internalLoadSpritesAndSounds(
            essentialBareGameSprites,
            essentialBareGameSounds,
            essentialBareGameAtlases
        )
            .then(() => this.internalPreloadCss("async-resources.scss"))
            .catch(err => {
                logger.warn("⏰ Failed to load essentials for bare game:", err);
            })
            .then(() => {
                logger.log("⏰ Finish load: bare game");
                this.bareGameReady = true;
                Object(_game_meta_building_registry__WEBPACK_IMPORTED_MODULE_5__["initBuildingCodesAfterResourcesLoaded"])();
                this.signalBareGameLoaded.dispatch();
                this.internalStartLoadingAdditionalGameAssets();
            });
    }

    internalStartLoadingAdditionalGameAssets() {
        const additionalAtlases = [];
        logger.log("⏰ Start load: additional assets (", additionalAtlases.length, "images)");
        this.internalLoadSpritesAndSounds(additionalGameSprites, additionalGameSounds, additionalAtlases)
            .catch(err => {
                logger.warn("⏰ Failed to load additional assets:", err);
            })
            .then(() => {
                logger.log("⏰ Finish load: additional assets");
                this.additionalReady = true;
                this.signalAdditionalLoaded.dispatch();
            });
    }

    internalPreloadCss(name) {
        return new Promise((resolve, reject) => {
            const link = document.createElement("link");

            link.onload = resolve;
            link.onerror = reject;

            link.setAttribute("rel", "stylesheet");
            link.setAttribute("media", "all");
            link.setAttribute("type", "text/css");
            link.setAttribute("href", Object(_cachebust__WEBPACK_IMPORTED_MODULE_6__["cachebust"])("async-resources.css"));
            document.head.appendChild(link);
        });
    }

    /**
     * @param {Array<string>} sprites
     * @param {Array<string>} sounds
     * @param {Array<AtlasDefinition>} atlases
     * @returns {Promise<void>}
     */
    internalLoadSpritesAndSounds(sprites, sounds, atlases = []) {
        this.numAssetsToLoadTotal = sprites.length + sounds.length + atlases.length;
        this.numAssetsLoaded = 0;

        let promises = [];

        for (let i = 0; i < sounds.length; ++i) {
            if (this.soundsLoaded.indexOf(sounds[i]) >= 0) {
                // Already loaded
                continue;
            }

            this.soundsLoaded.push(sounds[i]);
            promises.push(
                this.app.sound
                    .loadSound(sounds[i])
                    .catch(err => {
                        logger.warn("Failed to load sound:", sounds[i]);
                    })
                    .then(() => {
                        this.numAssetsLoaded++;
                    })
            );
        }

        for (let i = 0; i < sprites.length; ++i) {
            if (this.spritesLoaded.indexOf(sprites[i]) >= 0) {
                // Already loaded
                continue;
            }
            this.spritesLoaded.push(sprites[i]);
            promises.push(
                _loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].preloadCSSSprite(sprites[i])
                    .catch(err => {
                        logger.warn("Failed to load css sprite:", sprites[i]);
                    })
                    .then(() => {
                        this.numAssetsLoaded++;
                    })
            );
        }

        for (let i = 0; i < atlases.length; ++i) {
            const atlas = atlases[i];
            promises.push(
                _loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].preloadAtlas(atlas)
                    .catch(err => {
                        logger.warn("Failed to load atlas:", atlas.sourceFileName);
                    })
                    .then(() => {
                        this.numAssetsLoaded++;
                    })
            );
        }

        return (
            Promise.all(promises)

                // // Remove some pressure by waiting a bit
                // .then(() => {
                //     return new Promise(resolve => {
                //         setTimeout(resolve, 200);
                //     });
                // })
                .then(() => {
                    this.numAssetsToLoadTotal = 0;
                    this.numAssetsLoaded = 0;
                })
        );
    }
}


/***/ }),

/***/ "./src/js/core/buffer_maintainer.js":
/*!******************************************!*\
  !*** ./src/js/core/buffer_maintainer.js ***!
  \******************************************/
/*! exports provided: BufferMaintainer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BufferMaintainer", function() { return BufferMaintainer; });
/* harmony import */ var _game_root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game/root */ "./src/js/game/root.js");
/* harmony import */ var _buffer_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");





/**
 * @typedef {{
 *  canvas: HTMLCanvasElement,
 *  context: CanvasRenderingContext2D,
 *  lastUse: number,
 * }} CacheEntry
 */

const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("buffers");

const bufferGcDurationSeconds = 0.5;

class BufferMaintainer {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;

        /** @type {Map<string, Map<string, CacheEntry>>} */
        this.cache = new Map();

        this.iterationIndex = 1;
        this.lastIteration = 0;

        this.root.signals.gameFrameStarted.add(this.update, this);
    }

    /**
     * Returns the buffer stats
     */
    getStats() {
        let stats = {
            rootKeys: 0,
            subKeys: 0,
            vramBytes: 0,
        };
        this.cache.forEach((subCache, key) => {
            ++stats.rootKeys;

            subCache.forEach((cacheEntry, subKey) => {
                ++stats.subKeys;

                const canvas = cacheEntry.canvas;
                stats.vramBytes += canvas.width * canvas.height * 4;
            });
        });

        return stats;
    }

    /**
     * Goes to the next buffer iteration, clearing all buffers which were not used
     * for a few iterations
     */
    garbargeCollect() {
        let totalKeys = 0;
        let deletedKeys = 0;
        const minIteration = this.iterationIndex;

        this.cache.forEach((subCache, key) => {
            let unusedSubKeys = [];

            // Filter sub cache
            subCache.forEach((cacheEntry, subKey) => {
                if (cacheEntry.lastUse < minIteration) {
                    unusedSubKeys.push(subKey);
                    Object(_buffer_utils__WEBPACK_IMPORTED_MODULE_1__["freeCanvas"])(cacheEntry.canvas);
                    ++deletedKeys;
                } else {
                    ++totalKeys;
                }
            });

            // Delete unused sub keys
            for (let i = 0; i < unusedSubKeys.length; ++i) {
                subCache.delete(unusedSubKeys[i]);
            }
        });

        // Make sure our backlog never gets too big
        Object(_buffer_utils__WEBPACK_IMPORTED_MODULE_1__["clearBufferBacklog"])();

        // if (G_IS_DEV) {
        //     const bufferStats = getBufferStats();
        //     const mbUsed = round1Digit(bufferStats.vramUsage / (1024 * 1024));
        //     logger.log(
        //         "GC: Remove",
        //         (deletedKeys + "").padStart(4),
        //         ", Remain",
        //         (totalKeys + "").padStart(4),
        //         "(",
        //         (bufferStats.bufferCount + "").padStart(4),
        //         "total",
        //         ")",

        //         "(",
        //         (bufferStats.backlogSize + "").padStart(4),
        //         "backlog",
        //         ")",

        //         "VRAM:",
        //         mbUsed,
        //         "MB"
        //     );
        // }

        ++this.iterationIndex;
    }

    update() {
        const now = this.root.time.realtimeNow();
        if (now - this.lastIteration > bufferGcDurationSeconds) {
            this.lastIteration = now;
            this.garbargeCollect();
        }
    }

    /**
     * @param {object} param0
     * @param {string} param0.key
     * @param {string} param0.subKey
     * @param {number} param0.w
     * @param {number} param0.h
     * @param {number} param0.dpi
     * @param {function(HTMLCanvasElement, CanvasRenderingContext2D, number, number, number, object?) : void} param0.redrawMethod
     * @param {object=} param0.additionalParams
     * @returns {HTMLCanvasElement}
     *
     */
    getForKey({ key, subKey, w, h, dpi, redrawMethod, additionalParams }) {
        // First, create parent key
        let parent = this.cache.get(key);
        if (!parent) {
            parent = new Map();
            this.cache.set(key, parent);
        }

        // Now search for sub key
        const cacheHit = parent.get(subKey);
        if (cacheHit) {
            cacheHit.lastUse = this.iterationIndex;
            return cacheHit.canvas;
        }

        // Need to generate new buffer
        const effectiveWidth = w * dpi;
        const effectiveHeight = h * dpi;

        const [canvas, context] = Object(_buffer_utils__WEBPACK_IMPORTED_MODULE_1__["makeOffscreenBuffer"])(effectiveWidth, effectiveHeight, {
            reusable: true,
            label: "buffer-" + key + "/" + subKey,
            smooth: true,
        });

        redrawMethod(canvas, context, w, h, dpi, additionalParams);

        parent.set(subKey, {
            canvas,
            context,
            lastUse: this.iterationIndex,
        });
        return canvas;
    }
}


/***/ }),

/***/ "./src/js/core/buffer_utils.js":
/*!*************************************!*\
  !*** ./src/js/core/buffer_utils.js ***!
  \*************************************/
/*! exports provided: enableImageSmoothing, disableImageSmoothing, getBufferVramUsageBytes, getBufferStats, clearBufferBacklog, makeOffscreenBuffer, registerCanvas, freeCanvas */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enableImageSmoothing", function() { return enableImageSmoothing; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "disableImageSmoothing", function() { return disableImageSmoothing; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBufferVramUsageBytes", function() { return getBufferVramUsageBytes; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBufferStats", function() { return getBufferStats; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clearBufferBacklog", function() { return clearBufferBacklog; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "makeOffscreenBuffer", function() { return makeOffscreenBuffer; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "registerCanvas", function() { return registerCanvas; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "freeCanvas", function() { return freeCanvas; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");




const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("buffer_utils");

/**
 * Enables images smoothing on a context
 * @param {CanvasRenderingContext2D} context
 */
function enableImageSmoothing(context) {
    context.imageSmoothingEnabled = true;
    context.webkitImageSmoothingEnabled = true;

    // @ts-ignore
    context.imageSmoothingQuality = _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].smoothing.quality;
}

/**
 * Disables image smoothing on a context
 * @param {CanvasRenderingContext2D} context
 */
function disableImageSmoothing(context) {
    context.imageSmoothingEnabled = false;
    context.webkitImageSmoothingEnabled = false;
}

/**
 * @typedef {{
 *   canvas: HTMLCanvasElement,
 *   context: CanvasRenderingContext2D
 * }} CanvasCacheEntry
 */

/**
 * @type {Array<CanvasCacheEntry>}
 */
const registeredCanvas = [];

/**
 * Buckets for each width * height combination
 * @type {Map<number, Array<CanvasCacheEntry>>}
 */
const freeCanvasBuckets = new Map();

/**
 * Track statistics
 */
const stats = {
    vramUsage: 0,
    backlogVramUsage: 0,
    bufferCount: 0,
    numReused: 0,
    numCreated: 0,
};

/**
 *
 * @param {HTMLCanvasElement} canvas
 */
function getBufferVramUsageBytes(canvas) {
    window.assert(canvas, "no canvas given");
    window.assert(Number.isFinite(canvas.width), "bad canvas width: " + canvas.width);
    window.assert(Number.isFinite(canvas.height), "bad canvas height" + canvas.height);
    return canvas.width * canvas.height * 4;
}

/**
 * Returns stats on the allocated buffers
 */
function getBufferStats() {
    let numBuffersFree = 0;
    freeCanvasBuckets.forEach(bucket => {
        numBuffersFree += bucket.length;
    });

    return {
        ...stats,
        backlogKeys: freeCanvasBuckets.size,
        backlogSize: numBuffersFree,
    };
}

/**
 * Clears the backlog buffers if they grew too much
 */
function clearBufferBacklog() {
    freeCanvasBuckets.forEach(bucket => {
        while (bucket.length > 500) {
            const entry = bucket[bucket.length - 1];
            stats.backlogVramUsage -= getBufferVramUsageBytes(entry.canvas);
            delete entry.canvas;
            delete entry.context;
            bucket.pop();
        }
    });
}

/**
 * Creates a new offscreen buffer
 * @param {Number} w
 * @param {Number} h
 * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]}
 */
function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, label = "buffer" }) {
    window.assert(w > 0 && h > 0, "W or H < 0");
    if (w % 1 !== 0 || h % 1 !== 0) {
        // console.warn("Subpixel offscreen buffer size:", w, h);
    }
    if (w < 1 || h < 1) {
        logger.error("Offscreen buffer size < 0:", w, "x", h);
        w = Math.max(1, w);
        h = Math.max(1, h);
    }

    const recommendedSize = 1024 * 1024;
    if (w * h > recommendedSize) {
        logger.warn("Creating huge buffer:", w, "x", h, "with label", label);
    }

    w = Math.floor(w);
    h = Math.floor(h);

    let canvas = null;
    let context = null;

    // Ok, search in cache first
    const bucket = freeCanvasBuckets.get(w * h) || [];

    for (let i = 0; i < bucket.length; ++i) {
        const { canvas: useableCanvas, context: useableContext } = bucket[i];
        if (useableCanvas.width === w && useableCanvas.height === h) {
            // Ok we found one
            canvas = useableCanvas;
            context = useableContext;

            // Restore past state
            context.restore();
            context.save();
            context.clearRect(0, 0, canvas.width, canvas.height);

            delete canvas.style.width;
            delete canvas.style.height;

            stats.numReused++;
            stats.backlogVramUsage -= getBufferVramUsageBytes(canvas);
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["fastArrayDelete"])(bucket, i);
            break;
        }
    }

    // None found , create new one
    if (!canvas) {
        canvas = document.createElement("canvas");
        context = canvas.getContext("2d" /*, { alpha } */);

        stats.numCreated++;

        canvas.width = w;
        canvas.height = h;

        // Initial state
        context.save();
    }

    // @ts-ignore
    canvas.label = label;

    if (smooth) {
        enableImageSmoothing(context);
    } else {
        disableImageSmoothing(context);
    }

    if (reusable) {
        registerCanvas(canvas, context);
    }

    return [canvas, context];
}

/**
 * Frees a canvas
 * @param {HTMLCanvasElement} canvas
 */
function registerCanvas(canvas, context) {
    registeredCanvas.push({ canvas, context });

    stats.bufferCount += 1;
    const bytesUsed = getBufferVramUsageBytes(canvas);
    stats.vramUsage += bytesUsed;
}

/**
 * Frees a canvas
 * @param {HTMLCanvasElement} canvas
 */
function freeCanvas(canvas) {
    window.assert(canvas, "Canvas is empty");

    let index = -1;
    let data = null;

    for (let i = 0; i < registeredCanvas.length; ++i) {
        if (registeredCanvas[i].canvas === canvas) {
            index = i;
            data = registeredCanvas[i];
            break;
        }
    }

    if (index < 0) {
        logger.error("Tried to free unregistered canvas of size", canvas.width, canvas.height);
        return;
    }
    Object(_utils__WEBPACK_IMPORTED_MODULE_1__["fastArrayDelete"])(registeredCanvas, index);

    const key = canvas.width * canvas.height;
    const bucket = freeCanvasBuckets.get(key);
    if (bucket) {
        bucket.push(data);
    } else {
        freeCanvasBuckets.set(key, [data]);
    }

    stats.bufferCount -= 1;

    const bytesUsed = getBufferVramUsageBytes(canvas);
    stats.vramUsage -= bytesUsed;
    stats.backlogVramUsage += bytesUsed;
}


/***/ }),

/***/ "./src/js/core/cachebust.js":
/*!**********************************!*\
  !*** ./src/js/core/cachebust.js ***!
  \**********************************/
/*! exports provided: cachebust */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "cachebust", function() { return cachebust; });
/**
 * Generates a cachebuster string. This only modifies the path in the browser version
 * @param {string} path
 */
function cachebust(path) {
    return path;
}


/***/ }),

/***/ "./src/js/core/click_detector.js":
/*!***************************************!*\
  !*** ./src/js/core/click_detector.js ***!
  \***************************************/
/*! exports provided: MAX_MOVE_DISTANCE_PX, clickDetectorGlobals, ClickDetector */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MAX_MOVE_DISTANCE_PX", function() { return MAX_MOVE_DISTANCE_PX; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clickDetectorGlobals", function() { return clickDetectorGlobals; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ClickDetector", function() { return ClickDetector; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
/* harmony import */ var _vector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./vector */ "./src/js/core/vector.js");
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _globals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./globals */ "./src/js/core/globals.js");








const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("click_detector");

const MAX_MOVE_DISTANCE_PX = _config__WEBPACK_IMPORTED_MODULE_4__["IS_MOBILE"] ? 20 : 80;

// For debugging
const registerClickDetectors =  false && false;
if (registerClickDetectors) {
    /** @type {Array<ClickDetector>} */
    window.activeClickDetectors = [];
}

// Store active click detectors so we can cancel them
/** @type {Array<ClickDetector>} */
const ongoingClickDetectors = [];

// Store when the last touch event was registered, to avoid accepting a touch *and* a click event

let clickDetectorGlobals = {
    lastTouchTime: -1000,
};

/**
 * Click detector creation payload typehints
 * @typedef {{
 *  consumeEvents?: boolean,
 *  preventDefault?: boolean,
 *  applyCssClass?: string,
 *  captureTouchmove?: boolean,
 *  targetOnly?: boolean,
 *  maxDistance?: number,
 *  clickSound?: string,
 *  preventClick?: boolean,
 * }} ClickDetectorConstructorArgs
 */

// Detects clicks
class ClickDetector {
    /**
     *
     * @param {Element} element
     * @param {object} param1
     * @param {boolean=} param1.consumeEvents Whether to call stopPropagation
     *                                       (Useful for nested elements where the parent has a click handler as wel)
     * @param {boolean=} param1.preventDefault  Whether to call preventDefault (Usually makes the handler faster)
     * @param {string=} param1.applyCssClass The css class to add while the element is pressed
     * @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well
     * @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
     * @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
     * @param {string=} param1.clickSound Sound key to play on touchdown
     * @param {boolean=} param1.preventClick Whether to prevent click events
     */
    constructor(
        element,
        {
            consumeEvents = false,
            preventDefault = true,
            applyCssClass = "pressed",
            captureTouchmove = false,
            targetOnly = false,
            maxDistance = MAX_MOVE_DISTANCE_PX,
            clickSound = _platform_sound__WEBPACK_IMPORTED_MODULE_5__["SOUNDS"].uiClick,
            preventClick = false,
        }
    ) {
        window.assert(element, "No element given!");
        this.clickDownPosition = null;

        this.consumeEvents = consumeEvents;
        this.preventDefault = preventDefault;
        this.applyCssClass = applyCssClass;
        this.captureTouchmove = captureTouchmove;
        this.targetOnly = targetOnly;
        this.clickSound = clickSound;
        this.maxDistance = maxDistance;
        this.preventClick = preventClick;

        // Signals
        this.click = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.rightClick = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.touchstart = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.touchmove = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.touchend = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.touchcancel = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();

        // Simple signals which just receive the touch position
        this.touchstartSimple = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.touchmoveSimple = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();
        this.touchendSimple = new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]();

        // Store time of touch start
        this.clickStartTime = null;

        // A click can be cancelled if another detector registers a click
        this.cancelled = false;

        this.internalBindTo(/** @type {HTMLElement} */ (element));
    }

    /**
     * Cleans up all event listeners of this detector
     */
    cleanup() {
        if (this.element) {
            if (registerClickDetectors) {
                const index = window.activeClickDetectors.indexOf(this);
                if (index < 0) {
                    logger.error("Click detector cleanup but is not active");
                } else {
                    window.activeClickDetectors.splice(index, 1);
                }
            }
            const options = this.internalGetEventListenerOptions();

            if (_config__WEBPACK_IMPORTED_MODULE_4__["SUPPORT_TOUCH"]) {
                this.element.removeEventListener("touchstart", this.handlerTouchStart, options);
                this.element.removeEventListener("touchend", this.handlerTouchEnd, options);
                this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options);
            }

            this.element.removeEventListener("mouseup", this.handlerTouchStart, options);
            this.element.removeEventListener("mousedown", this.handlerTouchEnd, options);
            this.element.removeEventListener("mouseout", this.handlerTouchCancel, options);

            if (this.captureTouchmove) {
                if (_config__WEBPACK_IMPORTED_MODULE_4__["SUPPORT_TOUCH"]) {
                    this.element.removeEventListener("touchmove", this.handlerTouchMove, options);
                }
                this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
            }

            if (this.preventClick) {
                this.element.removeEventListener("click", this.handlerPreventClick, options);
            }

            this.click.removeAll();
            this.touchstart.removeAll();
            this.touchmove.removeAll();
            this.touchend.removeAll();
            this.touchcancel.removeAll();

            this.element = null;
        }
    }

    // INTERNAL METHODS

    /**
     *
     * @param {Event} event
     */
    internalPreventClick(event) {
        window.focus();
        event.preventDefault();
    }

    /**
     * Internal method to get the options to pass to an event listener
     */
    internalGetEventListenerOptions() {
        return {
            capture: this.consumeEvents,
            passive: !this.preventDefault,
        };
    }

    /**
     * Binds the click detector to an element
     * @param {HTMLElement} element
     */
    internalBindTo(element) {
        const options = this.internalGetEventListenerOptions();

        this.handlerTouchStart = this.internalOnPointerDown.bind(this);
        this.handlerTouchEnd = this.internalOnPointerEnd.bind(this);
        this.handlerTouchMove = this.internalOnPointerMove.bind(this);
        this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);

        if (this.preventClick) {
            this.handlerPreventClick = this.internalPreventClick.bind(this);
            element.addEventListener("click", this.handlerPreventClick, options);
        }

        if (_config__WEBPACK_IMPORTED_MODULE_4__["SUPPORT_TOUCH"]) {
            element.addEventListener("touchstart", this.handlerTouchStart, options);
            element.addEventListener("touchend", this.handlerTouchEnd, options);
            element.addEventListener("touchcancel", this.handlerTouchCancel, options);
        }

        element.addEventListener("mousedown", this.handlerTouchStart, options);
        element.addEventListener("mouseup", this.handlerTouchEnd, options);
        element.addEventListener("mouseout", this.handlerTouchCancel, options);

        if (this.captureTouchmove) {
            if (_config__WEBPACK_IMPORTED_MODULE_4__["SUPPORT_TOUCH"]) {
                element.addEventListener("touchmove", this.handlerTouchMove, options);
            }
            element.addEventListener("mousemove", this.handlerTouchMove, options);
        }

        if (registerClickDetectors) {
            window.activeClickDetectors.push(this);
        }
        this.element = element;
    }

    /**
     * Returns if the bound element is currently in the DOM.
     */
    internalIsDomElementAttached() {
        return this.element && document.documentElement.contains(this.element);
    }

    /**
     * Checks if the given event is relevant for this detector
     * @param {TouchEvent|MouseEvent} event
     */
    internalEventPreHandler(event, expectedRemainingTouches = 1) {
        if (!this.element) {
            // Already cleaned up
            return false;
        }

        if (this.targetOnly && event.target !== this.element) {
            // Clicked a child element
            return false;
        }

        // Stop any propagation and defaults if configured
        if (this.consumeEvents && event.cancelable) {
            event.stopPropagation();
        }

        if (this.preventDefault && event.cancelable) {
            event.preventDefault();
        }

        if (window.TouchEvent && event instanceof TouchEvent) {
            clickDetectorGlobals.lastTouchTime = performance.now();

            // console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches);
            if (event.targetTouches.length !== expectedRemainingTouches) {
                return false;
            }
        }

        if (event instanceof MouseEvent) {
            if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) {
                return false;
            }
        }

        return true;
    }

    /**
     * Extracts the mous position from an event
     * @param {TouchEvent|MouseEvent} event
     * @returns {Vector} The client space position
     */
    static extractPointerPosition(event) {
        if (window.TouchEvent && event instanceof TouchEvent) {
            if (event.changedTouches.length !== 1) {
                logger.warn(
                    "Got unexpected target touches:",
                    event.targetTouches.length,
                    "->",
                    event.targetTouches
                );
                return new _vector__WEBPACK_IMPORTED_MODULE_3__["Vector"](0, 0);
            }

            const touch = event.changedTouches[0];
            return new _vector__WEBPACK_IMPORTED_MODULE_3__["Vector"](touch.clientX, touch.clientY);
        }

        if (event instanceof MouseEvent) {
            return new _vector__WEBPACK_IMPORTED_MODULE_3__["Vector"](event.clientX, event.clientY);
        }

        window.assert(false, "Got unknown event: " + event);

        return new _vector__WEBPACK_IMPORTED_MODULE_3__["Vector"](0, 0);
    }

    /**
     * Cacnels all ongoing events on this detector
     */
    cancelOngoingEvents() {
        if (this.applyCssClass && this.element) {
            this.element.classList.remove(this.applyCssClass);
        }
        this.clickDownPosition = null;
        this.clickStartTime = null;
        this.cancelled = true;
        Object(_utils__WEBPACK_IMPORTED_MODULE_2__["fastArrayDeleteValueIfContained"])(ongoingClickDetectors, this);
    }

    /**
     * Internal pointer down handler
     * @param {TouchEvent|MouseEvent} event
     */
    internalOnPointerDown(event) {
        window.focus();

        if (!this.internalEventPreHandler(event, 1)) {
            return false;
        }

        const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);

        if (event instanceof MouseEvent) {
            const isRightClick = event.button === 2;
            if (isRightClick) {
                // Ignore right clicks
                this.rightClick.dispatch(position, event);
                this.cancelled = true;
                this.clickDownPosition = null;
                return;
            }
        }

        if (this.clickDownPosition) {
            logger.warn("Ignoring double click");
            return false;
        }

        this.cancelled = false;
        this.touchstart.dispatch(event);

        // Store where the touch started
        this.clickDownPosition = position;
        this.clickStartTime = performance.now();
        this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y);

        // If we are not currently within a click, register it
        if (ongoingClickDetectors.indexOf(this) < 0) {
            ongoingClickDetectors.push(this);
        } else {
            logger.warn("Click detector got pointer down of active pointer twice");
        }

        // If we should apply any classes, do this now
        if (this.applyCssClass) {
            this.element.classList.add(this.applyCssClass);
        }

        // If we should play any sound, do this
        if (this.clickSound) {
            _globals__WEBPACK_IMPORTED_MODULE_6__["GLOBAL_APP"].sound.playUiSound(this.clickSound);
        }

        return false;
    }

    /**
     * Internal pointer move handler
     * @param {TouchEvent|MouseEvent} event
     */
    internalOnPointerMove(event) {
        if (!this.internalEventPreHandler(event, 1)) {
            return false;
        }
        this.touchmove.dispatch(event);
        const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
        this.touchmoveSimple.dispatch(pos.x, pos.y);
        return false;
    }

    /**
     * Internal pointer end handler
     * @param {TouchEvent|MouseEvent} event
     */
    internalOnPointerEnd(event) {
        window.focus();

        if (!this.internalEventPreHandler(event, 0)) {
            return false;
        }

        if (this.cancelled) {
            // warn(this, "Not dispatching touchend on cancelled listener");
            return false;
        }

        if (event instanceof MouseEvent) {
            const isRightClick = event.button === 2;
            if (isRightClick) {
                return;
            }
        }

        const index = ongoingClickDetectors.indexOf(this);
        if (index < 0) {
            logger.warn("Got pointer end but click detector is not in pressed state");
        } else {
            Object(_utils__WEBPACK_IMPORTED_MODULE_2__["fastArrayDelete"])(ongoingClickDetectors, index);
        }

        let dispatchClick = false;
        let dispatchClickPos = null;

        // Check for correct down position, otherwise must have pinched or so
        if (this.clickDownPosition) {
            const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
            const distance = pos.distance(this.clickDownPosition);
            if (!_config__WEBPACK_IMPORTED_MODULE_4__["IS_MOBILE"] || distance <= this.maxDistance) {
                dispatchClick = true;
                dispatchClickPos = pos;
            } else {
                console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")");
            }
        }

        this.clickDownPosition = null;
        this.clickStartTime = null;

        if (this.applyCssClass) {
            this.element.classList.remove(this.applyCssClass);
        }

        // Dispatch in the end to avoid the element getting invalidated
        // Also make sure that the element is still in the dom
        if (this.internalIsDomElementAttached()) {
            this.touchend.dispatch(event);
            this.touchendSimple.dispatch();

            if (dispatchClick) {
                const detectors = ongoingClickDetectors.slice();
                for (let i = 0; i < detectors.length; ++i) {
                    detectors[i].cancelOngoingEvents();
                }
                this.click.dispatch(dispatchClickPos, event);
            }
        }
        return false;
    }

    /**
     * Internal touch cancel handler
     * @param {TouchEvent|MouseEvent} event
     */
    internalOnTouchCancel(event) {
        if (!this.internalEventPreHandler(event, 0)) {
            return false;
        }

        if (this.cancelled) {
            // warn(this, "Not dispatching touchcancel on cancelled listener");
            return false;
        }

        this.cancelOngoingEvents();
        this.touchcancel.dispatch(event);
        this.touchendSimple.dispatch(event);
        return false;
    }
}


/***/ }),

/***/ "./src/js/core/config.js":
/*!*******************************!*\
  !*** ./src/js/core/config.js ***!
  \*******************************/
/*! exports provided: IS_DEBUG, SUPPORT_TOUCH, IS_MAC, THIRDPARTY_URLS, A_B_TESTING_LINK_TYPE, globalConfig, IS_MOBILE */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IS_DEBUG", function() { return IS_DEBUG; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SUPPORT_TOUCH", function() { return SUPPORT_TOUCH; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IS_MAC", function() { return IS_MAC; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "THIRDPARTY_URLS", function() { return THIRDPARTY_URLS; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "A_B_TESTING_LINK_TYPE", function() { return A_B_TESTING_LINK_TYPE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "globalConfig", function() { return globalConfig; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IS_MOBILE", function() { return IS_MOBILE; });
const IS_DEBUG =
     false &&
    false;

const SUPPORT_TOUCH = false;

const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0;

const smoothCanvas = true;

const THIRDPARTY_URLS = {
    discord: "https://discord.gg/HN7EVzV",
    github: "https://github.com/tobspr/shapez.io",
    reddit: "https://www.reddit.com/r/shapezio",
    shapeViewer: "https://viewer.shapez.io",

    standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",

    levelTutorialVideos: {
        21: "https://www.youtube.com/watch?v=0nUfRLMCcgo&",
        25: "https://www.youtube.com/watch?v=7OCV1g40Iew&",
        26: "https://www.youtube.com/watch?v=gfm6dS1dCoY",
    },
};

// export const A_B_TESTING_LINK_TYPE = Math.random() > 0.95 ? "steam_1_pr" : "steam_2_npr";
const A_B_TESTING_LINK_TYPE = "steam_2_npr";

const globalConfig = {
    // Size of a single tile in Pixels.
    // NOTICE: Update webpack.production.config too!
    tileSize: 32,
    halfTileSize: 16,

    // Which dpi the assets have
    assetsDpi: 192 / 32,
    assetsSharpness: 1.5,
    shapesSharpness: 1.4,

    // Production analytics
    statisticsGraphDpi: 2.5,
    statisticsGraphSlices: 100,
    analyticsSliceDurationSeconds:  false ? undefined : 10,

    minimumTickRate: 25,
    maximumTickRate: 500,

    // Map
    mapChunkSize: 16,
    mapChunkOverviewMinZoom: 0.9,
    mapChunkWorldSize: null, // COMPUTED

    // Belt speeds
    // NOTICE: Update webpack.production.config too!
    beltSpeedItemsPerSecond: 2,
    minerSpeedItemsPerSecond: 0, // COMPUTED

    defaultItemDiameter: 20,

    itemSpacingOnBelts: 0.63,

    wiresSpeedItemsPerSecond: 6,

    undergroundBeltMaxTilesByTier: [5, 9],

    readerAnalyzeIntervalSeconds: 10,

    buildingSpeeds: {
        cutter: 1 / 4,
        cutterQuad: 1 / 4,
        rotater: 1 / 1,
        rotaterCCW: 1 / 1,
        rotater180: 1 / 1,
        painter: 1 / 6,
        painterDouble: 1 / 8,
        painterQuad: 1 / 2,
        mixer: 1 / 5,
        stacker: 1 / 8,
    },

    // Zooming
    initialZoom: 1.9,
    minZoomLevel: 0.1,
    maxZoomLevel: 3,

    // Global game speed
    gameSpeed: 1,

    warmupTimeSecondsFast: 0.5,
    warmupTimeSecondsRegular: 3,

    smoothing: {
        smoothMainCanvas: smoothCanvas && true,
        quality: "low", // Low is CRUCIAL for mobile performance!
    },

    rendering: {},
    debug: __webpack_require__(/*! ./config.local */ "./src/js/core/config.local.js").default,

    // Secret vars
    info: {
        // Binary file salt
        file: "Ec'])@^+*9zMevK3uMV4432x9%iK'=",

        // Savegame salt
        sgSalt: "}95Q3%8/.837Lqym_BJx%q7)pAHJbF",

        // Analytics key
        analyticsApiKey: "baf6a50f0cc7dfdec5a0e21c88a1c69a4b34bc4a",
    },
};

const IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

// Automatic calculations
globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5;

globalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize;

// Dynamic calculations
if (globalConfig.debug.disableMapOverview) {
    globalConfig.mapChunkOverviewMinZoom = 0;
}

// Stuff for making the trailer
if (false) {}

if (globalConfig.debug.fastGameEnter) {
    globalConfig.debug.noArtificialDelays = true;
}

if (false) {}


/***/ }),

/***/ "./src/js/core/config.local.js":
/*!*************************************!*\
  !*** ./src/js/core/config.local.js ***!
  \*************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ({
    // You can set any debug options here!
    /* dev:start */
    // -----------------------------------------------------------------------------------
    // Quickly enters the game and skips the main menu - good for fast iterating
    // fastGameEnter: true,
    // -----------------------------------------------------------------------------------
    // Skips any delays like transitions between states and such
    // noArtificialDelays: true,
    // -----------------------------------------------------------------------------------
    // Disables writing of savegames, useful for testing the same savegame over and over
    // disableSavegameWrite: true,
    // -----------------------------------------------------------------------------------
    // Shows bounds of all entities
    // showEntityBounds: true,
    // -----------------------------------------------------------------------------------
    // Shows arrows for every ejector / acceptor
    // showAcceptorEjectors: true,
    // -----------------------------------------------------------------------------------
    // Disables the music (Overrides any setting, can cause weird behaviour)
    // disableMusic: true,
    // -----------------------------------------------------------------------------------
    // Do not render static map entities (=most buildings)
    // doNotRenderStatics: true,
    // -----------------------------------------------------------------------------------
    // Allow to zoom freely without limits
    // disableZoomLimits: true,
    // -----------------------------------------------------------------------------------
    // All rewards can be unlocked by passing just 1 of any shape
    // rewardsInstant: true,
    // -----------------------------------------------------------------------------------
    // Unlocks all buildings
    // allBuildingsUnlocked: true,
    // -----------------------------------------------------------------------------------
    // Disables cost of blueprints
    // blueprintsNoCost: true,
    // -----------------------------------------------------------------------------------
    // Disables cost of upgrades
    // upgradesNoCost: true,
    // -----------------------------------------------------------------------------------
    // Disables the dialog when completing a level
    // disableUnlockDialog: true,
    // -----------------------------------------------------------------------------------
    // Disables the simulation - This effectively pauses the game.
    // disableLogicTicks: true,
    // -----------------------------------------------------------------------------------
    // Test the rendering if everything is clipped out properly
    // testClipping: true,
    // -----------------------------------------------------------------------------------
    // Allows to render slower, useful for recording at half speed to avoid stuttering
    // framePausesBetweenTicks: 250,
    // -----------------------------------------------------------------------------------
    // Replace all translations with emojis to see which texts are translateable
    // testTranslations: true,
    // -----------------------------------------------------------------------------------
    // Enables an inspector which shows information about the entity below the curosr
    // enableEntityInspector: true,
    // -----------------------------------------------------------------------------------
    // Enables ads in the local build (normally they are deactivated there)
    // testAds: true,
    // -----------------------------------------------------------------------------------
    // Disables the automatic switch to an overview when zooming out
    // disableMapOverview: true,
    // -----------------------------------------------------------------------------------
    // Disables the notification when there are new entries in the changelog since last played
    // disableUpgradeNotification: true,
    // -----------------------------------------------------------------------------------
    // Makes belts almost infinitely fast
    // instantBelts: true,
    // -----------------------------------------------------------------------------------
    // Makes item processors almost infinitely fast
    // instantProcessors: true,
    // -----------------------------------------------------------------------------------
    // Makes miners almost infinitely fast
    // instantMiners: true,
    // -----------------------------------------------------------------------------------
    // When using fastGameEnter, controls whether a new game is started or the last one is resumed
    // resumeGameOnFastEnter: true,
    // -----------------------------------------------------------------------------------
    // Special option used to render the trailer
    // renderForTrailer: true,
    // -----------------------------------------------------------------------------------
    // Whether to render changes
    // renderChanges: true,
    // -----------------------------------------------------------------------------------
    // Whether to render belt paths
    // renderBeltPaths: true,
    // -----------------------------------------------------------------------------------
    // Whether to check belt paths
    // checkBeltPaths: true,
    // -----------------------------------------------------------------------------------
    // Whether to items / s instead of items / m in stats
    // detailedStatistics: true,
    // -----------------------------------------------------------------------------------
    // Shows detailed information about which atlas is used
    // showAtlasInfo: true,
    // -----------------------------------------------------------------------------------
    // Renders the rotation of all wires
    // renderWireRotations: true,
    // -----------------------------------------------------------------------------------
    // Renders information about wire networks
    // renderWireNetworkInfos: true,
    // -----------------------------------------------------------------------------------
    // Disables ejector animations and processing
    // disableEjectorProcessing: true,
    // -----------------------------------------------------------------------------------
    // Allows manual ticking
    // manualTickOnly: true,
    // -----------------------------------------------------------------------------------
    // Disables slow asserts, useful for debugging performance
    // disableSlowAsserts: true,
    // -----------------------------------------------------------------------------------
    /* dev:end */
});


/***/ }),

/***/ "./src/js/core/dpi_manager.js":
/*!************************************!*\
  !*** ./src/js/core/dpi_manager.js ***!
  \************************************/
/*! exports provided: getDeviceDPI, smoothenDpi, prepareHighDPIContext, resizeHighDPICanvas, resizeCanvas, resizeCanvasAndClear */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getDeviceDPI", function() { return getDeviceDPI; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "smoothenDpi", function() { return smoothenDpi; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "prepareHighDPIContext", function() { return prepareHighDPIContext; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resizeHighDPICanvas", function() { return resizeHighDPICanvas; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resizeCanvas", function() { return resizeCanvas; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resizeCanvasAndClear", function() { return resizeCanvasAndClear; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");



/**
 * Returns the current dpi
 * @returns {number}
 */
function getDeviceDPI() {
    return window.devicePixelRatio || 1;
}

/**
 *
 * @param {number} dpi
 * @returns {number} Smoothed dpi
 */
function smoothenDpi(dpi) {
    if (dpi < 0.05) {
        return 0.05;
    } else if (dpi < 0.2) {
        return Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(Math.round(dpi / 0.04) * 0.04);
    } else if (dpi < 1) {
        return Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round1Digit"])(Math.round(dpi / 0.1) * 0.1);
    } else if (dpi < 4) {
        return Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round1Digit"])(Math.round(dpi / 0.5) * 0.5);
    } else {
        return 4;
    }
}

// Initial dpi
// setDPIMultiplicator(1);

/**
 * Prepares a context for hihg dpi rendering
 * @param {CanvasRenderingContext2D} context
 */
function prepareHighDPIContext(context, smooth = true) {
    const dpi = getDeviceDPI();
    context.scale(dpi, dpi);

    if (smooth) {
        context.imageSmoothingEnabled = true;
        context.webkitImageSmoothingEnabled = true;

        // @ts-ignore
        context.imageSmoothingQuality = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].smoothing.quality;
    } else {
        context.imageSmoothingEnabled = false;
        context.webkitImageSmoothingEnabled = false;
    }
}

/**
 * Resizes a high dpi canvas
 * @param {HTMLCanvasElement} canvas
 * @param {number} w
 * @param {number} h
 */
function resizeHighDPICanvas(canvas, w, h, smooth = true) {
    const dpi = getDeviceDPI();

    const wNumber = Math.floor(w);
    const hNumber = Math.floor(h);

    const targetW = Math.floor(wNumber * dpi);
    const targetH = Math.floor(hNumber * dpi);

    if (targetW !== canvas.width || targetH !== canvas.height) {
        // console.log("Resize Canvas from", canvas.width, canvas.height, "to", targetW, targetH)
        canvas.width = targetW;
        canvas.height = targetH;
        canvas.style.width = wNumber + "px";
        canvas.style.height = hNumber + "px";
        prepareHighDPIContext(canvas.getContext("2d"), smooth);
    }
}

/**
 * Resizes a canvas
 * @param {HTMLCanvasElement} canvas
 * @param {number} w
 * @param {number} h
 */
function resizeCanvas(canvas, w, h, setStyle = true) {
    const actualW = Math.ceil(w);
    const actualH = Math.ceil(h);
    if (actualW !== canvas.width || actualH !== canvas.height) {
        canvas.width = actualW;
        canvas.height = actualH;
        if (setStyle) {
            canvas.style.width = actualW + "px";
            canvas.style.height = actualH + "px";
        }
        // console.log("Resizing canvas to", actualW, "x", actualH);
    }
}

/**
 * Resizes a canvas and makes sure its cleared
 * @param {HTMLCanvasElement} canvas
 * @param {CanvasRenderingContext2D} context
 * @param {number} w
 * @param {number} h
 */
function resizeCanvasAndClear(canvas, context, w, h) {
    const actualW = Math.ceil(w);
    const actualH = Math.ceil(h);
    if (actualW !== canvas.width || actualH !== canvas.height) {
        canvas.width = actualW;
        canvas.height = actualH;
        canvas.style.width = actualW + "px";
        canvas.style.height = actualH + "px";
        // console.log("Resizing canvas to", actualW, "x", actualH);
    } else {
        context.clearRect(0, 0, actualW, actualH);
    }
}


/***/ }),

/***/ "./src/js/core/draw_parameters.js":
/*!****************************************!*\
  !*** ./src/js/core/draw_parameters.js ***!
  \****************************************/
/*! exports provided: DrawParameters */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DrawParameters", function() { return DrawParameters; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");


/**
 * @typedef {import("../game/root").GameRoot} GameRoot
 * @typedef {import("./rectangle").Rectangle} Rectangle
 */

class DrawParameters {
    constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
        /** @type {CanvasRenderingContext2D} */
        this.context = context;

        /** @type {Rectangle} */
        this.visibleRect = visibleRect;

        /** @type {string} */
        this.desiredAtlasScale = desiredAtlasScale;

        /** @type {number} */
        this.zoomLevel = zoomLevel;

        /** @type {GameRoot} */
        this.root = root;
    }
}


/***/ }),

/***/ "./src/js/core/draw_utils.js":
/*!***********************************!*\
  !*** ./src/js/core/draw_utils.js ***!
  \***********************************/
/*! exports provided: initDrawUtils, drawRotatedSprite, drawSpriteClipped */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initDrawUtils", function() { return initDrawUtils; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "drawRotatedSprite", function() { return drawRotatedSprite; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "drawSpriteClipped", function() { return drawSpriteClipped; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./rectangle */ "./src/js/core/rectangle.js");
/**
 * @typedef {import("./sprites").AtlasSprite} AtlasSprite
 * @typedef {import("./draw_parameters").DrawParameters} DrawParameters
 */





const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("draw_utils");

function initDrawUtils() {
    CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
        this.beginPath();

        if (r < 0.05) {
            this.rect(x, y, w, h);
            return;
        }

        if (w < 2 * r) {
            r = w / 2;
        }

        if (h < 2 * r) {
            r = h / 2;
        }

        this.moveTo(x + r, y);
        this.arcTo(x + w, y, x + w, y + h, r);
        this.arcTo(x + w, y + h, x, y + h, r);
        this.arcTo(x, y + h, x, y, r);
        this.arcTo(x, y, x + w, y, r);
    };

    CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) {
        this.beginPath();

        if (r < 0.05) {
            this.rect(x, y, 1, 1);
            return;
        }

        this.arc(x, y, r, 0, 2.0 * Math.PI);
    };
}

/**
 *
 * @param {object} param0
 * @param {DrawParameters} param0.parameters
 * @param {AtlasSprite} param0.sprite
 * @param {number} param0.x
 * @param {number} param0.y
 * @param {number} param0.angle
 * @param {number} param0.size
 * @param {number=} param0.offsetX
 * @param {number=} param0.offsetY
 */
function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offsetX = 0, offsetY = 0 }) {
    if (angle === 0) {
        sprite.drawCachedCentered(parameters, x + offsetX, y + offsetY, size);
        return;
    }

    parameters.context.translate(x, y);
    parameters.context.rotate(angle);
    sprite.drawCachedCentered(parameters, offsetX, offsetY, size, false);
    parameters.context.rotate(-angle);
    parameters.context.translate(-x, -y);
}

let warningsShown = 0;

/**
 * Draws a sprite with clipping
 * @param {object} param0
 * @param {DrawParameters} param0.parameters
 * @param {HTMLCanvasElement} param0.sprite
 * @param {number} param0.x
 * @param {number} param0.y
 * @param {number} param0.w
 * @param {number} param0.h
 * @param {number} param0.originalW
 * @param {number} param0.originalH
 */
function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, originalH }) {
    const rect = new _rectangle__WEBPACK_IMPORTED_MODULE_2__["Rectangle"](x, y, w, h);
    const intersection = rect.getIntersection(parameters.visibleRect);
    if (!intersection) {
        // Clipped
        if (++warningsShown % 200 === 1) {
            logger.warn(
                "Sprite drawn clipped but it's not on screen - perform culling before (",
                warningsShown,
                "warnings)"
            );
        }
        if (false) {}
        return;
    }

    parameters.context.drawImage(
        sprite,

        // src pos and size
        ((intersection.x - x) / w) * originalW,
        ((intersection.y - y) / h) * originalH,
        (originalW * intersection.w) / w,
        (originalH * intersection.h) / h,

        // dest pos and size
        intersection.x,
        intersection.y,
        intersection.w,
        intersection.h
    );
}


/***/ }),

/***/ "./src/js/core/error_handler.js":
/*!**************************************!*\
  !*** ./src/js/core/error_handler.js ***!
  \**************************************/
/*! exports provided: APPLICATION_ERROR_OCCURED */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "APPLICATION_ERROR_OCCURED", function() { return APPLICATION_ERROR_OCCURED; });
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");




let APPLICATION_ERROR_OCCURED = false;

/**
 *
 * @param {Event|string} message
 * @param {string} source
 * @param {number} lineno
 * @param {number} colno
 * @param {Error} source
 */
function catchErrors(message, source, lineno, colno, error) {
    let fullPayload = JSON.parse(
        Object(_logging__WEBPACK_IMPORTED_MODULE_0__["stringifyObjectContainingErrors"])({
            message,
            source,
            lineno,
            colno,
            error,
        })
    );

    if (("" + message).indexOf("Script error.") >= 0) {
        console.warn("Thirdparty script error:", message);
        return;
    }

    if (("" + message).indexOf("NS_ERROR_FAILURE") >= 0) {
        console.warn("Firefox NS_ERROR_FAILURE error:", message);
        return;
    }

    if (("" + message).indexOf("Cannot read property 'postMessage' of null") >= 0) {
        console.warn("Safari can not read post message error:", message);
        return;
    }

    if ( true && ("" + source).indexOf("shapez.io") < 0) {
        console.warn("Thirdparty error:", message);
        return;
    }

    console.log("\n\n\n⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️\n\n\n");
    console.log("          APPLICATION CRASHED             ");
    console.log("\n\n⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️\n\n\n");

    Object(_logging__WEBPACK_IMPORTED_MODULE_0__["logSection"])("APPLICATION CRASH", "#e53935");
    console.error("Error:", message, "->", error);
    console.log("Payload:", fullPayload);

    if (window.Sentry && !window.anyModLoaded) {
        window.Sentry.withScope(scope => {
            window.Sentry.setTag("message", message);
            window.Sentry.setTag("source", source);

            window.Sentry.setExtra("message", message);
            window.Sentry.setExtra("source", source);
            window.Sentry.setExtra("lineno", lineno);
            window.Sentry.setExtra("colno", colno);
            window.Sentry.setExtra("error", error);
            window.Sentry.setExtra("fullPayload", fullPayload);

            try {
                const userName = window.localStorage.getItem("tracking_context") || null;
                window.Sentry.setTag("username", userName);
            } catch (ex) {
                // ignore
            }

            window.Sentry.captureException(error || source);
        });
    }

    if (APPLICATION_ERROR_OCCURED) {
        console.warn("ERROR: Only showing and submitting first error");
        return;
    }

    APPLICATION_ERROR_OCCURED = true;
    const element = document.createElement("div");
    element.id = "applicationError";

    const title = document.createElement("h1");
    title.innerText = "Whoops!";
    element.appendChild(title);

    const desc = document.createElement("div");
    desc.classList.add("desc");
    desc.innerHTML = `
        It seems the application crashed - I am sorry for that!<br /><br />
        An anonymized crash report has been sent, and I will have a look as soon as possible.<br /><br />
        If you have additional information how I can reproduce this error, please E-Mail me:&nbsp;
        <a href="mailto:bugs@shapez.io?title=Application+Crash">bugs@shapez.io</a>`;
    element.appendChild(desc);

    const details = document.createElement("pre");
    details.classList.add("details");
    details.innerText = (error && error.stack) || message;
    element.appendChild(details);

    const inject = function () {
        if (true) {
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["removeAllChildren"])(document.body);
        }
        if (document.body.parentElement) {
            document.body.parentElement.appendChild(element);
        } else {
            document.body.appendChild(element);
        }
    };

    if (document.body) {
        inject();
    } else {
        setTimeout(() => {
            inject();
        }, 200);
    }

    return true;
}

window.onerror = catchErrors;


/***/ }),

/***/ "./src/js/core/explained_result.js":
/*!*****************************************!*\
  !*** ./src/js/core/explained_result.js ***!
  \*****************************************/
/*! exports provided: ExplainedResult */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExplainedResult", function() { return ExplainedResult; });
class ExplainedResult {
    constructor(result = true, reason = null, additionalProps = {}) {
        /** @type {boolean} */
        this.result = result;

        /** @type {string} */
        this.reason = reason;

        // Copy additional props
        for (const key in additionalProps) {
            this[key] = additionalProps[key];
        }
    }

    isGood() {
        return !!this.result;
    }

    isBad() {
        return !this.result;
    }

    static good() {
        return new ExplainedResult(true);
    }

    static bad(reason, additionalProps) {
        return new ExplainedResult(false, reason, additionalProps);
    }

    static requireAll(...args) {
        for (let i = 0; i < args.length; ++i) {
            const subResult = args[i].call();
            if (!subResult.isGood()) {
                return subResult;
            }
        }
        return this.good();
    }
}


/***/ }),

/***/ "./src/js/core/factory.js":
/*!********************************!*\
  !*** ./src/js/core/factory.js ***!
  \********************************/
/*! exports provided: Factory */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Factory", function() { return Factory; });
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");


const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("factory");

// simple factory pattern
class Factory {
    constructor(id) {
        this.id = id;

        // Store array as well as dictionary, to speed up lookups
        this.entries = [];
        this.entryIds = [];
        this.idToEntry = {};
    }

    getId() {
        return this.id;
    }

    register(entry) {
        // Extract id
        const id = entry.getId();
        window.assert(id, "Factory: Invalid id for class: " + entry);

        // Check duplicates
        window.assert(!this.idToEntry[id], "Duplicate factory entry for " + id);

        // Insert
        this.entries.push(entry);
        this.entryIds.push(id);
        this.idToEntry[id] = entry;
    }

    /**
     * Checks if a given id is registered
     * @param {string} id
     * @returns {boolean}
     */
    hasId(id) {
        return !!this.idToEntry[id];
    }

    /**
     * Finds an instance by a given id
     * @param {string} id
     * @returns {object}
     */
    findById(id) {
        const entry = this.idToEntry[id];
        if (!entry) {
            logger.error("Object with id", id, "is not registered on factory", this.id, "!");
            window.assert(false, "Factory: Object with id '" + id + "' is not registered!");
            return null;
        }
        return entry;
    }

    /**
     * Returns all entries
     * @returns {Array<object>}
     */
    getEntries() {
        return this.entries;
    }

    /**
     * Returns all registered ids
     * @returns {Array<string>}
     */
    getAllIds() {
        return this.entryIds;
    }

    /**
     * Returns amount of stored entries
     * @returns {number}
     */
    getNumEntries() {
        return this.entries.length;
    }
}


/***/ }),

/***/ "./src/js/core/game_state.js":
/*!***********************************!*\
  !*** ./src/js/core/game_state.js ***!
  \***********************************/
/*! exports provided: GameState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameState", function() { return GameState; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _click_detector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _input_receiver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
/* harmony import */ var _request_channel__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./request_channel */ "./src/js/core/request_channel.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");









const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("game_state");

/**
 * Basic state of the game state machine. This is the base of the whole game
 */
class GameState {
    /**
     * Constructs a new state with the given id
     * @param {string} key The id of the state. We use ids to refer to states because otherwise we get
     *                     circular references
     */
    constructor(key) {
        this.key = key;

        /** @type {StateManager} */
        this.stateManager = null;

        /** @type {Application} */
        this.app = null;

        // Store if we are currently fading out
        this.fadingOut = false;

        /** @type {Array<ClickDetector>} */
        this.clickDetectors = [];

        // Every state captures keyboard events by default
        this.inputReciever = new _input_receiver__WEBPACK_IMPORTED_MODULE_3__["InputReceiver"]("state-" + key);
        this.inputReciever.backButton.add(this.onBackButton, this);

        // A channel we can use to perform async ops
        this.asyncChannel = new _request_channel__WEBPACK_IMPORTED_MODULE_5__["RequestChannel"]();
    }

    //// GETTERS / HELPER METHODS ////

    /**
     * Returns the states key
     * @returns {string}
     */
    getKey() {
        return this.key;
    }

    /**
     * Returns the html element of the state
     * @returns {HTMLElement}
     */
    getDivElement() {
        return document.getElementById("state_" + this.key);
    }

    /**
     * Transfers to a new state
     * @param {string} stateKey The id of the new state
     */
    moveToState(stateKey, payload = {}, skipFadeOut = false) {
        if (this.fadingOut) {
            logger.warn("Skipping move to '" + stateKey + "' since already fading out");
            return;
        }

        // Clean up event listeners
        this.internalCleanUpClickDetectors();

        // Fading
        const fadeTime = this.internalGetFadeInOutTime();
        const doFade = !skipFadeOut && this.getHasFadeOut() && fadeTime !== 0;
        logger.log("Moving to", stateKey, "(fading=", doFade, ")");
        if (doFade) {
            this.htmlElement.classList.remove("arrived");
            this.fadingOut = true;
            setTimeout(() => {
                this.stateManager.moveToState(stateKey, payload);
            }, fadeTime);
        } else {
            this.stateManager.moveToState(stateKey, payload);
        }
    }

    /**
     *
     * @param {string} nextStateId
     * @param {object=} nextStatePayload
     */
    watchAdAndMoveToState(nextStateId, nextStatePayload = {}) {
        if (this.app.adProvider.getCanShowVideoAd() && this.app.isRenderable()) {
            this.moveToState(
                "WatchAdState",
                {
                    nextStateId,
                    nextStatePayload,
                },
                true
            );
        } else {
            this.moveToState(nextStateId, nextStatePayload);
        }
    }

    /**
     * Tracks clicks on a given element and calls the given callback *on this state*.
     * If you want to call another function wrap it inside a lambda.
     * @param {Element} element The element to track clicks on
     * @param {function():void} handler The handler to call
     * @param {import("./click_detector").ClickDetectorConstructorArgs=} args Click detector arguments
     */
    trackClicks(element, handler, args = {}) {
        const detector = new _click_detector__WEBPACK_IMPORTED_MODULE_1__["ClickDetector"](element, args);
        detector.click.add(handler, this);
        if (false) {}
        this.clickDetectors.push(detector);
    }

    /**
     * Cancels all promises on the api as well as our async channel
     */
    cancelAllAsyncOperations() {
        this.asyncChannel.cancelAll();
    }

    //// CALLBACKS ////

    /**
     * Callback when entering the state, to be overriddemn
     * @param {any} payload Arbitrary data passed from the state which we are transferring from
     */
    onEnter(payload) {}

    /**
     * Callback when leaving the state
     */
    onLeave() {}

    /**
     * Callback before leaving the game state or when the page is unloaded
     */
    onBeforeExit() {}

    /**
     * Callback when the app got paused (on android, this means in background)
     */
    onAppPause() {}

    /**
     * Callback when the app got resumed (on android, this means in foreground again)
     */
    onAppResume() {}

    /**
     * Render callback
     * @param {number} dt Delta time in ms since last render
     */
    onRender(dt) {}

    /**
     * Background tick callback, called while the game is inactiev
     * @param {number} dt Delta time in ms since last tick
     */
    onBackgroundTick(dt) {}

    /**
     * Called when the screen resized
     * @param {number} w window/screen width
     * @param {number} h window/screen height
     */
    onResized(w, h) {}

    /**
     * Internal backbutton handler, called when the hardware back button is pressed or
     * the escape key is pressed
     */
    onBackButton() {}

    //// INTERFACE ////

    /**
     * Should return how many mulliseconds to fade in / out the state. Not recommended to override!
     * @returns {number} Time in milliseconds to fade out
     */
    getInOutFadeTime() {
        if (_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].debug.noArtificialDelays) {
            return 0;
        }
        return 200;
    }

    /**
     * Should return whether to fade in the game state. This will then apply the right css classes
     * for the fadein.
     * @returns {boolean}
     */
    getHasFadeIn() {
        return true;
    }

    /**
     * Should return whether to fade out the game state. This will then apply the right css classes
     * for the fadeout and wait the delay before moving states
     * @returns {boolean}
     */
    getHasFadeOut() {
        return true;
    }

    /**
     * Returns if this state should get paused if it does not have focus
     * @returns {boolean} true to pause the updating of the game
     */
    getPauseOnFocusLost() {
        return true;
    }

    /**
     * Should return the html code of the state.
     * @returns {string}
     */
    getInnerHTML() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "";
    }

    /**
     * Returns if the state has an unload confirmation, this is the
     * "Are you sure you want to leave the page" message.
     */
    getHasUnloadConfirmation() {
        return false;
    }

    /**
     * Should return the theme music for this state
     * @returns {string|null}
     */
    getThemeMusic() {
        return _platform_sound__WEBPACK_IMPORTED_MODULE_6__["MUSIC"].menu;
    }

    ////////////////////

    //// INTERNAL ////

    /**
     * Internal callback from the manager. Do not override!
     * @param {StateManager} stateManager
     */
    internalRegisterCallback(stateManager, app) {
        window.assert(stateManager, "No state manager");
        window.assert(app, "No app");
        this.stateManager = stateManager;
        this.app = app;
    }

    /**
     * Internal callback when entering the state. Do not override!
     * @param {any} payload Arbitrary data passed from the state which we are transferring from
     * @param {boolean} callCallback Whether to call the onEnter callback
     */
    internalEnterCallback(payload, callCallback = true) {
        Object(_logging__WEBPACK_IMPORTED_MODULE_2__["logSection"])(this.key, "#26a69a");
        this.app.inputMgr.pushReciever(this.inputReciever);

        this.htmlElement = this.getDivElement();
        this.htmlElement.classList.add("active");

        // Apply classes in the next frame so the css transition keeps up
        Object(_utils__WEBPACK_IMPORTED_MODULE_4__["waitNextFrame"])().then(() => {
            if (this.htmlElement) {
                this.htmlElement.classList.remove("fadingOut");
                this.htmlElement.classList.remove("fadingIn");
            }
        });

        // Call handler
        if (callCallback) {
            this.onEnter(payload);
        }
    }

    /**
     * Internal callback when the state is left. Do not override!
     */
    internalLeaveCallback() {
        this.onLeave();

        this.htmlElement.classList.remove("active");
        this.app.inputMgr.popReciever(this.inputReciever);
        this.internalCleanUpClickDetectors();
        this.asyncChannel.cancelAll();
    }

    /**
     * Internal callback *before* the state is left. Also is called on page unload
     */
    internalOnBeforeExitCallback() {
        this.onBeforeExit();
    }

    /**
     * Internal app pause callback
     */
    internalOnAppPauseCallback() {
        this.onAppPause();
    }

    /**
     * Internal app resume callback
     */
    internalOnAppResumeCallback() {
        this.onAppResume();
    }

    /**
     * Cleans up all click detectors
     */
    internalCleanUpClickDetectors() {
        if (this.clickDetectors) {
            for (let i = 0; i < this.clickDetectors.length; ++i) {
                this.clickDetectors[i].cleanup();
            }
            this.clickDetectors = [];
        }
    }

    /**
     * Internal method to get the HTML of the game state.
     * @returns {string}
     */
    internalGetFullHtml() {
        return this.getInnerHTML();
    }

    /**
     * Internal method to compute the time to fade in / out
     * @returns {number} time to fade in / out in ms
     */
    internalGetFadeInOutTime() {
        if (false) {}
        if (false) {}
        return this.getInOutFadeTime();
    }
}


/***/ }),

/***/ "./src/js/core/global_registries.js":
/*!******************************************!*\
  !*** ./src/js/core/global_registries.js ***!
  \******************************************/
/*! exports provided: gMetaBuildingRegistry, gBuildingsByCategory, gComponentRegistry, gGameSpeedRegistry, gItemRegistry, initBuildingsByCategory */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gMetaBuildingRegistry", function() { return gMetaBuildingRegistry; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gBuildingsByCategory", function() { return gBuildingsByCategory; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gComponentRegistry", function() { return gComponentRegistry; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gGameSpeedRegistry", function() { return gGameSpeedRegistry; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gItemRegistry", function() { return gItemRegistry; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initBuildingsByCategory", function() { return initBuildingsByCategory; });
/* harmony import */ var _singleton_factory__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./singleton_factory */ "./src/js/core/singleton_factory.js");
/* harmony import */ var _factory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./factory */ "./src/js/core/factory.js");



/**
 * @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed
 * @typedef {import("../game/component").Component} Component
 * @typedef {import("../game/base_item").BaseItem} BaseItem
 * @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding


// These factories are here to remove circular dependencies

/** @type {SingletonFactoryTemplate<MetaBuilding>} */
let gMetaBuildingRegistry = new _singleton_factory__WEBPACK_IMPORTED_MODULE_0__["SingletonFactory"]();

/** @type {Object.<string, Array<Class<MetaBuilding>>>} */
let gBuildingsByCategory = null;

/** @type {FactoryTemplate<Component>} */
let gComponentRegistry = new _factory__WEBPACK_IMPORTED_MODULE_1__["Factory"]("component");

/** @type {FactoryTemplate<BaseGameSpeed>} */
let gGameSpeedRegistry = new _factory__WEBPACK_IMPORTED_MODULE_1__["Factory"]("gamespeed");

/** @type {FactoryTemplate<BaseItem>} */
let gItemRegistry = new _factory__WEBPACK_IMPORTED_MODULE_1__["Factory"]("item");

// Helpers

/**
 * @param {Object.<string, Array<Class<MetaBuilding>>>} buildings
 */
function initBuildingsByCategory(buildings) {
    gBuildingsByCategory = buildings;
}


/***/ }),

/***/ "./src/js/core/globals.js":
/*!********************************!*\
  !*** ./src/js/core/globals.js ***!
  \********************************/
/*! exports provided: GLOBAL_APP, setGlobalApp */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GLOBAL_APP", function() { return GLOBAL_APP; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setGlobalApp", function() { return setGlobalApp; });

/**
 * Used for the bug reporter, and the click detector which both have no handles to this.
 * It would be nicer to have no globals, but this is the only one. I promise!
 * @type {Application} */
let GLOBAL_APP = null;

/**
 * @param {Application} app
 */
function setGlobalApp(app) {
    window.assert(!GLOBAL_APP, "Create application twice!");
    GLOBAL_APP = app;
}


/***/ }),

/***/ "./src/js/core/input_distributor.js":
/*!******************************************!*\
  !*** ./src/js/core/input_distributor.js ***!
  \******************************************/
/*! exports provided: InputDistributor */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputDistributor", function() { return InputDistributor; });
/* harmony import */ var _signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./signal */ "./src/js/core/signal.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");





const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("input_distributor");

class InputDistributor {
    /**
     *
     * @param {Application} app
     */
    constructor(app) {
        this.app = app;

        /** @type {Array<InputReceiver>} */
        this.recieverStack = [];

        /** @type {Array<function(any) : boolean>} */
        this.filters = [];

        /**
         * All keys which are currently down
         */
        this.keysDown = new Set();

        this.bindToEvents();
    }

    /**
     * Attaches a new filter which can filter and reject events
     * @param {function(any): boolean} filter
     */
    installFilter(filter) {
        this.filters.push(filter);
    }

    /**
     * Removes an attached filter
     * @param {function(any) : boolean} filter
     */
    dismountFilter(filter) {
        Object(_utils__WEBPACK_IMPORTED_MODULE_2__["fastArrayDeleteValue"])(this.filters, filter);
    }

    /**
     * @param {InputReceiver} reciever
     */
    pushReciever(reciever) {
        if (this.isRecieverAttached(reciever)) {
            window.assert(false, "Can not add reciever " + reciever.context + " twice");
            logger.error("Can not add reciever", reciever.context, "twice");
            return;
        }
        this.recieverStack.push(reciever);

        if (this.recieverStack.length > 10) {
            logger.error(
                "Reciever stack is huge, probably some dead receivers arround:",
                this.recieverStack.map(x => x.context)
            );
        }
    }

    /**
     * @param {InputReceiver} reciever
     */
    popReciever(reciever) {
        if (this.recieverStack.indexOf(reciever) < 0) {
            window.assert(false, "Can not pop reciever " + reciever.context + "  since its not contained");
            logger.error("Can not pop reciever", reciever.context, "since its not contained");
            return;
        }
        if (this.recieverStack[this.recieverStack.length - 1] !== reciever) {
            logger.warn(
                "Popping reciever",
                reciever.context,
                "which is not on top of the stack. Stack is: ",
                this.recieverStack.map(x => x.context)
            );
        }
        Object(_utils__WEBPACK_IMPORTED_MODULE_2__["arrayDeleteValue"])(this.recieverStack, reciever);
    }

    /**
     * @param {InputReceiver} reciever
     */
    isRecieverAttached(reciever) {
        return this.recieverStack.indexOf(reciever) >= 0;
    }

    /**
     * @param {InputReceiver} reciever
     */
    isRecieverOnTop(reciever) {
        return (
            this.isRecieverAttached(reciever) &&
            this.recieverStack[this.recieverStack.length - 1] === reciever
        );
    }

    /**
     * @param {InputReceiver} reciever
     */
    makeSureAttachedAndOnTop(reciever) {
        this.makeSureDetached(reciever);
        this.pushReciever(reciever);
    }

    /**
     * @param {InputReceiver} reciever
     */
    makeSureDetached(reciever) {
        if (this.isRecieverAttached(reciever)) {
            Object(_utils__WEBPACK_IMPORTED_MODULE_2__["arrayDeleteValue"])(this.recieverStack, reciever);
        }
    }

    /**
     *
     * @param {InputReceiver} reciever
     */
    destroyReceiver(reciever) {
        this.makeSureDetached(reciever);
        reciever.cleanup();
    }

    // Internal

    getTopReciever() {
        if (this.recieverStack.length > 0) {
            return this.recieverStack[this.recieverStack.length - 1];
        }
        return null;
    }

    bindToEvents() {
        window.addEventListener("popstate", this.handleBackButton.bind(this), false);
        document.addEventListener("backbutton", this.handleBackButton.bind(this), false);

        window.addEventListener("keydown", this.handleKeyMouseDown.bind(this));
        window.addEventListener("keyup", this.handleKeyMouseUp.bind(this));

        window.addEventListener("mousedown", this.handleKeyMouseDown.bind(this));
        window.addEventListener("mouseup", this.handleKeyMouseUp.bind(this));

        window.addEventListener("blur", this.handleBlur.bind(this));
    }

    forwardToReceiver(eventId, payload = null) {
        // Check filters
        for (let i = 0; i < this.filters.length; ++i) {
            if (!this.filters[i](eventId)) {
                return _signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
            }
        }

        const reciever = this.getTopReciever();
        if (!reciever) {
            logger.warn("Dismissing event because not reciever was found:", eventId);
            return;
        }
        const signal = reciever[eventId];
        window.assert(signal instanceof _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"], "Not a valid event id");
        return signal.dispatch(payload);
    }

    /**
     * @param {Event} event
     */
    handleBackButton(event) {
        event.preventDefault();
        event.stopPropagation();
        this.forwardToReceiver("backButton");
    }

    /**
     * Handles when the page got blurred
     */
    handleBlur() {
        this.forwardToReceiver("pageBlur", {});
        this.keysDown.clear();
    }

    /**
     * @param {KeyboardEvent | MouseEvent} event
     */
    handleKeyMouseDown(event) {
        const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode;
        if (
            keyCode === 4 || // MB4
            keyCode === 5 || // MB5
            keyCode === 9 || // TAB
            keyCode === 16 || // SHIFT
            keyCode === 17 || // CTRL
            keyCode === 18 || // ALT
            (keyCode >= 112 && keyCode < 122) // F1 - F10
        ) {
            event.preventDefault();
        }

        const isInitial = !this.keysDown.has(keyCode);
        this.keysDown.add(keyCode);

        if (
            this.forwardToReceiver("keydown", {
                keyCode: keyCode,
                shift: event.shiftKey,
                alt: event.altKey,
                initial: isInitial,
                event,
            }) === _signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"]
        ) {
            return;
        }

        if (keyCode === 27) {
            // Escape key
            event.preventDefault();
            event.stopPropagation();
            return this.forwardToReceiver("backButton");
        }
    }

    /**
     * @param {KeyboardEvent | MouseEvent} event
     */
    handleKeyMouseUp(event) {
        const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode;
        this.keysDown.delete(keyCode);

        this.forwardToReceiver("keyup", {
            keyCode: keyCode,
            shift: event.shiftKey,
            alt: event.altKey,
        });
    }
}


/***/ }),

/***/ "./src/js/core/input_receiver.js":
/*!***************************************!*\
  !*** ./src/js/core/input_receiver.js ***!
  \***************************************/
/*! exports provided: InputReceiver */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputReceiver", function() { return InputReceiver; });
/* harmony import */ var _signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./signal */ "./src/js/core/signal.js");


class InputReceiver {
    constructor(context = "unknown") {
        this.context = context;

        this.backButton = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();

        this.keydown = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
        this.keyup = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
        this.pageBlur = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();

        // Dispatched on destroy
        this.destroyed = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
    }

    cleanup() {
        this.backButton.removeAll();
        this.keydown.removeAll();
        this.keyup.removeAll();

        this.destroyed.dispatch();
    }
}


/***/ }),

/***/ "./src/js/core/loader.js":
/*!*******************************!*\
  !*** ./src/js/core/loader.js ***!
  \*******************************/
/*! exports provided: Loader */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Loader", function() { return Loader; });
/* harmony import */ var _buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _sprites__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _cachebust__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cachebust */ "./src/js/core/cachebust.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");





/**
 * @typedef {import("../application").Application} Application
 * @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition;
 */

const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_3__["createLogger"])("loader");

const missingSpriteIds = {};

class LoaderImpl {
    constructor() {
        this.app = null;

        /** @type {Map<string, BaseSprite>} */
        this.sprites = new Map();

        this.rawImages = [];
    }

    /**
     * @param {Application} app
     */
    linkAppAfterBoot(app) {
        this.app = app;
        this.makeSpriteNotFoundCanvas();
    }

    /**
     * Fetches a given sprite from the cache
     * @param {string} key
     * @returns {BaseSprite}
     */
    getSpriteInternal(key) {
        const sprite = this.sprites.get(key);
        if (!sprite) {
            if (!missingSpriteIds[key]) {
                // Only show error once
                missingSpriteIds[key] = true;
                logger.error("Sprite '" + key + "' not found!");
            }
            return this.spriteNotFoundSprite;
        }
        return sprite;
    }

    /**
     * Returns an atlas sprite from the cache
     * @param {string} key
     * @returns {AtlasSprite}
     */
    getSprite(key) {
        const sprite = this.getSpriteInternal(key);
        window.assert(sprite instanceof _sprites__WEBPACK_IMPORTED_MODULE_1__["AtlasSprite"] || sprite === this.spriteNotFoundSprite, "Not an atlas sprite");
        return /** @type {AtlasSprite} */ (sprite);
    }

    /**
     * Returns a regular sprite from the cache
     * @param {string} key
     * @returns {RegularSprite}
     */
    getRegularSprite(key) {
        const sprite = this.getSpriteInternal(key);
        window.assert(
            sprite instanceof _sprites__WEBPACK_IMPORTED_MODULE_1__["RegularSprite"] || sprite === this.spriteNotFoundSprite,
            "Not a regular sprite"
        );
        return /** @type {RegularSprite} */ (sprite);
    }

    /**
     *
     * @param {string} key
     * @returns {Promise<HTMLImageElement|null>}
     */
    internalPreloadImage(key) {
        const url = Object(_cachebust__WEBPACK_IMPORTED_MODULE_2__["cachebust"])("res/" + key);
        const image = new Image();

        let triesSoFar = 0;

        return Promise.race([
            new Promise((resolve, reject) => {
                setTimeout(reject,  false ? undefined : 10000);
            }),

            new Promise(resolve => {
                image.onload = () => {
                    image.onerror = null;
                    image.onload = null;

                    if (typeof image.decode === "function") {
                        // SAFARI: Image.decode() fails on safari with svgs -> we dont want to fail
                        // on that
                        // FIREFOX: Decode never returns if the image is in cache, so call it in background
                        image.decode().then(
                            () => null,
                            () => null
                        );
                    }
                    resolve(image);
                };

                image.onerror = reason => {
                    logger.warn("Failed to load '" + url + "':", reason);
                    if (++triesSoFar < 4) {
                        logger.log("Retrying to load image from", url);
                        image.src = url + "?try=" + triesSoFar;
                    } else {
                        logger.warn("Failed to load", url, "after", triesSoFar, "tries with reason", reason);
                        image.onerror = null;
                        image.onload = null;
                        resolve(null);
                    }
                };

                image.src = url;
            }),
        ]);
    }

    /**
     * Preloads a sprite
     * @param {string} key
     * @returns {Promise<void>}
     */
    preloadCSSSprite(key) {
        return this.internalPreloadImage(key).then(image => {
            if (key.indexOf("game_misc") >= 0) {
                // Allow access to regular sprites
                this.sprites.set(key, new _sprites__WEBPACK_IMPORTED_MODULE_1__["RegularSprite"](image, image.width, image.height));
            }
            this.rawImages.push(image);
        });
    }

    /**
     * Preloads an atlas
     * @param {AtlasDefinition} atlas
     * @returns {Promise<void>}
     */
    preloadAtlas(atlas) {
        return this.internalPreloadImage(atlas.getFullSourcePath()).then(image => {
            // @ts-ignore
            image.label = atlas.sourceFileName;
            return this.internalParseAtlas(atlas, image);
        });
    }

    /**
     *
     * @param {AtlasDefinition} atlas
     * @param {HTMLImageElement} loadedImage
     */
    internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) {
        this.rawImages.push(loadedImage);

        for (const spriteName in sourceData) {
            const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName];

            let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName));

            if (!sprite) {
                sprite = new _sprites__WEBPACK_IMPORTED_MODULE_1__["AtlasSprite"](spriteName);
                this.sprites.set(spriteName, sprite);
            }

            const link = new _sprites__WEBPACK_IMPORTED_MODULE_1__["SpriteAtlasLink"]({
                packedX: frame.x,
                packedY: frame.y,
                packedW: frame.w,
                packedH: frame.h,
                packOffsetX: spriteSourceSize.x,
                packOffsetY: spriteSourceSize.y,
                atlas: loadedImage,
                w: sourceSize.w,
                h: sourceSize.h,
            });
            sprite.linksByResolution[scale] = link;
        }
    }

    /**
     * Makes the canvas which shows the question mark, shown when a sprite was not found
     */
    makeSpriteNotFoundCanvas() {
        const dims = 128;

        const [canvas, context] = Object(_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(dims, dims, {
            smooth: false,
            label: "not-found-sprite",
        });
        context.fillStyle = "#f77";
        context.fillRect(0, 0, dims, dims);

        context.textAlign = "center";
        context.textBaseline = "middle";
        context.fillStyle = "#eee";
        context.font = "25px Arial";
        context.fillText("???", dims / 2, dims / 2);

        // TODO: Not sure why this is set here
        // @ts-ignore
        canvas.src = "not-found";

        const sprite = new _sprites__WEBPACK_IMPORTED_MODULE_1__["AtlasSprite"]("not-found");
        ["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => {
            sprite.linksByResolution[resolution] = new _sprites__WEBPACK_IMPORTED_MODULE_1__["SpriteAtlasLink"]({
                packedX: 0,
                packedY: 0,
                w: dims,
                h: dims,
                packOffsetX: 0,
                packOffsetY: 0,
                packedW: dims,
                packedH: dims,
                atlas: canvas,
            });
        });

        this.spriteNotFoundSprite = sprite;
    }
}

const Loader = new LoaderImpl();


/***/ }),

/***/ "./src/js/core/logging.js":
/*!********************************!*\
  !*** ./src/js/core/logging.js ***!
  \********************************/
/*! exports provided: createLogger, serializeError, stringifyObjectContainingErrors, globalDebug, globalLog, globalWarn, globalError, logSection */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createLogger", function() { return createLogger; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "serializeError", function() { return serializeError; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "stringifyObjectContainingErrors", function() { return stringifyObjectContainingErrors; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "globalDebug", function() { return globalDebug; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "globalLog", function() { return globalLog; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "globalWarn", function() { return globalWarn; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "globalError", function() { return globalError; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "logSection", function() { return logSection; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");

const circularJson = __webpack_require__(/*! circular-json */ "./node_modules/circular-json/build/circular-json.node.js");

/*
Logging functions
- To be extended
*/

/**
 * Base logger class
 */
class Logger {
    constructor(context) {
        this.context = context;
    }

    debug(...args) {
        globalDebug(this.context, ...args);
    }

    log(...args) {
        globalLog(this.context, ...args);
    }

    warn(...args) {
        globalWarn(this.context, ...args);
    }

    error(...args) {
        globalError(this.context, ...args);
    }
}

function createLogger(context) {
    return new Logger(context);
}

function prepareObjectForLogging(obj, maxDepth = 1) {
    if (!window.Sentry) {
        // Not required without sentry
        return obj;
    }

    if (typeof obj !== "object" && !Array.isArray(obj)) {
        return obj;
    }
    const result = {};
    for (const key in obj) {
        const val = obj[key];

        if (typeof val === "object") {
            if (maxDepth > 0) {
                result[key] = prepareObjectForLogging(val, maxDepth - 1);
            } else {
                result[key] = "[object]";
            }
        } else {
            result[key] = val;
        }
    }
    return result;
}

/**
 * Serializes an error
 * @param {Error|ErrorEvent} err
 */
function serializeError(err) {
    if (!err) {
        return null;
    }
    const result = {
        type: err.constructor.name,
    };

    if (err instanceof Error) {
        result.message = err.message;
        result.name = err.name;
        result.stack = err.stack;
        result.type = "{type.Error}";
    } else if (err instanceof ErrorEvent) {
        result.filename = err.filename;
        result.message = err.message;
        result.lineno = err.lineno;
        result.colno = err.colno;
        result.type = "{type.ErrorEvent}";

        if (err.error) {
            result.error = serializeError(err.error);
        } else {
            result.error = "{not-provided}";
        }
    } else {
        result.type = "{unkown-type:" + typeof err + "}";
    }

    return result;
}

/**
 * Serializes an event
 * @param {Event} event
 */
function serializeEvent(event) {
    let result = {
        type: "{type.Event:" + typeof event + "}",
    };
    result.eventType = event.type;
    return result;
}

/**
 * Prepares a json payload
 * @param {string} key
 * @param {any} value
 */
function preparePayload(key, value) {
    if (value instanceof Error || value instanceof ErrorEvent) {
        return serializeError(value);
    }
    if (value instanceof Event) {
        return serializeEvent(value);
    }
    if (typeof value === "undefined") {
        return null;
    }
    return value;
}

/**
 * Stringifies an object containing circular references and errors
 * @param {any} payload
 */
function stringifyObjectContainingErrors(payload) {
    return circularJson.stringify(payload, preparePayload);
}

function globalDebug(context, ...args) {
    if (false) {}
}

function globalLog(context, ...args) {
    // eslint-disable-next-line no-console
    logInternal(context, console.log, prepareArgsForLogging(args));
}

function globalWarn(context, ...args) {
    // eslint-disable-next-line no-console
    logInternal(context, console.warn, prepareArgsForLogging(args));
}

function globalError(context, ...args) {
    args = prepareArgsForLogging(args);
    // eslint-disable-next-line no-console
    logInternal(context, console.error, args);

    if (window.Sentry) {
        window.Sentry.withScope(scope => {
            scope.setExtra("args", args);
            window.Sentry.captureMessage(internalBuildStringFromArgs(args), "error");
        });
    }
}

function prepareArgsForLogging(args) {
    let result = [];
    for (let i = 0; i < args.length; ++i) {
        result.push(prepareObjectForLogging(args[i]));
    }
    return result;
}

/**
 * @param {Array<any>} args
 */
function internalBuildStringFromArgs(args) {
    let result = [];

    for (let i = 0; i < args.length; ++i) {
        let arg = args[i];
        if (
            typeof arg === "string" ||
            typeof arg === "number" ||
            typeof arg === "boolean" ||
            arg === null ||
            arg === undefined
        ) {
            result.push("" + arg);
        } else if (arg instanceof Error) {
            result.push(arg.message);
        } else {
            result.push("[object]");
        }
    }
    return result.join(" ");
}

function logSection(name, color) {
    while (name.length <= 14) {
        name = " " + name + " ";
    }
    name = name.padEnd(19, " ");

    const lineCss =
        "letter-spacing: -3px; color: " + color + "; font-size: 6px; background: #eee; color: #eee;";
    const line = "%c----------------------------";
    console.log("\n" + line + " %c" + name + " " + line + "\n", lineCss, "color: " + color, lineCss);
}

function extractHandleContext(handle) {
    let context = handle || "unknown";
    if (handle && handle.constructor && handle.constructor.name) {
        context = handle.constructor.name;
        if (context === "String") {
            context = handle;
        }
    }

    if (handle && handle.name) {
        context = handle.name;
    }
    return context + "";
}

function logInternal(handle, consoleMethod, args) {
    const context = extractHandleContext(handle).padEnd(20, " ");
    const labelColor = handle && handle.LOG_LABEL_COLOR ? handle.LOG_LABEL_COLOR : "#aaa";

    if (false) {} else {
        // if (G_IS_DEV && !globalConfig.debug.disableLoggingLogSources) {
        consoleMethod.call(console, "%c" + context, "color: " + labelColor, ...args);
        // } else {
        // consoleMethod.call(console, ...args);
        // }
    }
}


/***/ }),

/***/ "./src/js/core/lzstring.js":
/*!*********************************!*\
  !*** ./src/js/core/lzstring.js ***!
  \*********************************/
/*! exports provided: compressU8, compressU8WHeader, decompressU8WHeader, compressX64, decompressX64 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "compressU8", function() { return compressU8; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "compressU8WHeader", function() { return compressU8WHeader; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "decompressU8WHeader", function() { return decompressU8WHeader; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "compressX64", function() { return compressX64; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "decompressX64", function() { return decompressX64; });
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
// This work is free. You can redistribute it and/or modify it
// under the terms of the WTFPL, Version 2
// For more information see LICENSE.txt or http://www.wtfpl.net/
//
// For more information, the home page:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.4

const fromCharCode = String.fromCharCode;
const hasOwnProperty = Object.prototype.hasOwnProperty;

const keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
const baseReverseDic = {};

function getBaseValue(alphabet, character) {
    if (!baseReverseDic[alphabet]) {
        baseReverseDic[alphabet] = {};
        for (let i = 0; i < alphabet.length; i++) {
            baseReverseDic[alphabet][alphabet.charAt(i)] = i;
        }
    }
    return baseReverseDic[alphabet][character];
}

//compress into uint8array (UCS-2 big endian format)
function compressU8(uncompressed) {
    let compressed = compress(uncompressed);
    let buf = new Uint8Array(compressed.length * 2); // 2 bytes per character

    for (let i = 0, TotalLen = compressed.length; i < TotalLen; i++) {
        let current_value = compressed.charCodeAt(i);
        buf[i * 2] = current_value >>> 8;
        buf[i * 2 + 1] = current_value % 256;
    }
    return buf;
}

// Compreses with header
/**
 * @param {string} uncompressed
 * @param {number} header
 */
function compressU8WHeader(uncompressed, header) {
    let compressed = compress(uncompressed);
    let buf = new Uint8Array(2 + compressed.length * 2); // 2 bytes per character

    buf[0] = header >>> 8;
    buf[1] = header % 256;
    for (let i = 0, TotalLen = compressed.length; i < TotalLen; i++) {
        let current_value = compressed.charCodeAt(i);
        buf[2 + i * 2] = current_value >>> 8;
        buf[2 + i * 2 + 1] = current_value % 256;
    }
    return buf;
}

//decompress from uint8array (UCS-2 big endian format)
/**
 *
 * @param {Uint8Array} compressed
 */
function decompressU8WHeader(compressed) {
    // let buf = new Array(compressed.length / 2); // 2 bytes per character
    // for (let i = 0, TotalLen = buf.length; i < TotalLen; i++) {
    //     buf[i] = compressed[i * 2] * 256 + compressed[i * 2 + 1];
    // }

    // let result = [];
    // buf.forEach(function (c) {
    //     result.push(fromCharCode(c));
    // });
    let result = [];
    for (let i = 2, n = compressed.length; i < n; i += 2) {
        const code = compressed[i] * 256 + compressed[i + 1];
        result.push(fromCharCode(code));
    }
    return decompress(result.join(""));
}

//compress into a string that is already URI encoded
function compressX64(input) {
    if (input == null) return "";
    return _compress(input, 6, function (a) {
        return keyStrUriSafe.charAt(a);
    });
}

//decompress from an output of compressToEncodedURIComponent
function decompressX64(input) {
    if (input == null) return "";
    if (input == "") return null;
    input = input.replace(/ /g, "+");
    return _decompress(input.length, 32, function (index) {
        return getBaseValue(keyStrUriSafe, input.charAt(index));
    });
}

function compress(uncompressed) {
    return _compress(uncompressed, 16, function (a) {
        return fromCharCode(a);
    });
}

function _compress(uncompressed, bitsPerChar, getCharFromInt) {
    if (uncompressed == null) return "";
    let i,
        value,
        context_dictionary = {},
        context_dictionaryToCreate = {},
        context_c = "",
        context_wc = "",
        context_w = "",
        context_enlargeIn = 2, // Compensate for the first entry which should not count
        context_dictSize = 3,
        context_numBits = 2,
        context_data = [],
        context_data_val = 0,
        context_data_position = 0,
        ii;

    for (ii = 0; ii < uncompressed.length; ii += 1) {
        context_c = uncompressed.charAt(ii);
        if (!hasOwnProperty.call(context_dictionary, context_c)) {
            context_dictionary[context_c] = context_dictSize++;
            context_dictionaryToCreate[context_c] = true;
        }

        context_wc = context_w + context_c;
        if (hasOwnProperty.call(context_dictionary, context_wc)) {
            context_w = context_wc;
        } else {
            if (hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
                if (context_w.charCodeAt(0) < 256) {
                    for (i = 0; i < context_numBits; i++) {
                        context_data_val = context_data_val << 1;
                        if (context_data_position == bitsPerChar - 1) {
                            context_data_position = 0;
                            context_data.push(getCharFromInt(context_data_val));
                            context_data_val = 0;
                        } else {
                            context_data_position++;
                        }
                    }
                    value = context_w.charCodeAt(0);
                    for (i = 0; i < 8; i++) {
                        context_data_val = (context_data_val << 1) | (value & 1);
                        if (context_data_position == bitsPerChar - 1) {
                            context_data_position = 0;
                            context_data.push(getCharFromInt(context_data_val));
                            context_data_val = 0;
                        } else {
                            context_data_position++;
                        }
                        value = value >> 1;
                    }
                } else {
                    value = 1;
                    for (i = 0; i < context_numBits; i++) {
                        context_data_val = (context_data_val << 1) | value;
                        if (context_data_position == bitsPerChar - 1) {
                            context_data_position = 0;
                            context_data.push(getCharFromInt(context_data_val));
                            context_data_val = 0;
                        } else {
                            context_data_position++;
                        }
                        value = 0;
                    }
                    value = context_w.charCodeAt(0);
                    for (i = 0; i < 16; i++) {
                        context_data_val = (context_data_val << 1) | (value & 1);
                        if (context_data_position == bitsPerChar - 1) {
                            context_data_position = 0;
                            context_data.push(getCharFromInt(context_data_val));
                            context_data_val = 0;
                        } else {
                            context_data_position++;
                        }
                        value = value >> 1;
                    }
                }
                context_enlargeIn--;
                if (context_enlargeIn == 0) {
                    context_enlargeIn = Math.pow(2, context_numBits);
                    context_numBits++;
                }
                delete context_dictionaryToCreate[context_w];
            } else {
                value = context_dictionary[context_w];
                for (i = 0; i < context_numBits; i++) {
                    context_data_val = (context_data_val << 1) | (value & 1);
                    if (context_data_position == bitsPerChar - 1) {
                        context_data_position = 0;
                        context_data.push(getCharFromInt(context_data_val));
                        context_data_val = 0;
                    } else {
                        context_data_position++;
                    }
                    value = value >> 1;
                }
            }
            context_enlargeIn--;
            if (context_enlargeIn == 0) {
                context_enlargeIn = Math.pow(2, context_numBits);
                context_numBits++;
            }
            // Add wc to the dictionary.
            context_dictionary[context_wc] = context_dictSize++;
            context_w = String(context_c);
        }
    }

    // Output the code for w.
    if (context_w !== "") {
        if (hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
            if (context_w.charCodeAt(0) < 256) {
                for (i = 0; i < context_numBits; i++) {
                    context_data_val = context_data_val << 1;
                    if (context_data_position == bitsPerChar - 1) {
                        context_data_position = 0;
                        context_data.push(getCharFromInt(context_data_val));
                        context_data_val = 0;
                    } else {
                        context_data_position++;
                    }
                }
                value = context_w.charCodeAt(0);
                for (i = 0; i < 8; i++) {
                    context_data_val = (context_data_val << 1) | (value & 1);
                    if (context_data_position == bitsPerChar - 1) {
                        context_data_position = 0;
                        context_data.push(getCharFromInt(context_data_val));
                        context_data_val = 0;
                    } else {
                        context_data_position++;
                    }
                    value = value >> 1;
                }
            } else {
                value = 1;
                for (i = 0; i < context_numBits; i++) {
                    context_data_val = (context_data_val << 1) | value;
                    if (context_data_position == bitsPerChar - 1) {
                        context_data_position = 0;
                        context_data.push(getCharFromInt(context_data_val));
                        context_data_val = 0;
                    } else {
                        context_data_position++;
                    }
                    value = 0;
                }
                value = context_w.charCodeAt(0);
                for (i = 0; i < 16; i++) {
                    context_data_val = (context_data_val << 1) | (value & 1);
                    if (context_data_position == bitsPerChar - 1) {
                        context_data_position = 0;
                        context_data.push(getCharFromInt(context_data_val));
                        context_data_val = 0;
                    } else {
                        context_data_position++;
                    }
                    value = value >> 1;
                }
            }
            context_enlargeIn--;
            if (context_enlargeIn == 0) {
                context_enlargeIn = Math.pow(2, context_numBits);
                context_numBits++;
            }
            delete context_dictionaryToCreate[context_w];
        } else {
            value = context_dictionary[context_w];
            for (i = 0; i < context_numBits; i++) {
                context_data_val = (context_data_val << 1) | (value & 1);
                if (context_data_position == bitsPerChar - 1) {
                    context_data_position = 0;
                    context_data.push(getCharFromInt(context_data_val));
                    context_data_val = 0;
                } else {
                    context_data_position++;
                }
                value = value >> 1;
            }
        }
        context_enlargeIn--;
        if (context_enlargeIn == 0) {
            context_enlargeIn = Math.pow(2, context_numBits);
            context_numBits++;
        }
    }

    // Mark the end of the stream
    value = 2;
    for (i = 0; i < context_numBits; i++) {
        context_data_val = (context_data_val << 1) | (value & 1);
        if (context_data_position == bitsPerChar - 1) {
            context_data_position = 0;
            context_data.push(getCharFromInt(context_data_val));
            context_data_val = 0;
        } else {
            context_data_position++;
        }
        value = value >> 1;
    }

    // Flush the last char
    // eslint-disable-next-line no-constant-condition
    while (true) {
        context_data_val = context_data_val << 1;
        if (context_data_position == bitsPerChar - 1) {
            context_data.push(getCharFromInt(context_data_val));
            break;
        } else context_data_position++;
    }
    return context_data.join("");
}

function decompress(compressed) {
    if (compressed == null) return "";
    if (compressed == "") return null;
    return _decompress(compressed.length, 32768, function (index) {
        return compressed.charCodeAt(index);
    });
}

function _decompress(length, resetValue, getNextValue) {
    let dictionary = [],
        next,
        enlargeIn = 4,
        dictSize = 4,
        numBits = 3,
        entry = "",
        result = [],
        i,
        w,
        bits,
        resb,
        maxpower,
        power,
        c,
        data = { val: getNextValue(0), position: resetValue, index: 1 };

    for (i = 0; i < 3; i += 1) {
        dictionary[i] = i;
    }

    bits = 0;
    maxpower = Math.pow(2, 2);
    power = 1;
    while (power != maxpower) {
        resb = data.val & data.position;
        data.position >>= 1;
        if (data.position == 0) {
            data.position = resetValue;
            data.val = getNextValue(data.index++);
        }
        bits |= (resb > 0 ? 1 : 0) * power;
        power <<= 1;
    }

    switch ((next = bits)) {
        case 0:
            bits = 0;
            maxpower = Math.pow(2, 8);
            power = 1;
            while (power != maxpower) {
                resb = data.val & data.position;
                data.position >>= 1;
                if (data.position == 0) {
                    data.position = resetValue;
                    data.val = getNextValue(data.index++);
                }
                bits |= (resb > 0 ? 1 : 0) * power;
                power <<= 1;
            }
            c = fromCharCode(bits);
            break;
        case 1:
            bits = 0;
            maxpower = Math.pow(2, 16);
            power = 1;
            while (power != maxpower) {
                resb = data.val & data.position;
                data.position >>= 1;
                if (data.position == 0) {
                    data.position = resetValue;
                    data.val = getNextValue(data.index++);
                }
                bits |= (resb > 0 ? 1 : 0) * power;
                power <<= 1;
            }
            c = fromCharCode(bits);
            break;
        case 2:
            return "";
    }
    dictionary[3] = c;
    w = c;
    result.push(c);

    // eslint-disable-next-line no-constant-condition
    while (true) {
        if (data.index > length) {
            return "";
        }

        bits = 0;
        maxpower = Math.pow(2, numBits);
        power = 1;
        while (power != maxpower) {
            resb = data.val & data.position;
            data.position >>= 1;
            if (data.position == 0) {
                data.position = resetValue;
                data.val = getNextValue(data.index++);
            }
            bits |= (resb > 0 ? 1 : 0) * power;
            power <<= 1;
        }

        switch ((c = bits)) {
            case 0:
                bits = 0;
                maxpower = Math.pow(2, 8);
                power = 1;
                while (power != maxpower) {
                    resb = data.val & data.position;
                    data.position >>= 1;
                    if (data.position == 0) {
                        data.position = resetValue;
                        data.val = getNextValue(data.index++);
                    }
                    bits |= (resb > 0 ? 1 : 0) * power;
                    power <<= 1;
                }

                dictionary[dictSize++] = fromCharCode(bits);
                c = dictSize - 1;
                enlargeIn--;
                break;
            case 1:
                bits = 0;
                maxpower = Math.pow(2, 16);
                power = 1;
                while (power != maxpower) {
                    resb = data.val & data.position;
                    data.position >>= 1;
                    if (data.position == 0) {
                        data.position = resetValue;
                        data.val = getNextValue(data.index++);
                    }
                    bits |= (resb > 0 ? 1 : 0) * power;
                    power <<= 1;
                }
                dictionary[dictSize++] = fromCharCode(bits);
                c = dictSize - 1;
                enlargeIn--;
                break;
            case 2:
                return result.join("");
        }

        if (enlargeIn == 0) {
            enlargeIn = Math.pow(2, numBits);
            numBits++;
        }

        if (dictionary[c]) {
            // @ts-ignore
            entry = dictionary[c];
        } else {
            if (c === dictSize) {
                entry = w + w.charAt(0);
            } else {
                return null;
            }
        }
        result.push(entry);

        // Add w+entry[0] to the dictionary.
        dictionary[dictSize++] = w + entry.charAt(0);
        enlargeIn--;

        w = entry;

        if (enlargeIn == 0) {
            enlargeIn = Math.pow(2, numBits);
            numBits++;
        }
    }
}


/***/ }),

/***/ "./src/js/core/modal_dialog_elements.js":
/*!**********************************************!*\
  !*** ./src/js/core/modal_dialog_elements.js ***!
  \**********************************************/
/*! exports provided: Dialog, DialogLoading, DialogOptionChooser, DialogWithForm */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Dialog", function() { return Dialog; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DialogLoading", function() { return DialogLoading; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DialogOptionChooser", function() { return DialogOptionChooser; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DialogWithForm", function() { return DialogWithForm; });
/* harmony import */ var _signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./signal */ "./src/js/core/signal.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
/* harmony import */ var _click_detector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _input_receiver__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _modal_dialog_forms__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./modal_dialog_forms */ "./src/js/core/modal_dialog_forms.js");
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../game/key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");













/*
 * ***************************************************
 *
 *  LEGACY CODE WARNING
 *
 *  This is old code from yorg3.io and needs to be refactored
 *  @TODO
 *
 * ***************************************************
 */

const kbEnter = 13;
const kbCancel = 27;

const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_8__["createLogger"])("dialogs");

/**
 * Basic text based dialog
 */
class Dialog {
    /**
     *
     * Constructs a new dialog with the given options
     * @param {object} param0
     * @param {Application} param0.app
     * @param {string} param0.title Title of the dialog
     * @param {string} param0.contentHTML Inner dialog html
     * @param {Array<string>} param0.buttons
     *  Button list, each button contains of up to 3 parts separated by ':'.
     *  Part 0: The id, one of the one defined in dialog_buttons.yaml
     *  Part 1: The style, either good, bad or misc
     *  Part 2 (optional): Additional parameters separated by '/', available are:
     *    timeout: This button is only available after some waiting time
     *    kb_enter: This button is triggered by the enter key
     *    kb_escape This button is triggered by the escape key
     * @param {string=} param0.type The dialog type, either "info" or "warn"
     * @param {boolean=} param0.closeButton Whether this dialog has a close button
     */
    constructor({ app, title, contentHTML, buttons, type = "info", closeButton = false }) {
        this.app = app;
        this.title = title;
        this.contentHTML = contentHTML;
        this.type = type;
        this.buttonIds = buttons;
        this.closeButton = closeButton;

        this.closeRequested = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
        this.buttonSignals = {};

        for (let i = 0; i < buttons.length; ++i) {
            if (false) {}

            const buttonId = this.buttonIds[i].split(":")[0];
            this.buttonSignals[buttonId] = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
        }

        this.valueChosen = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();

        this.timeouts = [];
        this.clickDetectors = [];

        this.inputReciever = new _input_receiver__WEBPACK_IMPORTED_MODULE_4__["InputReceiver"]("dialog-" + this.title);

        this.inputReciever.keydown.add(this.handleKeydown, this);

        this.enterHandler = null;
        this.escapeHandler = null;
    }

    /**
     * Internal keydown handler
     * @param {object} param0
     * @param {number} param0.keyCode
     * @param {boolean} param0.shift
     * @param {boolean} param0.alt
     */
    handleKeydown({ keyCode, shift, alt }) {
        if (keyCode === kbEnter && this.enterHandler) {
            this.internalButtonHandler(this.enterHandler);
            return _signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
        }

        if (keyCode === kbCancel && this.escapeHandler) {
            this.internalButtonHandler(this.escapeHandler);
            return _signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
        }
    }

    internalButtonHandler(id, ...payload) {
        this.app.inputMgr.popReciever(this.inputReciever);

        if (id !== "close-button") {
            this.buttonSignals[id].dispatch(...payload);
        }
        this.closeRequested.dispatch();
    }

    createElement() {
        const elem = document.createElement("div");
        elem.classList.add("ingameDialog");

        this.dialogElem = document.createElement("div");
        this.dialogElem.classList.add("dialogInner");

        if (this.type) {
            this.dialogElem.classList.add(this.type);
        }
        elem.appendChild(this.dialogElem);

        const title = document.createElement("h1");
        title.innerText = this.title;
        title.classList.add("title");
        this.dialogElem.appendChild(title);

        if (this.closeButton) {
            this.dialogElem.classList.add("hasCloseButton");

            const closeBtn = document.createElement("button");
            closeBtn.classList.add("closeButton");

            this.trackClicks(closeBtn, () => this.internalButtonHandler("close-button"), {
                applyCssClass: "pressedSmallElement",
            });

            title.appendChild(closeBtn);
            this.inputReciever.backButton.add(() => this.internalButtonHandler("close-button"));
        }

        const content = document.createElement("div");
        content.classList.add("content");
        content.innerHTML = this.contentHTML;
        this.dialogElem.appendChild(content);

        if (this.buttonIds.length > 0) {
            const buttons = document.createElement("div");
            buttons.classList.add("buttons");

            // Create buttons
            for (let i = 0; i < this.buttonIds.length; ++i) {
                const [buttonId, buttonStyle, rawParams] = this.buttonIds[i].split(":");

                const button = document.createElement("button");
                button.classList.add("button");
                button.classList.add("styledButton");
                button.classList.add(buttonStyle);
                button.innerText = _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.buttons[buttonId];

                const params = (rawParams || "").split("/");
                const useTimeout = params.indexOf("timeout") >= 0;

                const isEnter = params.indexOf("enter") >= 0;
                const isEscape = params.indexOf("escape") >= 0;

                if (isEscape && this.closeButton) {
                    logger.warn("Showing dialog with close button, and additional cancel button");
                }

                if (useTimeout) {
                    button.classList.add("timedButton");
                    const timeout = setTimeout(() => {
                        button.classList.remove("timedButton");
                        Object(_utils__WEBPACK_IMPORTED_MODULE_1__["arrayDeleteValue"])(this.timeouts, timeout);
                    }, 3000);
                    this.timeouts.push(timeout);
                }
                if (isEnter || isEscape) {
                    // if (this.app.settings.getShowKeyboardShortcuts()) {
                    // Show keybinding
                    const spacer = document.createElement("code");
                    spacer.classList.add("keybinding");
                    spacer.innerHTML = Object(_game_key_action_mapper__WEBPACK_IMPORTED_MODULE_7__["getStringForKeyCode"])(isEnter ? kbEnter : kbCancel);
                    button.appendChild(spacer);
                    // }

                    if (isEnter) {
                        this.enterHandler = buttonId;
                    }
                    if (isEscape) {
                        this.escapeHandler = buttonId;
                    }
                }

                this.trackClicks(button, () => this.internalButtonHandler(buttonId));
                buttons.appendChild(button);
            }

            this.dialogElem.appendChild(buttons);
        } else {
            this.dialogElem.classList.add("buttonless");
        }

        this.element = elem;
        this.app.inputMgr.pushReciever(this.inputReciever);

        return this.element;
    }

    setIndex(index) {
        this.element.style.zIndex = index;
    }

    destroy() {
        if (!this.element) {
            window.assert(false, "Tried to destroy dialog twice");
            return;
        }
        // We need to do this here, because if the backbutton event gets
        // dispatched to the modal dialogs, it will not call the internalButtonHandler,
        // and thus our receiver stays attached the whole time
        this.app.inputMgr.destroyReceiver(this.inputReciever);

        for (let i = 0; i < this.clickDetectors.length; ++i) {
            this.clickDetectors[i].cleanup();
        }
        this.clickDetectors = [];

        this.element.remove();
        this.element = null;

        for (let i = 0; i < this.timeouts.length; ++i) {
            clearTimeout(this.timeouts[i]);
        }
        this.timeouts = [];
    }

    hide() {
        this.element.classList.remove("visible");
    }

    show() {
        this.element.classList.add("visible");
    }

    /**
     * Helper method to track clicks on an element
     * @param {Element} elem
     * @param {function():void} handler
     * @param {import("./click_detector").ClickDetectorConstructorArgs=} args
     * @returns {ClickDetector}
     */
    trackClicks(elem, handler, args = {}) {
        const detector = new _click_detector__WEBPACK_IMPORTED_MODULE_2__["ClickDetector"](elem, args);
        detector.click.add(handler, this);
        this.clickDetectors.push(detector);
        return detector;
    }
}

/**
 * Dialog which simply shows a loading spinner
 */
class DialogLoading extends Dialog {
    constructor(app) {
        super({
            app,
            title: "",
            contentHTML: "",
            buttons: [],
            type: "loading",
        });

        // Loading dialog can not get closed with back button
        this.inputReciever.backButton.removeAll();
        this.inputReciever.context = "dialog-loading";
    }

    createElement() {
        const elem = document.createElement("div");
        elem.classList.add("ingameDialog");
        elem.classList.add("loadingDialog");
        this.element = elem;

        const loader = document.createElement("div");
        loader.classList.add("prefab_LoadingTextWithAnim");
        loader.classList.add("loadingIndicator");
        elem.appendChild(loader);

        this.app.inputMgr.pushReciever(this.inputReciever);

        return elem;
    }
}

class DialogOptionChooser extends Dialog {
    constructor({ app, title, options }) {
        let html = "<div class='optionParent'>";

        options.options.forEach(({ value, text, desc = null, iconPrefix = null }) => {
            const descHtml = desc ? `<span class="desc">${desc}</span>` : "";
            let iconHtml = iconPrefix ? `<span class="icon icon-${iconPrefix}-${value}"></span>` : "";
            html += `
                <div class='option ${value === options.active ? "active" : ""} ${
                iconPrefix ? "hasIcon" : ""
            }' data-optionvalue='${value}'>
                    ${iconHtml}    
                    <span class='title'>${text}</span>
                    ${descHtml}
                </div>
                `;
        });

        html += "</div>";
        super({
            app,
            title,
            contentHTML: html,
            buttons: [],
            type: "info",
            closeButton: true,
        });

        this.options = options;
        this.initialOption = options.active;

        this.buttonSignals.optionSelected = new _signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
    }

    createElement() {
        const div = super.createElement();
        this.dialogElem.classList.add("optionChooserDialog");

        div.querySelectorAll("[data-optionvalue]").forEach(handle => {
            const value = handle.getAttribute("data-optionvalue");
            if (!handle) {
                logger.error("Failed to bind option value in dialog:", value);
                return;
            }
            // Need click detector here to forward elements, otherwise scrolling does not work
            const detector = new _click_detector__WEBPACK_IMPORTED_MODULE_2__["ClickDetector"](handle, {
                consumeEvents: false,
                preventDefault: false,
                clickSound: null,
                applyCssClass: "pressedOption",
                targetOnly: true,
            });
            this.clickDetectors.push(detector);

            if (value !== this.initialOption) {
                detector.click.add(() => {
                    const selected = div.querySelector(".option.active");
                    if (selected) {
                        selected.classList.remove("active");
                    } else {
                        logger.warn("No selected option");
                    }
                    handle.classList.add("active");
                    this.app.sound.playUiSound(_platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].uiClick);
                    this.internalButtonHandler("optionSelected", value);
                });
            }
        });
        return div;
    }
}

class DialogWithForm extends Dialog {
    /**
     *
     * @param {object} param0
     * @param {Application} param0.app
     * @param {string} param0.title
     * @param {string} param0.desc
     * @param {array=} param0.buttons
     * @param {string=} param0.confirmButtonId
     * @param {string=} param0.extraButton
     * @param {boolean=} param0.closeButton
     * @param {Array<FormElement>} param0.formElements
     */
    constructor({
        app,
        title,
        desc,
        formElements,
        buttons = ["cancel", "ok:good"],
        confirmButtonId = "ok",
        closeButton = true,
    }) {
        let html = "";
        html += desc + "<br>";
        for (let i = 0; i < formElements.length; ++i) {
            html += formElements[i].getHtml();
        }

        super({
            app,
            title: title,
            contentHTML: html,
            buttons: buttons,
            type: "info",
            closeButton,
        });
        this.confirmButtonId = confirmButtonId;
        this.formElements = formElements;

        this.enterHandler = confirmButtonId;
    }

    internalButtonHandler(id, ...payload) {
        if (id === this.confirmButtonId) {
            if (this.hasAnyInvalid()) {
                this.dialogElem.classList.remove("errorShake");
                Object(_utils__WEBPACK_IMPORTED_MODULE_1__["waitNextFrame"])().then(() => {
                    if (this.dialogElem) {
                        this.dialogElem.classList.add("errorShake");
                    }
                });
                this.app.sound.playUiSound(_platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].uiError);
                return;
            }
        }

        super.internalButtonHandler(id, payload);
    }

    hasAnyInvalid() {
        for (let i = 0; i < this.formElements.length; ++i) {
            if (!this.formElements[i].isValid()) {
                return true;
            }
        }
        return false;
    }

    createElement() {
        const div = super.createElement();

        for (let i = 0; i < this.formElements.length; ++i) {
            const elem = this.formElements[i];
            elem.bindEvents(div, this.clickDetectors);
            elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested);
            elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen);
        }

        Object(_utils__WEBPACK_IMPORTED_MODULE_1__["waitNextFrame"])().then(() => {
            this.formElements[this.formElements.length - 1].focus();
        });

        return div;
    }
}


/***/ }),

/***/ "./src/js/core/modal_dialog_forms.js":
/*!*******************************************!*\
  !*** ./src/js/core/modal_dialog_forms.js ***!
  \*******************************************/
/*! exports provided: FormElement, FormElementInput, FormElementCheckbox, FormElementItemChooser */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FormElement", function() { return FormElement; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FormElementInput", function() { return FormElementInput; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FormElementCheckbox", function() { return FormElementCheckbox; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FormElementItemChooser", function() { return FormElementItemChooser; });
/* harmony import */ var _game_base_item__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game/base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _click_detector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./signal */ "./src/js/core/signal.js");




/*
 * ***************************************************
 *
 *  LEGACY CODE WARNING
 *
 *  This is old code from yorg3.io and needs to be refactored
 *  @TODO
 *
 * ***************************************************
 */

class FormElement {
    constructor(id, label) {
        this.id = id;
        this.label = label;

        this.valueChosen = new _signal__WEBPACK_IMPORTED_MODULE_2__["Signal"]();
    }

    getHtml() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "";
    }

    getFormElement(parent) {
        return parent.querySelector("[data-formId='" + this.id + "']");
    }

    bindEvents(parent, clickTrackers) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    focus() {}

    isValid() {
        return true;
    }

    /** @returns {any} */
    getValue() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }
}

class FormElementInput extends FormElement {
    constructor({ id, label = null, placeholder, defaultValue = "", inputType = "text", validator = null }) {
        super(id, label);
        this.placeholder = placeholder;
        this.defaultValue = defaultValue;
        this.inputType = inputType;
        this.validator = validator;

        this.element = null;
    }

    getHtml() {
        let classes = [];
        let inputType = "text";
        let maxlength = 256;
        switch (this.inputType) {
            case "text": {
                classes.push("input-text");
                break;
            }

            case "email": {
                classes.push("input-email");
                inputType = "email";
                break;
            }

            case "token": {
                classes.push("input-token");
                inputType = "text";
                maxlength = 4;
                break;
            }
        }

        return `
            <div class="formElement input">
                ${this.label ? `<label>${this.label}</label>` : ""}
                <input
                    type="${inputType}"
                    value="${this.defaultValue.replace(/["\\]+/gi, "")}"
                    maxlength="${maxlength}"
                    autocomplete="off"
                    autocorrect="off"
                    autocapitalize="off"
                    spellcheck="false"
                    class="${classes.join(" ")}"
                    placeholder="${this.placeholder.replace(/["\\]+/gi, "")}"
                    data-formId="${this.id}">
            </div>
        `;
    }

    bindEvents(parent, clickTrackers) {
        this.element = this.getFormElement(parent);
        this.element.addEventListener("input", event => this.updateErrorState());
        this.updateErrorState();
    }

    updateErrorState() {
        this.element.classList.toggle("errored", !this.isValid());
    }

    isValid() {
        return !this.validator || this.validator(this.element.value);
    }

    getValue() {
        return this.element.value;
    }

    focus() {
        this.element.focus();
    }
}

class FormElementCheckbox extends FormElement {
    constructor({ id, label, defaultValue = true }) {
        super(id, label);
        this.defaultValue = defaultValue;
        this.value = this.defaultValue;

        this.element = null;
    }

    getHtml() {
        return `
            <div class="formElement checkBoxFormElem">
            ${this.label ? `<label>${this.label}</label>` : ""}
                <div class="checkbox ${this.defaultValue ? "checked" : ""}" data-formId='${this.id}'>
                    <span class="knob"></span >
                </div >
            </div>
        `;
    }

    bindEvents(parent, clickTrackers) {
        this.element = this.getFormElement(parent);
        const detector = new _click_detector__WEBPACK_IMPORTED_MODULE_1__["ClickDetector"](this.element, {
            consumeEvents: false,
            preventDefault: false,
        });
        clickTrackers.push(detector);
        detector.click.add(this.toggle, this);
    }

    getValue() {
        return this.value;
    }

    toggle() {
        this.value = !this.value;
        this.element.classList.toggle("checked", this.value);
    }

    focus(parent) {}
}

class FormElementItemChooser extends FormElement {
    /**
     *
     * @param {object} param0
     * @param {string} param0.id
     * @param {string=} param0.label
     * @param {Array<BaseItem>} param0.items
     */
    constructor({ id, label, items = [] }) {
        super(id, label);
        this.items = items;
        this.element = null;

        /**
         * @type {BaseItem}
         */
        this.chosenItem = null;
    }

    getHtml() {
        let classes = [];

        return `
            <div class="formElement">
                ${this.label ? `<label>${this.label}</label>` : ""}
                <div class="ingameItemChooser input" data-formId="${this.id}"></div>
            </div>
            `;
    }

    /**
     * @param {HTMLElement} parent
     * @param {Array<ClickDetector>} clickTrackers
     */
    bindEvents(parent, clickTrackers) {
        this.element = this.getFormElement(parent);

        for (let i = 0; i < this.items.length; ++i) {
            const item = this.items[i];

            const canvas = document.createElement("canvas");
            canvas.width = 128;
            canvas.height = 128;
            const context = canvas.getContext("2d");
            item.drawFullSizeOnCanvas(context, 128);
            this.element.appendChild(canvas);

            const detector = new _click_detector__WEBPACK_IMPORTED_MODULE_1__["ClickDetector"](canvas, {});
            clickTrackers.push(detector);
            detector.click.add(() => {
                this.chosenItem = item;
                this.valueChosen.dispatch(item);
            });
        }
    }

    isValid() {
        return true;
    }

    getValue() {
        return null;
    }

    focus() {}
}


/***/ }),

/***/ "./src/js/core/polyfills.js":
/*!**********************************!*\
  !*** ./src/js/core/polyfills.js ***!
  \**********************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var whatwg_fetch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! whatwg-fetch */ "./node_modules/whatwg-fetch/fetch.js");
function mathPolyfills() {
    // Converts from degrees to radians.
    Math.radians = function (degrees) {
        return (degrees * Math.PI) / 180.0;
    };

    // Converts from radians to degrees.
    Math.degrees = function (radians) {
        return (radians * 180.0) / Math.PI;
    };
}

function stringPolyfills() {
    // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
    if (!String.prototype.padStart) {
        String.prototype.padStart = function padStart(targetLength, padString) {
            targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
            padString = String(typeof padString !== "undefined" ? padString : " ");
            if (this.length >= targetLength) {
                return String(this);
            } else {
                targetLength = targetLength - this.length;
                if (targetLength > padString.length) {
                    padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
                }
                return padString.slice(0, targetLength) + String(this);
            }
        };
    }

    // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
    if (!String.prototype.padEnd) {
        String.prototype.padEnd = function padEnd(targetLength, padString) {
            targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
            padString = String(typeof padString !== "undefined" ? padString : " ");
            if (this.length > targetLength) {
                return String(this);
            } else {
                targetLength = targetLength - this.length;
                if (targetLength > padString.length) {
                    padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
                }
                return String(this) + padString.slice(0, targetLength);
            }
        };
    }
}

function objectPolyfills() {
    // https://github.com/tc39/proposal-object-values-entries/blob/master/polyfill.js

    // @ts-ignore
    const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
    // @ts-ignore
    const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
    // @ts-ignore
    const concat = Function.bind.call(Function.call, Array.prototype.concat);
    const keys = Reflect.ownKeys;

    // @ts-ignore
    if (!Object.values) {
        // @ts-ignore
        Object.values = function values(O) {
            return reduce(
                keys(O),
                (v, k) => concat(v, typeof k === "string" && isEnumerable(O, k) ? [O[k]] : []),
                []
            );
        };
    }

    if (!Object.entries) {
        // @ts-ignore
        Object.entries = function entries(O) {
            return reduce(
                keys(O),
                (e, k) => concat(e, typeof k === "string" && isEnumerable(O, k) ? [[k, O[k]]] : []),
                []
            );
        };
    }
}

function initPolyfills() {
    mathPolyfills();
    stringPolyfills();
    objectPolyfills();
}

function initExtensions() {
    String.prototype.replaceAll = function (search, replacement) {
        var target = this;
        return target.split(search).join(replacement);
    };
}

// Fetch polyfill

// Other polyfills
initPolyfills();
initExtensions();


/***/ }),

/***/ "./src/js/core/query_parameters.js":
/*!*****************************************!*\
  !*** ./src/js/core/query_parameters.js ***!
  \*****************************************/
/*! exports provided: queryParamOptions */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "queryParamOptions", function() { return queryParamOptions; });
const queryString = __webpack_require__(/*! query-string */ "./node_modules/query-string/index.js");
const options = queryString.parse(location.search);

let queryParamOptions = {
    embedProvider: null,
    fullVersion: false,
    sandboxMode: false,
};

if (options.embed) {
    queryParamOptions.embedProvider = options.embed;
}

// Allow testing full version outside of standalone
if (options.fullVersion && !true) {
    queryParamOptions.fullVersion = true;
}

// Allow testing full version outside of standalone
if (options.sandboxMode && !true) {
    queryParamOptions.sandboxMode = true;
}


/***/ }),

/***/ "./src/js/core/read_write_proxy.js":
/*!*****************************************!*\
  !*** ./src/js/core/read_write_proxy.js ***!
  \*****************************************/
/*! exports provided: ReadWriteProxy */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReadWriteProxy", function() { return ReadWriteProxy; });
/* harmony import */ var _sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sensitive_utils.encrypt */ "./src/js/core/sensitive_utils.encrypt.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _platform_storage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../platform/storage */ "./src/js/platform/storage.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _explained_result__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./explained_result */ "./src/js/core/explained_result.js");
/* harmony import */ var _lzstring__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./lzstring */ "./src/js/core/lzstring.js");
/* harmony import */ var _async_compression__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./async_compression */ "./src/js/core/async_compression.js");
/* harmony import */ var _savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../savegame/savegame_compressor */ "./src/js/savegame/savegame_compressor.js");












const debounce = __webpack_require__(/*! debounce-promise */ "./node_modules/debounce-promise/dist/index.js");

const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("read_write_proxy");

const salt = Object(_utils__WEBPACK_IMPORTED_MODULE_3__["accessNestedPropertyReverse"])(_config__WEBPACK_IMPORTED_MODULE_4__["globalConfig"], ["file", "info"]);

// Helper which only writes / reads if verify() works. Also performs migration
class ReadWriteProxy {
    constructor(app, filename) {
        /** @type {Application} */
        this.app = app;

        this.filename = filename;

        /** @type {object} */
        this.currentData = null;

        // TODO: EXTREMELY HACKY! To verify we need to do this a step later
        if (false) {}

        /**
         * Store a debounced handler to prevent double writes
         */
        this.debouncedWrite = debounce(this.doWriteAsync.bind(this), 50);
    }

    // -- Methods to override

    /** @returns {ExplainedResult} */
    verify(data) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return _explained_result__WEBPACK_IMPORTED_MODULE_5__["ExplainedResult"].bad();
    }

    // Should return the default data
    getDefaultData() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return {};
    }

    // Should return the current version as an integer
    getCurrentVersion() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return 0;
    }

    // Should migrate the data (Modify in place)
    /** @returns {ExplainedResult} */
    migrate(data) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return _explained_result__WEBPACK_IMPORTED_MODULE_5__["ExplainedResult"].bad();
    }

    // -- / Methods

    // Resets whole data, returns promise
    resetEverythingAsync() {
        logger.warn("Reset data to default");
        this.currentData = this.getDefaultData();
        return this.writeAsync();
    }

    /**
     *
     * @param {object} obj
     */
    static serializeObject(obj) {
        const jsonString = JSON.stringify(Object(_savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_8__["compressObject"])(obj));
        const checksum = Object(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["computeCrc"])(jsonString + salt);
        return _async_compression__WEBPACK_IMPORTED_MODULE_7__["compressionPrefix"] + Object(_lzstring__WEBPACK_IMPORTED_MODULE_6__["compressX64"])(checksum + jsonString);
    }

    /**
     *
     * @param {object} text
     */
    static deserializeObject(text) {
        const decompressed = Object(_lzstring__WEBPACK_IMPORTED_MODULE_6__["decompressX64"])(text.substr(_async_compression__WEBPACK_IMPORTED_MODULE_7__["compressionPrefix"].length));
        if (!decompressed) {
            // LZ string decompression failure
            throw new Error("bad-content / decompression-failed");
        }
        if (decompressed.length < 40) {
            // String too short
            throw new Error("bad-content / payload-too-small");
        }

        // Compare stored checksum with actual checksum
        const checksum = decompressed.substring(0, 40);
        const jsonString = decompressed.substr(40);

        const desiredChecksum = checksum.startsWith(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["CRC_PREFIX"])
            ? Object(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["computeCrc"])(jsonString + salt)
            : Object(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["sha1"])(jsonString + salt);

        if (desiredChecksum !== checksum) {
            // Checksum mismatch
            throw new Error("bad-content / checksum-mismatch");
        }

        const parsed = JSON.parse(jsonString);
        const decoded = Object(_savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_8__["decompressObject"])(parsed);
        return decoded;
    }

    /**
     * Writes the data asychronously, fails if verify() fails.
     * Debounces the operation by up to 50ms
     * @returns {Promise<void>}
     */
    writeAsync() {
        const verifyResult = this.internalVerifyEntry(this.currentData);

        if (!verifyResult.result) {
            logger.error("Tried to write invalid data to", this.filename, "reason:", verifyResult.reason);
            return Promise.reject(verifyResult.reason);
        }

        return this.debouncedWrite();
    }

    /**
     * Actually writes the data asychronously
     * @returns {Promise<void>}
     */
    doWriteAsync() {
        return _async_compression__WEBPACK_IMPORTED_MODULE_7__["asyncCompressor"]
            .compressObjectAsync(this.currentData)
            .then(compressed => {
                return this.app.storage.writeFileAsync(this.filename, compressed);
            })
            .then(() => {
                logger.log("📄 Wrote", this.filename);
            })
            .catch(err => {
                logger.error("Failed to write", this.filename, ":", err);
                throw err;
            });
    }

    // Reads the data asynchronously, fails if verify() fails
    readAsync() {
        // Start read request
        return (
            this.app.storage
                .readFileAsync(this.filename)

                // Check for errors during read
                .catch(err => {
                    if (err === _platform_storage__WEBPACK_IMPORTED_MODULE_2__["FILE_NOT_FOUND"]) {
                        logger.log("File not found, using default data");

                        // File not found or unreadable, assume default file
                        return Promise.resolve(null);
                    }

                    return Promise.reject("file-error: " + err);
                })

                // Decrypt data (if its encrypted)
                // @ts-ignore
                .then(rawData => {
                    if (rawData == null) {
                        // So, the file has not been found, use default data
                        return JSON.stringify(Object(_savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_8__["compressObject"])(this.getDefaultData()));
                    }

                    if (rawData.startsWith(_async_compression__WEBPACK_IMPORTED_MODULE_7__["compressionPrefix"])) {
                        const decompressed = Object(_lzstring__WEBPACK_IMPORTED_MODULE_6__["decompressX64"])(rawData.substr(_async_compression__WEBPACK_IMPORTED_MODULE_7__["compressionPrefix"].length));
                        if (!decompressed) {
                            // LZ string decompression failure
                            return Promise.reject("bad-content / decompression-failed");
                        }
                        if (decompressed.length < 40) {
                            // String too short
                            return Promise.reject("bad-content / payload-too-small");
                        }

                        // Compare stored checksum with actual checksum
                        const checksum = decompressed.substring(0, 40);
                        const jsonString = decompressed.substr(40);

                        const desiredChecksum = checksum.startsWith(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["CRC_PREFIX"])
                            ? Object(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["computeCrc"])(jsonString + salt)
                            : Object(_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_0__["sha1"])(jsonString + salt);

                        if (desiredChecksum !== checksum) {
                            // Checksum mismatch
                            return Promise.reject(
                                "bad-content / checksum-mismatch: " + desiredChecksum + " vs " + checksum
                            );
                        }
                        return jsonString;
                    } else {
                        if (true) {
                            return Promise.reject("bad-content / missing-compression");
                        }
                    }
                    return rawData;
                })

                // Parse JSON, this could throw but that's fine
                .then(res => {
                    try {
                        return JSON.parse(res);
                    } catch (ex) {
                        logger.error(
                            "Failed to parse file content of",
                            this.filename,
                            ":",
                            ex,
                            "(content was:",
                            res,
                            ")"
                        );
                        throw new Error("invalid-serialized-data");
                    }
                })

                // Decompress
                .then(compressed => Object(_savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_8__["decompressObject"])(compressed))

                // Verify basic structure
                .then(contents => {
                    const result = this.internalVerifyBasicStructure(contents);
                    if (!result.isGood()) {
                        return Promise.reject("verify-failed: " + result.reason);
                    }
                    return contents;
                })

                // Check version and migrate if required
                .then(contents => {
                    if (contents.version > this.getCurrentVersion()) {
                        return Promise.reject("stored-data-is-newer");
                    }

                    if (contents.version < this.getCurrentVersion()) {
                        logger.log(
                            "Trying to migrate data object from version",
                            contents.version,
                            "to",
                            this.getCurrentVersion()
                        );
                        const migrationResult = this.migrate(contents); // modify in place
                        if (migrationResult.isBad()) {
                            return Promise.reject("migration-failed: " + migrationResult.reason);
                        }
                    }
                    return contents;
                })

                // Verify
                .then(contents => {
                    const verifyResult = this.internalVerifyEntry(contents);
                    if (!verifyResult.result) {
                        logger.error(
                            "Read invalid data from",
                            this.filename,
                            "reason:",
                            verifyResult.reason,
                            "contents:",
                            contents
                        );
                        return Promise.reject("invalid-data: " + verifyResult.reason);
                    }
                    return contents;
                })

                // Store
                .then(contents => {
                    this.currentData = contents;
                    logger.log("📄 Read data with version", this.currentData.version, "from", this.filename);
                    return contents;
                })

                // Catchall
                .catch(err => {
                    return Promise.reject("Failed to read " + this.filename + ": " + err);
                })
        );
    }

    /**
     * Deletes the file
     * @returns {Promise<void>}
     */
    deleteAsync() {
        return this.app.storage.deleteFileAsync(this.filename);
    }

    // Internal

    /** @returns {ExplainedResult} */
    internalVerifyBasicStructure(data) {
        if (!data) {
            return _explained_result__WEBPACK_IMPORTED_MODULE_5__["ExplainedResult"].bad("Data is empty");
        }
        if (!Number.isInteger(data.version) || data.version < 0) {
            return _explained_result__WEBPACK_IMPORTED_MODULE_5__["ExplainedResult"].bad(
                `Data has invalid version: ${data.version} (expected ${this.getCurrentVersion()})`
            );
        }

        return _explained_result__WEBPACK_IMPORTED_MODULE_5__["ExplainedResult"].good();
    }

    /** @returns {ExplainedResult} */
    internalVerifyEntry(data) {
        if (data.version !== this.getCurrentVersion()) {
            return _explained_result__WEBPACK_IMPORTED_MODULE_5__["ExplainedResult"].bad(
                "Version mismatch, got " + data.version + " and expected " + this.getCurrentVersion()
            );
        }

        const verifyStructureError = this.internalVerifyBasicStructure(data);
        if (!verifyStructureError.isGood()) {
            return verifyStructureError;
        }
        return this.verify(data);
    }
}


/***/ }),

/***/ "./src/js/core/rectangle.js":
/*!**********************************!*\
  !*** ./src/js/core/rectangle.js ***!
  \**********************************/
/*! exports provided: Rectangle */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Rectangle", function() { return Rectangle; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");
/* harmony import */ var _vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./vector */ "./src/js/core/vector.js");




class Rectangle {
    constructor(x = 0, y = 0, w = 0, h = 0) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

    /**
     * Creates a rectangle from top right bottom and left offsets
     * @param {number} top
     * @param {number} right
     * @param {number} bottom
     * @param {number} left
     */
    static fromTRBL(top, right, bottom, left) {
        return new Rectangle(left, top, right - left, bottom - top);
    }

    /**
     * Constructs a new square rectangle
     * @param {number} x
     * @param {number} y
     * @param {number} size
     */
    static fromSquare(x, y, size) {
        return new Rectangle(x, y, size, size);
    }

    /**
     *
     * @param {Vector} p1
     * @param {Vector} p2
     */
    static fromTwoPoints(p1, p2) {
        const left = Math.min(p1.x, p2.x);
        const top = Math.min(p1.y, p2.y);
        const right = Math.max(p1.x, p2.x);
        const bottom = Math.max(p1.y, p2.y);
        return new Rectangle(left, top, right - left, bottom - top);
    }

    /**
     * Returns if a intersects b
     * @param {Rectangle} a
     * @param {Rectangle} b
     */
    static intersects(a, b) {
        return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
    }

    /**
     * Copies this instance
     * @returns {Rectangle}
     */
    clone() {
        return new Rectangle(this.x, this.y, this.w, this.h);
    }

    /**
     * Returns if this rectangle is empty
     * @returns {boolean}
     */
    isEmpty() {
        return Object(_utils__WEBPACK_IMPORTED_MODULE_1__["epsilonCompare"])(this.w * this.h, 0);
    }

    /**
     * Returns if this rectangle is equal to the other while taking an epsilon into account
     * @param {Rectangle} other
     * @param {number} epsilon
     */
    equalsEpsilon(other, epsilon) {
        return (
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["epsilonCompare"])(this.x, other.x, epsilon) &&
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["epsilonCompare"])(this.y, other.y, epsilon) &&
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["epsilonCompare"])(this.w, other.w, epsilon) &&
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["epsilonCompare"])(this.h, other.h, epsilon)
        );
    }

    /**
     * @returns {number}
     */
    left() {
        return this.x;
    }

    /**
     * @returns {number}
     */
    right() {
        return this.x + this.w;
    }

    /**
     * @returns {number}
     */
    top() {
        return this.y;
    }

    /**
     * @returns {number}
     */
    bottom() {
        return this.y + this.h;
    }

    /**
     * Returns Top, Right, Bottom, Left
     * @returns {[number, number, number, number]}
     */
    trbl() {
        return [this.y, this.right(), this.bottom(), this.x];
    }

    /**
     * Returns the center of the rect
     * @returns {Vector}
     */
    getCenter() {
        return new _vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](this.x + this.w / 2, this.y + this.h / 2);
    }

    /**
     * Sets the right side of the rect without moving it
     * @param {number} right
     */
    setRight(right) {
        this.w = right - this.x;
    }

    /**
     * Sets the bottom side of the rect without moving it
     * @param {number} bottom
     */
    setBottom(bottom) {
        this.h = bottom - this.y;
    }

    /**
     * Sets the top side of the rect without scaling it
     * @param {number} top
     */
    setTop(top) {
        const bottom = this.bottom();
        this.y = top;
        this.setBottom(bottom);
    }

    /**
     * Sets the left side of the rect without scaling it
     * @param {number} left
     */
    setLeft(left) {
        const right = this.right();
        this.x = left;
        this.setRight(right);
    }

    /**
     * Returns the top left point
     * @returns {Vector}
     */
    topLeft() {
        return new _vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](this.x, this.y);
    }

    /**
     * Returns the bottom left point
     * @returns {Vector}
     */
    bottomRight() {
        return new _vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](this.right(), this.bottom());
    }

    /**
     * Moves the rectangle by the given parameters
     * @param {number} x
     * @param {number} y
     */
    moveBy(x, y) {
        this.x += x;
        this.y += y;
    }

    /**
     * Moves the rectangle by the given vector
     * @param {Vector} vec
     */
    moveByVector(vec) {
        this.x += vec.x;
        this.y += vec.y;
    }

    /**
     * Scales every parameter (w, h, x, y) by the given factor. Useful to transform from world to
     * tile space and vice versa
     * @param {number} factor
     */
    allScaled(factor) {
        return new Rectangle(this.x * factor, this.y * factor, this.w * factor, this.h * factor);
    }

    /**
     * Expands the rectangle in all directions
     * @param {number} amount
     * @returns {Rectangle} new rectangle
     */
    expandedInAllDirections(amount) {
        return new Rectangle(this.x - amount, this.y - amount, this.w + 2 * amount, this.h + 2 * amount);
    }

    /**
     * Returns if the given rectangle is contained
     * @param {Rectangle} rect
     * @returns {boolean}
     */
    containsRect(rect) {
        return (
            this.x <= rect.right() &&
            rect.x <= this.right() &&
            this.y <= rect.bottom() &&
            rect.y <= this.bottom()
        );
    }

    /**
     * Returns if this rectangle contains the other rectangle specified by the parameters
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     * @returns {boolean}
     */
    containsRect4Params(x, y, w, h) {
        return this.x <= x + w && x <= this.right() && this.y <= y + h && y <= this.bottom();
    }

    /**
     * Returns if the rectangle contains the given circle at (x, y) with the radius (radius)
     * @param {number} x
     * @param {number} y
     * @param {number} radius
     * @returns {boolean}
     */
    containsCircle(x, y, radius) {
        return (
            this.x <= x + radius &&
            x - radius <= this.right() &&
            this.y <= y + radius &&
            y - radius <= this.bottom()
        );
    }

    /**
     * Returns if hte rectangle contains the given point
     * @param {number} x
     * @param {number} y
     * @returns {boolean}
     */
    containsPoint(x, y) {
        return x >= this.x && x < this.right() && y >= this.y && y < this.bottom();
    }

    /**
     * Returns the shared area with another rectangle, or null if there is no intersection
     * @param {Rectangle} rect
     * @returns {Rectangle|null}
     */
    getIntersection(rect) {
        const left = Math.max(this.x, rect.x);
        const top = Math.max(this.y, rect.y);

        const right = Math.min(this.x + this.w, rect.x + rect.w);
        const bottom = Math.min(this.y + this.h, rect.y + rect.h);

        if (right <= left || bottom <= top) {
            return null;
        }

        return Rectangle.fromTRBL(top, right, bottom, left);
    }

    /**
     * Returns the union of this rectangle with another
     * @param {Rectangle} rect
     */
    getUnion(rect) {
        if (this.isEmpty()) {
            // If this is rect is empty, return the other one
            return rect.clone();
        }
        if (rect.isEmpty()) {
            // If the other is empty, return this one
            return this.clone();
        }

        // Find contained area
        const left = Math.min(this.x, rect.x);
        const top = Math.min(this.y, rect.y);
        const right = Math.max(this.right(), rect.right());
        const bottom = Math.max(this.bottom(), rect.bottom());

        return Rectangle.fromTRBL(top, right, bottom, left);
    }

    /**
     * Good for caching stuff
     */
    toCompareableString() {
        return (
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.x) +
            "/" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.y) +
            "/" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.w) +
            "/" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.h)
        );
    }

    /**
     * Good for printing stuff
     */
    toString() {
        return (
            "[x:" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.x) +
            "| y:" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.y) +
            "| w:" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.w) +
            "| h:" +
            Object(_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(this.h) +
            "]"
        );
    }

    /**
     * Returns a new rectangle in tile space which includes all tiles which are visible in this rect
     * @returns {Rectangle}
     */
    toTileCullRectangle() {
        return new Rectangle(
            Math.floor(this.x / _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize),
            Math.floor(this.y / _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize),
            Math.ceil(this.w / _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize),
            Math.ceil(this.h / _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize)
        );
    }
}


/***/ }),

/***/ "./src/js/core/request_channel.js":
/*!****************************************!*\
  !*** ./src/js/core/request_channel.js ***!
  \****************************************/
/*! exports provided: PROMISE_ABORTED, RequestChannel */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PROMISE_ABORTED", function() { return PROMISE_ABORTED; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RequestChannel", function() { return RequestChannel; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");



const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("request_channel");

// Thrown when a request is aborted
const PROMISE_ABORTED = "promise-aborted";

class RequestChannel {
    constructor() {
        /** @type {Array<Promise>} */
        this.pendingPromises = [];
    }

    /**
     *
     * @param {Promise<any>} promise
     * @returns {Promise<any>}
     */
    watch(promise) {
        // log(this, "Added new promise:", promise, "(pending =", this.pendingPromises.length, ")");
        let cancelled = false;
        const wrappedPromise = new Promise((resolve, reject) => {
            promise.then(
                result => {
                    // Remove from pending promises
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["fastArrayDeleteValueIfContained"])(this.pendingPromises, wrappedPromise);

                    // If not cancelled, resolve promise with same payload
                    if (!cancelled) {
                        resolve.call(this, result);
                    } else {
                        logger.warn("Not resolving because promise got cancelled");
                        // reject.call(this, PROMISE_ABORTED);
                    }
                },
                err => {
                    // Remove from pending promises
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["fastArrayDeleteValueIfContained"])(this.pendingPromises, wrappedPromise);

                    // If not cancelled, reject promise with same payload
                    if (!cancelled) {
                        reject.call(this, err);
                    } else {
                        logger.warn("Not rejecting because promise got cancelled");
                        // reject.call(this, PROMISE_ABORTED);
                    }
                }
            );
        });

        // Add cancel handler
        // @ts-ignore
        wrappedPromise.cancel = function () {
            cancelled = true;
        };

        this.pendingPromises.push(wrappedPromise);
        return wrappedPromise;
    }

    cancelAll() {
        if (this.pendingPromises.length > 0) {
            logger.log("Cancel all pending promises (", this.pendingPromises.length, ")");
        }
        for (let i = 0; i < this.pendingPromises.length; ++i) {
            // @ts-ignore
            this.pendingPromises[i].cancel();
        }
        this.pendingPromises = [];
    }
}


/***/ }),

/***/ "./src/js/core/restriction_manager.js":
/*!********************************************!*\
  !*** ./src/js/core/restriction_manager.js ***!
  \********************************************/
/*! exports provided: RestrictionManager */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RestrictionManager", function() { return RestrictionManager; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _explained_result__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./explained_result */ "./src/js/core/explained_result.js");
/* harmony import */ var _query_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./query_parameters */ "./src/js/core/query_parameters.js");
/* harmony import */ var _read_write_proxy__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./read_write_proxy */ "./src/js/core/read_write_proxy.js");






class RestrictionManager extends _read_write_proxy__WEBPACK_IMPORTED_MODULE_3__["ReadWriteProxy"] {
    /**
     * @param {Application} app
     */
    constructor(app) {
        super(app, "restriction-flags.bin");

        this.currentData = this.getDefaultData();
    }

    // -- RW Proxy Impl

    /**
     * @param {any} data
     */
    verify(data) {
        return _explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].good();
    }

    /**
     */
    getDefaultData() {
        return {
            version: this.getCurrentVersion(),
            savegameV1119Imported: false,
        };
    }

    /**
     */
    getCurrentVersion() {
        return 1;
    }

    /**
     * @param {any} data
     */
    migrate(data) {
        return _explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].good();
    }

    initialize() {
        return this.readAsync().then(() => {
            if (this.currentData.savegameV1119Imported) {
                console.warn("Levelunlock is granted to current user due to past savegame");
            }
        });
    }

    // -- End RW Proxy Impl

    /**
     * Checks if there are any savegames from the 1.1.19 version
     */
    onHasLegacySavegamesChanged(has119Savegames = false) {
        if (has119Savegames && !this.currentData.savegameV1119Imported) {
            this.currentData.savegameV1119Imported = true;
            console.warn("Current user now has access to all levels due to 1119 savegame");
            return this.writeAsync();
        }
        return Promise.resolve();
    }

    /**
     * Returns if the app is currently running as the limited version
     * @returns {boolean}
     */
    isLimitedVersion() {
        if (_config__WEBPACK_IMPORTED_MODULE_0__["IS_MAC"]) {
            // On mac, the full version is always active
            return false;
        }

        if (false) {}

        if (_query_parameters__WEBPACK_IMPORTED_MODULE_2__["queryParamOptions"].fullVersion) {
            // Full version is activated via flag
            return false;
        }

        if (false) {}

        return false;
    }

    /**
     * Returns if the app markets the standalone version on steam
     * @returns {boolean}
     */
    getIsStandaloneMarketingActive() {
        return this.isLimitedVersion();
    }

    /**
     * Returns if exporting the base as a screenshot is possible
     * @returns {boolean}
     */
    getIsExportingScreenshotsPossible() {
        return !this.isLimitedVersion();
    }

    /**
     * Returns the maximum number of supported waypoints
     * @returns {number}
     */
    getMaximumWaypoints() {
        return this.isLimitedVersion() ? 2 : 1e20;
    }

    /**
     * Returns if the user has unlimited savegames
     * @returns {boolean}
     */
    getHasUnlimitedSavegames() {
        return !this.isLimitedVersion();
    }

    /**
     * Returns if the app has all settings available
     * @returns {boolean}
     */
    getHasExtendedSettings() {
        return !this.isLimitedVersion();
    }

    /**
     * Returns if all upgrades are available
     * @returns {boolean}
     */
    getHasExtendedUpgrades() {
        return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
    }

    /**
     * Returns if all levels & freeplay is available
     * @returns {boolean}
     */
    getHasExtendedLevelsAndFreeplay() {
        return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
    }
}


/***/ }),

/***/ "./src/js/core/rng.js":
/*!****************************!*\
  !*** ./src/js/core/rng.js ***!
  \****************************/
/*! exports provided: RandomNumberGenerator */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RandomNumberGenerator", function() { return RandomNumberGenerator; });
// ALEA RNG

function Mash() {
    var n = 0xefc8249d;
    return function (data) {
        data = data.toString();
        for (var i = 0; i < data.length; i++) {
            n += data.charCodeAt(i);
            var h = 0.02519603282416938 * n;
            n = h >>> 0;
            h -= n;
            h *= n;
            n = h >>> 0;
            h -= n;
            n += h * 0x100000000; // 2^32
        }
        return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
    };
}

/**
 * @param {number|string} seed
 */
function makeNewRng(seed) {
    // Johannes Baagøe <baagoe@baagoe.com>, 2010
    var c = 1;
    var mash = Mash();
    let s0 = mash(" ");
    let s1 = mash(" ");
    let s2 = mash(" ");

    s0 -= mash(seed);
    if (s0 < 0) {
        s0 += 1;
    }
    s1 -= mash(seed);
    if (s1 < 0) {
        s1 += 1;
    }
    s2 -= mash(seed);
    if (s2 < 0) {
        s2 += 1;
    }
    mash = null;

    var random = function () {
        var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
        s0 = s1;
        s1 = s2;
        return (s2 = t - (c = t | 0));
    };

    random.exportState = function () {
        return [s0, s1, s2, c];
    };

    random.importState = function (i) {
        s0 = +i[0] || 0;
        s1 = +i[1] || 0;
        s2 = +i[2] || 0;
        c = +i[3] || 0;
    };

    return random;
}

class RandomNumberGenerator {
    /**
     *
     * @param {number|string=} seed
     */
    constructor(seed) {
        this.internalRng = makeNewRng(seed || Math.random());
    }

    /**
     * Re-seeds the generator
     * @param {number|string} seed
     */
    reseed(seed) {
        this.internalRng = makeNewRng(seed || Math.random());
    }

    /**
     * @returns {number} between 0 and 1
     */
    next() {
        return this.internalRng();
    }

    /**
     * Random choice of an array
     * @param {array} array
     */
    choice(array) {
        const index = this.nextIntRange(0, array.length);
        return array[index];
    }

    /**
     * @param {number} min
     * @param {number} max
     * @returns {number} Integer in range [min, max[
     */
    nextIntRange(min, max) {
        window.assert(Number.isFinite(min), "Minimum is no integer");
        window.assert(Number.isFinite(max), "Maximum is no integer");
        window.assert(max > min, "rng: max <= min");
        return Math.floor(this.next() * (max - min) + min);
    }

    /**
     * @param {number} min
     * @param {number} max
     * @returns {number} Number in range [min, max[
     */
    nextRange(min, max) {
        window.assert(max > min, "rng: max <= min");
        return this.next() * (max - min) + min;
    }

    /**
     * Updates the seed
     * @param {number} seed
     */
    setSeed(seed) {
        this.internalRng = makeNewRng(seed);
    }
}


/***/ }),

/***/ "./src/js/core/sensitive_utils.encrypt.js":
/*!************************************************!*\
  !*** ./src/js/core/sensitive_utils.encrypt.js ***!
  \************************************************/
/*! exports provided: sha1, getNameOfProvider, CRC_PREFIX, computeCrc */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sha1", function() { return sha1; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getNameOfProvider", function() { return getNameOfProvider; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CRC_PREFIX", function() { return CRC_PREFIX; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "computeCrc", function() { return computeCrc; });
/* harmony import */ var rusha__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rusha */ "./node_modules/rusha/dist/rusha.js");
/* harmony import */ var rusha__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(rusha__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var crc_crc32__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! crc/crc32 */ "./node_modules/crc/crc32.js");
/* harmony import */ var _lzstring__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./lzstring */ "./src/js/core/lzstring.js");




function sha1(str) {
    return Object(rusha__WEBPACK_IMPORTED_MODULE_0__["createHash"])().update(str).digest("hex");
}

// Window.location.host
function getNameOfProvider() {
    return window[Object(_lzstring__WEBPACK_IMPORTED_MODULE_2__["decompressX64"])("DYewxghgLgliB2Q")][Object(_lzstring__WEBPACK_IMPORTED_MODULE_2__["decompressX64"])("BYewzgLgdghgtgUyA")];
}

// Distinguish legacy crc prefixes
const CRC_PREFIX = "crc32".padEnd(32, "-");

/**
 * Computes the crc for a given string
 * @param {string} str
 */
function computeCrc(str) {
    return CRC_PREFIX + Object(crc_crc32__WEBPACK_IMPORTED_MODULE_1__["default"])(str).toString(16).padStart(8, "0");
}


/***/ }),

/***/ "./src/js/core/signal.js":
/*!*******************************!*\
  !*** ./src/js/core/signal.js ***!
  \*******************************/
/*! exports provided: STOP_PROPAGATION, Signal */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "STOP_PROPAGATION", function() { return STOP_PROPAGATION; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Signal", function() { return Signal; });
const STOP_PROPAGATION = "stop_propagation";

class Signal {
    constructor() {
        this.receivers = [];
        this.modifyCount = 0;
    }

    /**
     * Adds a new signal listener
     * @param {function} receiver
     * @param {object} scope
     */
    add(receiver, scope = null) {
        window.assert(receiver, "receiver is null");
        this.receivers.push({ receiver, scope });
        ++this.modifyCount;
    }

    /**
     * Dispatches the signal
     * @param  {...any} payload
     */
    dispatch() {
        const modifyState = this.modifyCount;

        const n = this.receivers.length;
        for (let i = 0; i < n; ++i) {
            const { receiver, scope } = this.receivers[i];
            if (receiver.apply(scope, arguments) === STOP_PROPAGATION) {
                return STOP_PROPAGATION;
            }

            if (modifyState !== this.modifyCount) {
                // Signal got modified during iteration
                return STOP_PROPAGATION;
            }
        }
    }

    /**
     * Removes a receiver
     * @param {function} receiver
     */
    remove(receiver) {
        let index = null;
        const n = this.receivers.length;
        for (let i = 0; i < n; ++i) {
            if (this.receivers[i].receiver === receiver) {
                index = i;
                break;
            }
        }
        window.assert(index !== null, "Receiver not found in list");
        this.receivers.splice(index, 1);
        ++this.modifyCount;
    }

    /**
     * Removes all receivers
     */
    removeAll() {
        this.receivers = [];
        ++this.modifyCount;
    }
}


/***/ }),

/***/ "./src/js/core/singleton_factory.js":
/*!******************************************!*\
  !*** ./src/js/core/singleton_factory.js ***!
  \******************************************/
/*! exports provided: SingletonFactory */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SingletonFactory", function() { return SingletonFactory; });
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");


const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("singleton_factory");

// simple factory pattern
class SingletonFactory {
    constructor(id) {
        this.id = id;

        // Store array as well as dictionary, to speed up lookups
        this.entries = [];
        this.idToEntry = {};
    }

    getId() {
        return this.id;
    }

    register(classHandle) {
        // First, construct instance
        const instance = new classHandle();

        // Extract id
        const id = instance.getId();
        window.assert(id, "Factory: Invalid id for class " + classHandle.name + ": " + id);

        // Check duplicates
        window.assert(!this.idToEntry[id], "Duplicate factory entry for " + id);

        // Insert
        this.entries.push(instance);
        this.idToEntry[id] = instance;
    }

    /**
     * Checks if a given id is registered
     * @param {string} id
     * @returns {boolean}
     */
    hasId(id) {
        return !!this.idToEntry[id];
    }

    /**
     * Finds an instance by a given id
     * @param {string} id
     * @returns {object}
     */
    findById(id) {
        const entry = this.idToEntry[id];
        if (!entry) {
            logger.error("Object with id", id, "is not registered!");
            window.assert(false, "Factory: Object with id '" + id + "' is not registered!");
            return null;
        }
        return entry;
    }

    /**
     * Finds an instance by its constructor (The class handle)
     * @param {object} classHandle
     * @returns {object}
     */
    findByClass(classHandle) {
        for (let i = 0; i < this.entries.length; ++i) {
            if (this.entries[i] instanceof classHandle) {
                return this.entries[i];
            }
        }
        window.assert(false, "Factory: Object not found by classHandle (classid: " + classHandle.name + ")");
        return null;
    }

    /**
     * Returns all entries
     * @returns {Array<object>}
     */
    getEntries() {
        return this.entries;
    }

    /**
     * Returns all registered ids
     * @returns {Array<string>}
     */
    getAllIds() {
        return Object.keys(this.idToEntry);
    }

    /**
     * Returns amount of stored entries
     * @returns {number}
     */
    getNumEntries() {
        return this.entries.length;
    }
}


/***/ }),

/***/ "./src/js/core/sprites.js":
/*!********************************!*\
  !*** ./src/js/core/sprites.js ***!
  \********************************/
/*! exports provided: ORIGINAL_SPRITE_SCALE, FULL_CLIP_RECT, BaseSprite, SpriteAtlasLink, AtlasSprite, RegularSprite */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ORIGINAL_SPRITE_SCALE", function() { return ORIGINAL_SPRITE_SCALE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FULL_CLIP_RECT", function() { return FULL_CLIP_RECT; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSprite", function() { return BaseSprite; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpriteAtlasLink", function() { return SpriteAtlasLink; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AtlasSprite", function() { return AtlasSprite; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RegularSprite", function() { return RegularSprite; });
/* harmony import */ var _draw_parameters__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");




const ORIGINAL_SPRITE_SCALE = "0.75";
const FULL_CLIP_RECT = new _rectangle__WEBPACK_IMPORTED_MODULE_1__["Rectangle"](0, 0, 1, 1);

const EXTRUDE = 0.1;

class BaseSprite {
    /**
     * Returns the raw handle
     * @returns {HTMLImageElement|HTMLCanvasElement}
     */
    getRawTexture() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return null;
    }

    /**
     * Draws the sprite
     * @param {CanvasRenderingContext2D} context
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     */
    draw(context, x, y, w, h) {
        // eslint-disable-line no-unused-vars
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }
}

/**
 * Position of a sprite within an atlas
 */
class SpriteAtlasLink {
    /**
     *
     * @param {object} param0
     * @param {number} param0.packedX
     * @param {number} param0.packedY
     * @param {number} param0.packOffsetX
     * @param {number} param0.packOffsetY
     * @param {number} param0.packedW
     * @param {number} param0.packedH
     * @param {number} param0.w
     * @param {number} param0.h
     * @param {HTMLImageElement|HTMLCanvasElement} param0.atlas
     */
    constructor({ w, h, packedX, packedY, packOffsetX, packOffsetY, packedW, packedH, atlas }) {
        this.packedX = packedX;
        this.packedY = packedY;
        this.packedW = packedW;
        this.packedH = packedH;
        this.packOffsetX = packOffsetX;
        this.packOffsetY = packOffsetY;
        this.atlas = atlas;
        this.w = w;
        this.h = h;
    }
}

class AtlasSprite extends BaseSprite {
    /**
     *
     * @param {string} spriteName
     */
    constructor(spriteName = "sprite") {
        super();
        /** @type {Object.<string, SpriteAtlasLink>} */
        this.linksByResolution = {};
        this.spriteName = spriteName;
    }

    getRawTexture() {
        return this.linksByResolution[ORIGINAL_SPRITE_SCALE].atlas;
    }

    /**
     * Draws the sprite onto a regular context using no contexts
     * @see {BaseSprite.draw}
     */
    draw(context, x, y, w, h) {
        if (false) {}

        const link = this.linksByResolution[ORIGINAL_SPRITE_SCALE];

        window.assert(
            link,
            "Link not known: " +
                ORIGINAL_SPRITE_SCALE +
                " (having " +
                Object.keys(this.linksByResolution) +
                ")"
        );

        const width = w || link.w;
        const height = h || link.h;

        const scaleW = width / link.w;
        const scaleH = height / link.h;

        context.drawImage(
            link.atlas,

            link.packedX,
            link.packedY,
            link.packedW,
            link.packedH,

            x + link.packOffsetX * scaleW,
            y + link.packOffsetY * scaleH,
            link.packedW * scaleW,
            link.packedH * scaleH
        );
    }

    /**
     *
     * @param {DrawParameters} parameters
     * @param {number} x
     * @param {number} y
     * @param {number} size
     * @param {boolean=} clipping
     */
    drawCachedCentered(parameters, x, y, size, clipping = true) {
        this.drawCached(parameters, x - size / 2, y - size / 2, size, size, clipping);
    }

    /**
     *
     * @param {CanvasRenderingContext2D} context
     * @param {number} x
     * @param {number} y
     * @param {number} size
     */
    drawCentered(context, x, y, size) {
        this.draw(context, x - size / 2, y - size / 2, size, size);
    }

    /**
     * Draws the sprite
     * @param {DrawParameters} parameters
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     * @param {boolean=} clipping Whether to perform culling
     */
    drawCached(parameters, x, y, w = null, h = null, clipping = true) {
        if (false) {}

        const visibleRect = parameters.visibleRect;

        const scale = parameters.desiredAtlasScale;
        const link = this.linksByResolution[scale];

        if (!link) {
            window.assert(false, `Link not known: ${scale} (having ${Object.keys(this.linksByResolution)})`);
        }

        const scaleW = w / link.w;
        const scaleH = h / link.h;

        let destX = x + link.packOffsetX * scaleW;
        let destY = y + link.packOffsetY * scaleH;
        let destW = link.packedW * scaleW;
        let destH = link.packedH * scaleH;

        let srcX = link.packedX;
        let srcY = link.packedY;
        let srcW = link.packedW;
        let srcH = link.packedH;

        let intersection = null;

        if (clipping) {
            const rect = new _rectangle__WEBPACK_IMPORTED_MODULE_1__["Rectangle"](destX, destY, destW, destH);
            intersection = rect.getIntersection(visibleRect);
            if (!intersection) {
                return;
            }

            srcX += (intersection.x - destX) / scaleW;
            srcY += (intersection.y - destY) / scaleH;

            srcW *= intersection.w / destW;
            srcH *= intersection.h / destH;

            destX = intersection.x;
            destY = intersection.y;

            destW = intersection.w;
            destH = intersection.h;
        }

        parameters.context.drawImage(
            link.atlas,

            // atlas src pos
            srcX,
            srcY,

            // atlas src size
            srcW,
            srcH,

            // dest pos and size
            destX - EXTRUDE,
            destY - EXTRUDE,
            destW + 2 * EXTRUDE,
            destH + 2 * EXTRUDE
        );
    }

    /**
     * Draws a subset of the sprite. Does NO culling
     * @param {DrawParameters} parameters
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     * @param {Rectangle=} clipRect The rectangle in local space (0 ... 1) to draw of the image
     */
    drawCachedWithClipRect(parameters, x, y, w = null, h = null, clipRect = FULL_CLIP_RECT) {
        if (false) {}

        const scale = parameters.desiredAtlasScale;
        const link = this.linksByResolution[scale];

        if (!link) {
            window.assert(false, `Link not known: ${scale} (having ${Object.keys(this.linksByResolution)})`);
        }

        const scaleW = w / link.w;
        const scaleH = h / link.h;

        let destX = x + link.packOffsetX * scaleW + clipRect.x * w;
        let destY = y + link.packOffsetY * scaleH + clipRect.y * h;
        let destW = link.packedW * scaleW * clipRect.w;
        let destH = link.packedH * scaleH * clipRect.h;

        let srcX = link.packedX + clipRect.x * link.packedW;
        let srcY = link.packedY + clipRect.y * link.packedH;
        let srcW = link.packedW * clipRect.w;
        let srcH = link.packedH * clipRect.h;

        parameters.context.drawImage(
            link.atlas,

            // atlas src pos
            srcX,
            srcY,

            // atlas src siize
            srcW,
            srcH,

            // dest pos and size
            destX - EXTRUDE,
            destY - EXTRUDE,
            destW + 2 * EXTRUDE,
            destH + 2 * EXTRUDE
        );
    }

    /**
     * Renders into an html element
     * @param {HTMLElement} element
     * @param {number} w
     * @param {number} h
     */
    renderToHTMLElement(element, w = 1, h = 1) {
        element.style.position = "relative";
        element.innerHTML = this.getAsHTML(w, h);
    }

    /**
     * Returns the html to render as icon
     * @param {number} w
     * @param {number} h
     */
    getAsHTML(w, h) {
        const link = this.linksByResolution["0.5"];

        // Find out how much we have to scale it so that it fits
        const scaleX = w / link.w;
        const scaleY = h / link.h;

        // Find out how big the scaled atlas is
        const atlasW = link.atlas.width * scaleX;
        const atlasH = link.atlas.height * scaleY;

        // @ts-ignore
        const srcSafe = link.atlas.src.replaceAll("\\", "/");

        // Find out how big we render the sprite
        const widthAbsolute = scaleX * link.packedW;
        const heightAbsolute = scaleY * link.packedH;

        // Compute the position in the relative container
        const leftRelative = (link.packOffsetX * scaleX) / w;
        const topRelative = (link.packOffsetY * scaleY) / h;
        const widthRelative = widthAbsolute / w;
        const heightRelative = heightAbsolute / h;

        // Scale the atlas relative to the width and height of the element
        const bgW = atlasW / widthAbsolute;
        const bgH = atlasH / heightAbsolute;

        // Figure out what the position of the atlas is
        const bgX = link.packedX * scaleX;
        const bgY = link.packedY * scaleY;

        // Fuck you, whoever thought its a good idea to make background-position work like it does now
        const bgXRelative = -bgX / (widthAbsolute - atlasW);
        const bgYRelative = -bgY / (heightAbsolute - atlasH);

        return `
            <span class="spritesheetImage" style="
                background-image: url('${srcSafe}');
                left: ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(leftRelative * 100.0)}%;
                top: ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(topRelative * 100.0)}%;
                width: ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(widthRelative * 100.0)}%;
                height: ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(heightRelative * 100.0)}%;
                background-repeat: repeat;
                background-position: ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(bgXRelative * 100.0)}% ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(
            bgYRelative * 100.0
        )}%;
                background-size: ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(bgW * 100.0)}% ${Object(_utils__WEBPACK_IMPORTED_MODULE_2__["round3Digits"])(bgH * 100.0)}%;
            "></span>
        `;
    }
}

class RegularSprite extends BaseSprite {
    constructor(sprite, w, h) {
        super();
        this.w = w;
        this.h = h;
        this.sprite = sprite;
    }

    getRawTexture() {
        return this.sprite;
    }

    /**
     * Draws the sprite, do *not* use this for sprites which are rendered! Only for drawing
     * images into buffers
     * @param {CanvasRenderingContext2D} context
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     */
    draw(context, x, y, w, h) {
        window.assert(context, "No context given");
        window.assert(x !== undefined, "No x given");
        window.assert(y !== undefined, "No y given");
        window.assert(w !== undefined, "No width given");
        window.assert(h !== undefined, "No height given");
        context.drawImage(this.sprite, x, y, w, h);
    }

    /**
     * Draws the sprite, do *not* use this for sprites which are rendered! Only for drawing
     * images into buffers
     * @param {CanvasRenderingContext2D} context
     * @param {number} x
     * @param {number} y
     * @param {number} w
     * @param {number} h
     */
    drawCentered(context, x, y, w, h) {
        window.assert(context, "No context given");
        window.assert(x !== undefined, "No x given");
        window.assert(y !== undefined, "No y given");
        window.assert(w !== undefined, "No width given");
        window.assert(h !== undefined, "No height given");
        context.drawImage(this.sprite, x - w / 2, y - h / 2, w, h);
    }
}


/***/ }),

/***/ "./src/js/core/stale_area_detector.js":
/*!********************************************!*\
  !*** ./src/js/core/stale_area_detector.js ***!
  \********************************************/
/*! exports provided: StaleAreaDetector */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StaleAreaDetector", function() { return StaleAreaDetector; });
/* harmony import */ var _game_component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game/component */ "./src/js/game/component.js");
/* harmony import */ var _game_entity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../game/entity */ "./src/js/game/entity.js");
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./rectangle */ "./src/js/core/rectangle.js");






const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_3__["createLogger"])("stale_areas");

class StaleAreaDetector {
    /**
     *
     * @param {object} param0
     * @param {import("../game/root").GameRoot} param0.root
     * @param {string} param0.name The name for reference
     * @param {(Rectangle) => void} param0.recomputeMethod Method which recomputes the given area
     */
    constructor({ root, name, recomputeMethod }) {
        this.root = root;
        this.name = name;
        this.recomputeMethod = recomputeMethod;

        /** @type {Rectangle} */
        this.staleArea = null;
    }

    /**
     * Invalidates the given area
     * @param {Rectangle} area
     */
    invalidate(area) {
        // logger.log(this.name, "invalidated", area.toString());
        if (this.staleArea) {
            this.staleArea = this.staleArea.getUnion(area);
        } else {
            this.staleArea = area.clone();
        }
    }

    /**
     * Makes this detector recompute the area of an entity whenever
     * it changes in any way
     * @param {Array<typeof Component>} components
     * @param {number} tilesAround How many tiles arround to expand the area
     */
    recomputeOnComponentsChanged(components, tilesAround) {
        const componentIds = components.map(component => component.getId());

        /**
         * Internal checker method
         * @param {Entity} entity
         */
        const checker = entity => {
            if (!this.root.gameInitialized) {
                return;
            }

            // Check for all components
            for (let i = 0; i < componentIds.length; ++i) {
                if (entity.components[componentIds[i]]) {
                    // Entity is relevant, compute affected area
                    const area = entity.components.StaticMapEntity.getTileSpaceBounds().expandedInAllDirections(
                        tilesAround
                    );
                    this.invalidate(area);
                    return;
                }
            }
        };

        this.root.signals.entityAdded.add(checker);
        this.root.signals.entityChanged.add(checker);
        this.root.signals.entityComponentRemoved.add(checker);
        this.root.signals.entityGotNewComponent.add(checker);
        this.root.signals.entityDestroyed.add(checker);
    }

    /**
     * Updates the stale area
     */
    update() {
        if (this.staleArea) {
            logger.log(this.name, "is recomputing", this.staleArea.toString());
            if (false) {}
            this.recomputeMethod(this.staleArea);
            this.staleArea = null;
        }
    }
}


/***/ }),

/***/ "./src/js/core/state_manager.js":
/*!**************************************!*\
  !*** ./src/js/core/state_manager.js ***!
  \**************************************/
/*! exports provided: StateManager */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StateManager", function() { return StateManager; });
/* harmony import */ var _game_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logging */ "./src/js/core/logging.js");
/* harmony import */ var _error_handler__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./error_handler */ "./src/js/core/error_handler.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");






const logger = Object(_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("state_manager");

/**
 * This is the main state machine which drives the game states.
 */
class StateManager {
    /**
     * @param {Application} app
     */
    constructor(app) {
        this.app = app;

        /** @type {GameState} */
        this.currentState = null;

        /** @type {Object.<string, new() => GameState>} */
        this.stateClasses = {};
    }

    /**
     * Registers a new state class, should be a GameState derived class
     * @param {object} stateClass
     */
    register(stateClass) {
        // Create a dummy to retrieve the key
        const dummy = new stateClass();
        window.assert(dummy instanceof _game_state__WEBPACK_IMPORTED_MODULE_0__["GameState"], "Not a state!");
        const key = dummy.getKey();
        window.assert(!this.stateClasses[key], `State '${key}' is already registered!`);
        this.stateClasses[key] = stateClass;
    }

    /**
     * Constructs a new state or returns the instance from the cache
     * @param {string} key
     */
    constructState(key) {
        if (this.stateClasses[key]) {
            return new this.stateClasses[key]();
        }
        window.assert(false, `State '${key}' is not known!`);
    }

    /**
     * Moves to a given state
     * @param {string} key State Key
     */
    moveToState(key, payload = {}) {
        if (_error_handler__WEBPACK_IMPORTED_MODULE_2__["APPLICATION_ERROR_OCCURED"]) {
            console.warn("Skipping state transition because of application crash");
            return;
        }

        if (this.currentState) {
            if (key === this.currentState.getKey()) {
                logger.error(`State '${key}' is already active!`);
                return false;
            }
            this.currentState.internalLeaveCallback();

            // Remove all references
            for (const stateKey in this.currentState) {
                if (this.currentState.hasOwnProperty(stateKey)) {
                    delete this.currentState[stateKey];
                }
            }
            this.currentState = null;
        }

        this.currentState = this.constructState(key);
        this.currentState.internalRegisterCallback(this, this.app);

        // Clean up old elements
        Object(_utils__WEBPACK_IMPORTED_MODULE_3__["removeAllChildren"])(document.body);

        document.body.className = "gameState " + (this.currentState.getHasFadeIn() ? "" : "arrived");
        document.body.id = "state_" + key;
        document.body.innerHTML = this.currentState.internalGetFullHtml();

        const dialogParent = document.createElement("div");
        dialogParent.classList.add("modalDialogParent");
        document.body.appendChild(dialogParent);

        this.app.sound.playThemeMusic(this.currentState.getThemeMusic());

        this.currentState.internalEnterCallback(payload);
        this.currentState.onResized(this.app.screenWidth, this.app.screenHeight);

        this.app.analytics.trackStateEnter(key);

        window.history.pushState(
            {
                key,
            },
            key
        );

        Object(_utils__WEBPACK_IMPORTED_MODULE_3__["waitNextFrame"])().then(() => {
            document.body.classList.add("arrived");
        });

        return true;
    }

    /**
     * Returns the current state
     * @returns {GameState}
     */
    getCurrentState() {
        return this.currentState;
    }
}


/***/ }),

/***/ "./src/js/core/textual_game_state.js":
/*!*******************************************!*\
  !*** ./src/js/core/textual_game_state.js ***!
  \*******************************************/
/*! exports provided: TextualGameState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextualGameState", function() { return TextualGameState; });
/* harmony import */ var _game_hud_parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game/hud/parts/modal_dialogs */ "./src/js/game/hud/parts/modal_dialogs.js");
/* harmony import */ var _game_state__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");




/**
 * Baseclass for all game states which are structured similary: A header with back button + some
 * scrollable content.
 */
class TextualGameState extends _game_state__WEBPACK_IMPORTED_MODULE_1__["GameState"] {
    ///// INTERFACE ////

    /**
     * Should return the states inner html. If not overriden, will create a scrollable container
     * with the content of getMainContentHTML()
     * @returns {string}
     */
    getInnerHTML() {
        return `
            <div class="content mainContent">
                ${this.getMainContentHTML()}
            </div>
        `;
    }

    /**
     * Should return the states HTML content.
     */
    getMainContentHTML() {
        return "";
    }

    /**
     * Should return the title of the game state. If null, no title and back button will
     * get created
     * @returns {string|null}
     */
    getStateHeaderTitle() {
        return null;
    }

    /////////////

    /**
     * Back button handler, can be overridden. Per default it goes back to the main menu,
     * or if coming from the game it moves back to the game again.
     */
    onBackButton() {
        if (this.backToStateId) {
            this.moveToState(this.backToStateId, this.backToStatePayload);
        } else {
            this.moveToState(this.getDefaultPreviousState());
        }
    }

    /**
     * Returns the default state to go back to
     */
    getDefaultPreviousState() {
        return "MainMenuState";
    }

    /**
     * Goes to a new state, telling him to go back to this state later
     * @param {string} stateId
     */
    moveToStateAddGoBack(stateId) {
        this.moveToState(stateId, {
            backToStateId: this.key,
            backToStatePayload: {
                backToStateId: this.backToStateId,
                backToStatePayload: this.backToStatePayload,
            },
        });
    }

    /**
     * Removes all click detectors, except the one on the back button. Useful when regenerating
     * content.
     */
    clearClickDetectorsExceptHeader() {
        for (let i = 0; i < this.clickDetectors.length; ++i) {
            const detector = this.clickDetectors[i];
            if (detector.element === this.headerElement) {
                continue;
            }
            detector.cleanup();
            this.clickDetectors.splice(i, 1);
            i -= 1;
        }
    }

    /**
     * Overrides the GameState implementation to provide our own html
     */
    internalGetFullHtml() {
        let headerHtml = "";
        if (this.getStateHeaderTitle()) {
            headerHtml = `
            <div class="headerBar">
            
                <h1><button class="backButton"></button> ${this.getStateHeaderTitle()}</h1>
            </div>`;
        }

        return `
            ${headerHtml}
            <div class="container">
                    ${this.getInnerHTML()}

            </div>
        `;
    }

    //// INTERNALS /////

    /**
     * Overrides the GameState leave callback to cleanup stuff
     */
    internalLeaveCallback() {
        super.internalLeaveCallback();
        this.dialogs.cleanup();
    }

    /**
     * Overrides the GameState enter callback to setup required stuff
     * @param {any} payload
     */
    internalEnterCallback(payload) {
        super.internalEnterCallback(payload, false);
        if (payload.backToStateId) {
            this.backToStateId = payload.backToStateId;
            this.backToStatePayload = payload.backToStatePayload;
        }

        this.htmlElement.classList.add("textualState");
        if (this.getStateHeaderTitle()) {
            this.htmlElement.classList.add("hasTitle");
        }

        this.containerElement = this.htmlElement.querySelector(".widthKeeper .container");
        this.headerElement = this.htmlElement.querySelector(".headerBar > h1");

        if (this.headerElement) {
            this.trackClicks(this.headerElement, this.onBackButton);
        }

        this.dialogs = new _game_hud_parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_0__["HUDModalDialogs"](null, this.app);
        const dialogsElement = document.body.querySelector(".modalDialogParent");
        this.dialogs.initializeToElement(dialogsElement);

        this.onEnter(payload);
    }
}


/***/ }),

/***/ "./src/js/core/tracked_state.js":
/*!**************************************!*\
  !*** ./src/js/core/tracked_state.js ***!
  \**************************************/
/*! exports provided: TrackedState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TrackedState", function() { return TrackedState; });
class TrackedState {
    constructor(callbackMethod = null, callbackScope = null) {
        this.lastSeenValue = null;

        if (callbackMethod) {
            this.callback = callbackMethod;
            if (callbackScope) {
                this.callback = this.callback.bind(callbackScope);
            }
        }
    }

    set(value, changeHandler = null, changeScope = null) {
        if (value !== this.lastSeenValue) {
            // Copy value since the changeHandler call could actually modify our lastSeenValue
            const valueCopy = value;
            this.lastSeenValue = value;
            if (changeHandler) {
                if (changeScope) {
                    changeHandler.call(changeScope, valueCopy);
                } else {
                    changeHandler(valueCopy);
                }
            } else if (this.callback) {
                this.callback(value);
            } else {
                window.assert(false, "No callback specified");
            }
        }
    }

    setSilent(value) {
        this.lastSeenValue = value;
    }

    get() {
        return this.lastSeenValue;
    }
}


/***/ }),

/***/ "./src/js/core/utils.js":
/*!******************************!*\
  !*** ./src/js/core/utils.js ***!
  \******************************/
/*! exports provided: isAndroid, isIos, getPlatformName, getIPCRenderer, make2DUndefinedArray, newEmptyMap, randomInt, accessNestedPropertyReverse, randomChoice, fastArrayDelete, fastArrayDeleteValue, fastArrayDeleteValueIfContained, arrayDelete, arrayDeleteValue, epsilonCompare, lerp, findNiceValue, findNiceIntegerValue, formatBigNumber, formatBigNumberFull, waitNextFrame, round1Digit, round2Digits, round3Digits, round4Digits, clamp, makeDiv, makeButtonElement, makeButton, removeAllChildren, isSupportedBrowser, formatSecondsToTimeAgo, formatSeconds, round1DigitLocalized, formatItemsPerSecond, rotateFlatMatrix3x3, generateMatrixRotations, rotateDirectionalObject, safeModulo, smoothPulse, fillInLinkIntoTranslation, generateFileDownload, startFileChoose, getRomanNumber */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isAndroid", function() { return isAndroid; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isIos", function() { return isIos; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getPlatformName", function() { return getPlatformName; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getIPCRenderer", function() { return getIPCRenderer; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "make2DUndefinedArray", function() { return make2DUndefinedArray; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "newEmptyMap", function() { return newEmptyMap; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "randomInt", function() { return randomInt; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "accessNestedPropertyReverse", function() { return accessNestedPropertyReverse; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "randomChoice", function() { return randomChoice; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fastArrayDelete", function() { return fastArrayDelete; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fastArrayDeleteValue", function() { return fastArrayDeleteValue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fastArrayDeleteValueIfContained", function() { return fastArrayDeleteValueIfContained; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "arrayDelete", function() { return arrayDelete; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "arrayDeleteValue", function() { return arrayDeleteValue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "epsilonCompare", function() { return epsilonCompare; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lerp", function() { return lerp; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findNiceValue", function() { return findNiceValue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findNiceIntegerValue", function() { return findNiceIntegerValue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatBigNumber", function() { return formatBigNumber; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatBigNumberFull", function() { return formatBigNumberFull; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitNextFrame", function() { return waitNextFrame; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round1Digit", function() { return round1Digit; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round2Digits", function() { return round2Digits; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round3Digits", function() { return round3Digits; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round4Digits", function() { return round4Digits; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clamp", function() { return clamp; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "makeDiv", function() { return makeDiv; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "makeButtonElement", function() { return makeButtonElement; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "makeButton", function() { return makeButton; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllChildren", function() { return removeAllChildren; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isSupportedBrowser", function() { return isSupportedBrowser; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatSecondsToTimeAgo", function() { return formatSecondsToTimeAgo; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatSeconds", function() { return formatSeconds; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round1DigitLocalized", function() { return round1DigitLocalized; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatItemsPerSecond", function() { return formatItemsPerSecond; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateFlatMatrix3x3", function() { return rotateFlatMatrix3x3; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "generateMatrixRotations", function() { return generateMatrixRotations; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateDirectionalObject", function() { return rotateDirectionalObject; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "safeModulo", function() { return safeModulo; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "smoothPulse", function() { return smoothPulse; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fillInLinkIntoTranslation", function() { return fillInLinkIntoTranslation; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "generateFileDownload", function() { return generateFileDownload; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "startFileChoose", function() { return startFileChoose; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getRomanNumber", function() { return getRomanNumber; });
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");


const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"];

/**
 * Returns if this platform is android
 * @returns {boolean}
 */
function isAndroid() {
    if (true) {
        return false;
    }
    const platform = window.device.platform;
    return platform === "Android" || platform === "amazon-fireos";
}

/**
 * Returns if this platform is iOs
 * @returns {boolean}
 */
function isIos() {
    if (true) {
        return false;
    }
    return window.device.platform === "iOS";
}

/**
 * Returns a platform name
 * @returns {"android" | "browser" | "ios" | "standalone" | "unknown"}
 */
function getPlatformName() {
    if (false) {} else if (true) {
        return "browser";
    } else {}
    return "unknown";
}

/**
 * Returns the IPC renderer, or null if not within the standalone
 * @returns {object|null}
 */
let ipcRenderer = null;
function getIPCRenderer() {
    if (true) {
        return null;
    }
    if (!ipcRenderer) {
        ipcRenderer = eval("require")("electron").ipcRenderer;
    }
    return ipcRenderer;
}

/**
 * Makes a new 2D array with undefined contents
 * @param {number} w
 * @param {number} h
 * @returns {Array<Array<any>>}
 */
function make2DUndefinedArray(w, h) {
    const result = new Array(w);
    for (let x = 0; x < w; ++x) {
        result[x] = new Array(h);
    }
    return result;
}

/**
 * Creates a new map (an empty object without any props)
 */
function newEmptyMap() {
    return Object.create(null);
}

/**
 * Returns a random integer in the range [start,end]
 * @param {number} start
 * @param {number} end
 */
function randomInt(start, end) {
    return start + Math.round(Math.random() * (end - start));
}

/**
 * Access an object in a very annoying way, used for obsfuscation.
 * @param {any} obj
 * @param {Array<string>} keys
 */
function accessNestedPropertyReverse(obj, keys) {
    let result = obj;
    for (let i = keys.length - 1; i >= 0; --i) {
        result = result[keys[i]];
    }
    return result;
}

/**
 * Chooses a random entry of an array
 * @template T
 * @param {T[]} arr
 * @returns {T}
 */
function randomChoice(arr) {
    return arr[Math.floor(Math.random() * arr.length)];
}

/**
 * Deletes from an array by swapping with the last element
 * @param {Array<any>} array
 * @param {number} index
 */
function fastArrayDelete(array, index) {
    if (index < 0 || index >= array.length) {
        throw new Error("Out of bounds");
    }
    // When the element is not the last element
    if (index !== array.length - 1) {
        // Get the last element, and swap it with the one we want to delete
        const last = array[array.length - 1];
        array[index] = last;
    }

    // Finally remove the last element
    array.length -= 1;
}

/**
 * Deletes from an array by swapping with the last element. Searches
 * for the value in the array first
 * @param {Array<any>} array
 * @param {any} value
 */
function fastArrayDeleteValue(array, value) {
    if (array == null) {
        throw new Error("Tried to delete from non array!");
    }
    const index = array.indexOf(value);
    if (index < 0) {
        console.error("Value", value, "not contained in array:", array, "!");
        return value;
    }
    return fastArrayDelete(array, index);
}

/**
 * @see fastArrayDeleteValue
 * @param {Array<any>} array
 * @param {any} value
 */
function fastArrayDeleteValueIfContained(array, value) {
    if (array == null) {
        throw new Error("Tried to delete from non array!");
    }
    const index = array.indexOf(value);
    if (index < 0) {
        return value;
    }
    return fastArrayDelete(array, index);
}

/**
 * Deletes from an array at the given index
 * @param {Array<any>} array
 * @param {number} index
 */
function arrayDelete(array, index) {
    if (index < 0 || index >= array.length) {
        throw new Error("Out of bounds");
    }
    array.splice(index, 1);
}

/**
 * Deletes the given value from an array
 * @param {Array<any>} array
 * @param {any} value
 */
function arrayDeleteValue(array, value) {
    if (array == null) {
        throw new Error("Tried to delete from non array!");
    }
    const index = array.indexOf(value);
    if (index < 0) {
        console.error("Value", value, "not contained in array:", array, "!");
        return value;
    }
    return arrayDelete(array, index);
}

/**
 * Compare two floats for epsilon equality
 * @param {number} a
 * @param {number} b
 * @returns {boolean}
 */
function epsilonCompare(a, b, epsilon = 1e-5) {
    return Math.abs(a - b) < epsilon;
}

/**
 * Interpolates two numbers
 * @param {number} a
 * @param {number} b
 * @param {number} x Mix factor, 0 means 100% a, 1 means 100%b, rest is interpolated
 */
function lerp(a, b, x) {
    return a * (1 - x) + b * x;
}

/**
 * Finds a value which is nice to display, e.g. 15669 -> 15000. Also handles fractional stuff
 * @param {number} num
 */
function findNiceValue(num) {
    if (num > 1e8) {
        return num;
    }
    if (num < 0.00001) {
        return 0;
    }

    let roundAmount = 1;
    if (num > 50000) {
        roundAmount = 10000;
    } else if (num > 20000) {
        roundAmount = 5000;
    } else if (num > 5000) {
        roundAmount = 1000;
    } else if (num > 2000) {
        roundAmount = 500;
    } else if (num > 1000) {
        roundAmount = 100;
    } else if (num > 100) {
        roundAmount = 20;
    } else if (num > 20) {
        roundAmount = 5;
    }

    const niceValue = Math.floor(num / roundAmount) * roundAmount;
    if (num >= 10) {
        return Math.round(niceValue);
    }
    if (num >= 1) {
        return Math.round(niceValue * 10) / 10;
    }

    return Math.round(niceValue * 100) / 100;
}

/**
 * Finds a nice integer value
 * @see findNiceValue
 * @param {number} num
 */
function findNiceIntegerValue(num) {
    return Math.ceil(findNiceValue(num));
}

/**
 * Formats a big number
 * @param {number} num
 * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.')
 * @returns {string}
 */
function formatBigNumber(num, separator = _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.decimalSeparator) {
    const sign = num < 0 ? "-" : "";
    num = Math.abs(num);

    if (num > 1e54) {
        return sign + _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.infinite;
    }

    if (num < 10 && !Number.isInteger(num)) {
        return sign + num.toFixed(2);
    }
    if (num < 50 && !Number.isInteger(num)) {
        return sign + num.toFixed(1);
    }
    num = Math.floor(num);

    if (num < 1000) {
        return sign + "" + num;
    } else {
        let leadingDigits = num;
        let suffix = "";
        for (let suffixIndex = 0; suffixIndex < bigNumberSuffixTranslationKeys.length; ++suffixIndex) {
            leadingDigits = leadingDigits / 1000;
            suffix = _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.suffix[bigNumberSuffixTranslationKeys[suffixIndex]];
            if (leadingDigits < 1000) {
                break;
            }
        }
        const leadingDigitsRounded = round1Digit(leadingDigits);
        const leadingDigitsNoTrailingDecimal = leadingDigitsRounded
            .toString()
            .replace(".0", "")
            .replace(".", separator);
        return sign + leadingDigitsNoTrailingDecimal + suffix;
    }
}

/**
 * Formats a big number, but does not add any suffix and instead uses its full representation
 * @param {number} num
 * @param {string=} divider The divider for numbers like 50,000 (divider=',')
 * @returns {string}
 */
function formatBigNumberFull(num, divider = _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.thousandsDivider) {
    if (num < 1000) {
        return num + "";
    }
    if (num > 1e54) {
        return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.infinite;
    }
    let rest = num;
    let out = "";
    while (rest >= 1000) {
        out = (rest % 1000).toString().padStart(3, "0") + divider + out;
        rest = Math.floor(rest / 1000);
    }
    out = rest + divider + out;

    return out.substring(0, out.length - 1);
}

/**
 * Waits two frames so the ui is updated
 * @returns {Promise<void>}
 */
function waitNextFrame() {
    return new Promise(function (resolve) {
        window.requestAnimationFrame(function () {
            window.requestAnimationFrame(function () {
                resolve();
            });
        });
    });
}

/**
 * Rounds 1 digit
 * @param {number} n
 * @returns {number}
 */
function round1Digit(n) {
    return Math.floor(n * 10.0) / 10.0;
}

/**
 * Rounds 2 digits
 * @param {number} n
 * @returns {number}
 */
function round2Digits(n) {
    return Math.floor(n * 100.0) / 100.0;
}

/**
 * Rounds 3 digits
 * @param {number} n
 * @returns {number}
 */
function round3Digits(n) {
    return Math.floor(n * 1000.0) / 1000.0;
}

/**
 * Rounds 4 digits
 * @param {number} n
 * @returns {number}
 */
function round4Digits(n) {
    return Math.floor(n * 10000.0) / 10000.0;
}

/**
 * Clamps a value between [min, max]
 * @param {number} v
 * @param {number=} minimum Default 0
 * @param {number=} maximum Default 1
 */
function clamp(v, minimum = 0, maximum = 1) {
    return Math.max(minimum, Math.min(maximum, v));
}

/**
 * Helper method to create a new div element
 * @param {string=} id
 * @param {Array<string>=} classes
 * @param {string=} innerHTML
 */
function makeDivElement(id = null, classes = [], innerHTML = "") {
    const div = document.createElement("div");
    if (id) {
        div.id = id;
    }
    for (let i = 0; i < classes.length; ++i) {
        div.classList.add(classes[i]);
    }
    div.innerHTML = innerHTML;
    return div;
}

/**
 * Helper method to create a new div
 * @param {Element} parent
 * @param {string=} id
 * @param {Array<string>=} classes
 * @param {string=} innerHTML
 */
function makeDiv(parent, id = null, classes = [], innerHTML = "") {
    const div = makeDivElement(id, classes, innerHTML);
    parent.appendChild(div);
    return div;
}

/**
 * Helper method to create a new button element
 * @param {Array<string>=} classes
 * @param {string=} innerHTML
 */
function makeButtonElement(classes = [], innerHTML = "") {
    const element = document.createElement("button");
    for (let i = 0; i < classes.length; ++i) {
        element.classList.add(classes[i]);
    }
    element.classList.add("styledButton");
    element.innerHTML = innerHTML;
    return element;
}

/**
 * Helper method to create a new button
 * @param {Element} parent
 * @param {Array<string>=} classes
 * @param {string=} innerHTML
 */
function makeButton(parent, classes = [], innerHTML = "") {
    const element = makeButtonElement(classes, innerHTML);
    parent.appendChild(element);
    return element;
}

/**
 * Removes all children of the given element
 * @param {Element} elem
 */
function removeAllChildren(elem) {
    if (elem) {
        var range = document.createRange();
        range.selectNodeContents(elem);
        range.deleteContents();
    }
}

/**
 * Returns if the game supports this browser
 */
function isSupportedBrowser() {
    // please note,
    // that IE11 now returns undefined again for window.chrome
    // and new Opera 30 outputs true for window.chrome
    // but needs to check if window.opr is not undefined
    // and new IE Edge outputs to true now for window.chrome
    // and if not iOS Chrome check
    // so use the below updated condition

    if (false) {}

    // @ts-ignore
    var isChromium = window.chrome;
    var winNav = window.navigator;
    var vendorName = winNav.vendor;
    // @ts-ignore
    var isIEedge = winNav.userAgent.indexOf("Edge") > -1;
    var isIOSChrome = winNav.userAgent.match("CriOS");

    if (isIOSChrome) {
        // is Google Chrome on IOS
        return false;
    } else if (
        isChromium !== null &&
        typeof isChromium !== "undefined" &&
        vendorName === "Google Inc." &&
        isIEedge === false
    ) {
        // is Google Chrome
        return true;
    } else {
        // not Google Chrome
        return false;
    }
}

/**
 * Formats an amount of seconds into something like "5s ago"
 * @param {number} secs Seconds
 * @returns {string}
 */
function formatSecondsToTimeAgo(secs) {
    const seconds = Math.floor(secs);
    const minutes = Math.floor(seconds / 60);
    const hours = Math.floor(minutes / 60);
    const days = Math.floor(hours / 24);

    if (seconds < 60) {
        if (seconds === 1) {
            return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.oneSecondAgo;
        }
        return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.xSecondsAgo.replace("<x>", "" + seconds);
    } else if (minutes < 60) {
        if (minutes === 1) {
            return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.oneMinuteAgo;
        }
        return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.xMinutesAgo.replace("<x>", "" + minutes);
    } else if (hours < 24) {
        if (hours === 1) {
            return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.oneHourAgo;
        }
        return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.xHoursAgo.replace("<x>", "" + hours);
    } else {
        if (days === 1) {
            return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.oneDayAgo;
        }
        return _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time.xDaysAgo.replace("<x>", "" + days);
    }
}

/**
 * Formats seconds into a readable string like "5h 23m"
 * @param {number} secs Seconds
 * @returns {string}
 */
function formatSeconds(secs) {
    const trans = _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.time;
    secs = Math.ceil(secs);
    if (secs < 60) {
        return trans.secondsShort.replace("<seconds>", "" + secs);
    } else if (secs < 60 * 60) {
        const minutes = Math.floor(secs / 60);
        const seconds = secs % 60;
        return trans.minutesAndSecondsShort
            .replace("<seconds>", "" + seconds)
            .replace("<minutes>", "" + minutes);
    } else {
        const hours = Math.floor(secs / 3600);
        const minutes = Math.floor(secs / 60) % 60;
        return trans.hoursAndMinutesShort.replace("<minutes>", "" + minutes).replace("<hours>", "" + hours);
    }
}

/**
 * Formats a number like 2.51 to "2.5"
 * @param {number} speed
 * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.')
 */
function round1DigitLocalized(speed, separator = _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.decimalSeparator) {
    return round1Digit(speed).toString().replace(".", separator);
}

/**
 * Formats a number like 2.51 to "2.51 items / s"
 * @param {number} speed
 * @param {boolean=} double
 * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.')
 */
function formatItemsPerSecond(speed, double = false, separator = _translations__WEBPACK_IMPORTED_MODULE_0__["T"].global.decimalSeparator) {
    return speed === 1.0
        ? _translations__WEBPACK_IMPORTED_MODULE_0__["T"].ingame.buildingPlacement.infoTexts.oneItemPerSecond
        : _translations__WEBPACK_IMPORTED_MODULE_0__["T"].ingame.buildingPlacement.infoTexts.itemsPerSecond.replace(
              "<x>",
              round2Digits(speed).toString().replace(".", separator)
          ) + (double ? "  " + _translations__WEBPACK_IMPORTED_MODULE_0__["T"].ingame.buildingPlacement.infoTexts.itemsPerSecondDouble : "");
}

/**
 * Rotates a flat 3x3 matrix clockwise
 * Entries:
 * 0 lo
 * 1 mo
 * 2 ro
 * 3 lm
 * 4 mm
 * 5 rm
 * 6 lu
 * 7 mu
 * 8 ru
 * @param {Array<number>} flatMatrix
 */

function rotateFlatMatrix3x3(flatMatrix) {
    return [
        flatMatrix[6],
        flatMatrix[3],
        flatMatrix[0],
        flatMatrix[7],
        flatMatrix[4],
        flatMatrix[1],
        flatMatrix[8],
        flatMatrix[5],
        flatMatrix[2],
    ];
}

/**
 * Generates rotated variants of the matrix
 * @param {Array<number>} originalMatrix
 * @returns {Object<number, Array<number>>}
 */
function generateMatrixRotations(originalMatrix) {
    const result = {
        0: originalMatrix,
    };

    originalMatrix = rotateFlatMatrix3x3(originalMatrix);
    result[90] = originalMatrix;

    originalMatrix = rotateFlatMatrix3x3(originalMatrix);
    result[180] = originalMatrix;

    originalMatrix = rotateFlatMatrix3x3(originalMatrix);
    result[270] = originalMatrix;

    return result;
}

/**
 *
 * @typedef {{
 *   top: any,
 *   right: any,
 *   bottom: any,
 *   left: any
 * }} DirectionalObject
 */

/**
 * Rotates a directional object
 * @param {DirectionalObject} obj
 * @returns {DirectionalObject}
 */
function rotateDirectionalObject(obj, rotation) {
    const queue = [obj.top, obj.right, obj.bottom, obj.left];
    while (rotation !== 0) {
        rotation -= 90;
        queue.push(queue.shift());
    }

    return {
        top: queue[0],
        right: queue[1],
        bottom: queue[2],
        left: queue[3],
    };
}

/**
 * Modulo which works for negative numbers
 * @param {number} n
 * @param {number} m
 */
function safeModulo(n, m) {
    return ((n % m) + m) % m;
}

/**
 * Returns a smooth pulse between 0 and 1
 * @param {number} time time in seconds
 * @returns {number}
 */
function smoothPulse(time) {
    return Math.sin(time * 4) * 0.5 + 0.5;
}

/**
 * Fills in a <link> tag
 * @param {string} translation
 * @param {string} link
 */
function fillInLinkIntoTranslation(translation, link) {
    return translation
        .replace("<link>", "<a href='" + link + "' target='_blank'>")
        .replace("</link>", "</a>");
}

/**
 * Generates a file download
 * @param {string} filename
 * @param {string} text
 */
function generateFileDownload(filename, text) {
    var element = document.createElement("a");
    element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
    element.setAttribute("download", filename);

    element.style.display = "none";
    document.body.appendChild(element);

    element.click();
    document.body.removeChild(element);
}

/**
 * Starts a file chooser
 * @param {string} acceptedType
 */
function startFileChoose(acceptedType = ".bin") {
    var input = document.createElement("input");
    input.type = "file";
    input.accept = acceptedType;

    return new Promise(resolve => {
        input.onchange = _ => resolve(input.files[0]);
        input.click();
    });
}

const MAX_ROMAN_NUMBER = 49;
const romanLiteralsCache = ["0"];

/**
 *
 * @param {number} number
 * @returns {string}
 */
function getRomanNumber(number) {
    number = Math.max(0, Math.round(number));
    if (romanLiteralsCache[number]) {
        return romanLiteralsCache[number];
    }

    if (number > MAX_ROMAN_NUMBER) {
        return String(number);
    }

    function formatDigit(digit, unit, quintuple, decuple) {
        switch (digit) {
            case 0:
                return "";
            case 1: // I
                return unit;
            case 2: // II
                return unit + unit;
            case 3: // III
                return unit + unit + unit;
            case 4: // IV
                return unit + quintuple;
            case 9: // IX
                return unit + decuple;
            default:
                // V, VI, VII, VIII
                return quintuple + formatDigit(digit - 5, unit, quintuple, decuple);
        }
    }

    let thousands = Math.floor(number / 1000);
    let thousandsPart = "";
    while (thousands > 0) {
        thousandsPart += "M";
        thousands -= 1;
    }

    const hundreds = Math.floor((number % 1000) / 100);
    const hundredsPart = formatDigit(hundreds, "C", "D", "M");

    const tens = Math.floor((number % 100) / 10);
    const tensPart = formatDigit(tens, "X", "L", "C");

    const units = number % 10;
    const unitsPart = formatDigit(units, "I", "V", "X");

    const formatted = thousandsPart + hundredsPart + tensPart + unitsPart;

    romanLiteralsCache[number] = formatted;
    return formatted;
}


/***/ }),

/***/ "./src/js/core/vector.js":
/*!*******************************!*\
  !*** ./src/js/core/vector.js ***!
  \*******************************/
/*! exports provided: enumDirection, enumInvertedDirections, enumDirectionToAngle, enumAngleToDirection, arrayAllDirections, Vector, mixVector, enumDirectionToVector */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumDirection", function() { return enumDirection; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumInvertedDirections", function() { return enumInvertedDirections; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumDirectionToAngle", function() { return enumDirectionToAngle; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumAngleToDirection", function() { return enumAngleToDirection; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "arrayAllDirections", function() { return arrayAllDirections; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector", function() { return Vector; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mixVector", function() { return mixVector; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumDirectionToVector", function() { return enumDirectionToVector; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./config */ "./src/js/core/config.js");
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./src/js/core/utils.js");



const tileSize = _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
const halfTileSize = _config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;

/**
 * @enum {string}
 */
const enumDirection = {
    top: "top",
    right: "right",
    bottom: "bottom",
    left: "left",
};

/**
 * @enum {string}
 */
const enumInvertedDirections = {
    [enumDirection.top]: enumDirection.bottom,
    [enumDirection.right]: enumDirection.left,
    [enumDirection.bottom]: enumDirection.top,
    [enumDirection.left]: enumDirection.right,
};

/**
 * @enum {number}
 */
const enumDirectionToAngle = {
    [enumDirection.top]: 0,
    [enumDirection.right]: 90,
    [enumDirection.bottom]: 180,
    [enumDirection.left]: 270,
};

/**
 * @enum {enumDirection}
 */
const enumAngleToDirection = {
    0: enumDirection.top,
    90: enumDirection.right,
    180: enumDirection.bottom,
    270: enumDirection.left,
};

/** @type {Array<enumDirection>} */
const arrayAllDirections = [
    enumDirection.top,
    enumDirection.right,
    enumDirection.bottom,
    enumDirection.left,
];

class Vector {
    /**
     *
     * @param {number=} x
     * @param {number=} y
     */
    constructor(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    }

    /**
     * return a copy of the vector
     * @returns {Vector}
     */
    copy() {
        return new Vector(this.x, this.y);
    }

    /**
     * Adds a vector and return a new vector
     * @param {Vector} other
     * @returns {Vector}
     */
    add(other) {
        return new Vector(this.x + other.x, this.y + other.y);
    }

    /**
     * Adds a vector
     * @param {Vector} other
     * @returns {Vector}
     */
    addInplace(other) {
        this.x += other.x;
        this.y += other.y;
        return this;
    }

    /**
     * Substracts a vector and return a new vector
     * @param {Vector} other
     * @returns {Vector}
     */
    sub(other) {
        return new Vector(this.x - other.x, this.y - other.y);
    }

    /**
     * Subs a vector
     * @param {Vector} other
     * @returns {Vector}
     */
    subInplace(other) {
        this.x -= other.x;
        this.y -= other.y;
        return this;
    }

    /**
     * Multiplies with a vector and return a new vector
     * @param {Vector} other
     * @returns {Vector}
     */
    mul(other) {
        return new Vector(this.x * other.x, this.y * other.y);
    }

    /**
     * Adds two scalars and return a new vector
     * @param {number} x
     * @param {number} y
     * @returns {Vector}
     */
    addScalars(x, y) {
        return new Vector(this.x + x, this.y + y);
    }

    /**
     * Substracts a scalar and return a new vector
     * @param {number} f
     * @returns {Vector}
     */
    subScalar(f) {
        return new Vector(this.x - f, this.y - f);
    }

    /**
     * Substracts two scalars and return a new vector
     * @param {number} x
     * @param {number} y
     * @returns {Vector}
     */
    subScalars(x, y) {
        return new Vector(this.x - x, this.y - y);
    }

    /**
     * Returns the euclidian length
     * @returns {number}
     */
    length() {
        return Math.hypot(this.x, this.y);
    }

    /**
     * Returns the square length
     * @returns {number}
     */
    lengthSquare() {
        return this.x * this.x + this.y * this.y;
    }

    /**
     * Divides both components by a scalar and return a new vector
     * @param {number} f
     * @returns {Vector}
     */
    divideScalar(f) {
        return new Vector(this.x / f, this.y / f);
    }

    /**
     * Divides both components by the given scalars and return a new vector
     * @param {number} a
     * @param {number} b
     * @returns {Vector}
     */
    divideScalars(a, b) {
        return new Vector(this.x / a, this.y / b);
    }

    /**
     * Divides both components by a scalar
     * @param {number} f
     * @returns {Vector}
     */
    divideScalarInplace(f) {
        this.x /= f;
        this.y /= f;
        return this;
    }

    /**
     * Multiplies both components with a scalar and return a new vector
     * @param {number} f
     * @returns {Vector}
     */
    multiplyScalar(f) {
        return new Vector(this.x * f, this.y * f);
    }

    /**
     * Multiplies both components with two scalars and returns a new vector
     * @param {number} a
     * @param {number} b
     * @returns {Vector}
     */
    multiplyScalars(a, b) {
        return new Vector(this.x * a, this.y * b);
    }

    /**
     * For both components, compute the maximum of each component and the given scalar, and return a new vector.
     * For example:
     *   - new Vector(-1, 5).maxScalar(0) -> Vector(0, 5)
     * @param {number} f
     * @returns {Vector}
     */
    maxScalar(f) {
        return new Vector(Math.max(f, this.x), Math.max(f, this.y));
    }

    /**
     * Adds a scalar to both components and return a new vector
     * @param {number} f
     * @returns {Vector}
     */
    addScalar(f) {
        return new Vector(this.x + f, this.y + f);
    }

    /**
     * Computes the component wise minimum and return a new vector
     * @param {Vector} v
     * @returns {Vector}
     */
    min(v) {
        return new Vector(Math.min(v.x, this.x), Math.min(v.y, this.y));
    }

    /**
     * Computes the component wise maximum and return a new vector
     * @param {Vector} v
     * @returns {Vector}
     */
    max(v) {
        return new Vector(Math.max(v.x, this.x), Math.max(v.y, this.y));
    }
    /**
     * Computes the component wise absolute
     * @returns {Vector}
     */
    abs() {
        return new Vector(Math.abs(this.x), Math.abs(this.y));
    }

    /**
     * Computes the scalar product
     * @param {Vector} v
     * @returns {number}
     */
    dot(v) {
        return this.x * v.x + this.y * v.y;
    }

    /**
     * Computes the distance to a given vector
     * @param {Vector} v
     * @returns {number}
     */
    distance(v) {
        return Math.hypot(this.x - v.x, this.y - v.y);
    }

    /**
     * Computes the square distance to a given vectort
     * @param {Vector} v
     * @returns {number}
     */
    distanceSquare(v) {
        const dx = this.x - v.x;
        const dy = this.y - v.y;
        return dx * dx + dy * dy;
    }

    /**
     * Returns x % f, y % f
     * @param {number} f
     * @returns {Vector} new vector
     */
    modScalar(f) {
        return new Vector(Object(_utils__WEBPACK_IMPORTED_MODULE_1__["safeModulo"])(this.x, f), Object(_utils__WEBPACK_IMPORTED_MODULE_1__["safeModulo"])(this.y, f));
    }

    /**
     * Computes and returns the center between both points
     * @param {Vector} v
     * @returns {Vector}
     */
    centerPoint(v) {
        const cx = this.x + v.x;
        const cy = this.y + v.y;
        return new Vector(cx / 2, cy / 2);
    }

    /**
     * Computes componentwise floor and returns a new vector
     * @returns {Vector}
     */
    floor() {
        return new Vector(Math.floor(this.x), Math.floor(this.y));
    }

    /**
     * Computes componentwise ceil and returns a new vector
     * @returns {Vector}
     */
    ceil() {
        return new Vector(Math.ceil(this.x), Math.ceil(this.y));
    }

    /**
     * Computes componentwise round and return a new vector
     * @returns {Vector}
     */
    round() {
        return new Vector(Math.round(this.x), Math.round(this.y));
    }

    /**
     * Converts this vector from world to tile space and return a new vector
     * @returns {Vector}
     */
    toTileSpace() {
        return new Vector(Math.floor(this.x / tileSize), Math.floor(this.y / tileSize));
    }

    /**
     * Converts this vector from world to street space and return a new vector
     * @returns {Vector}
     */
    toStreetSpace() {
        return new Vector(Math.floor(this.x / halfTileSize + 0.25), Math.floor(this.y / halfTileSize + 0.25));
    }

    /**
     * Converts this vector to world space and return a new vector
     * @returns {Vector}
     */
    toWorldSpace() {
        return new Vector(this.x * tileSize, this.y * tileSize);
    }

    /**
     * Converts this vector to world space and return a new vector
     * @returns {Vector}
     */
    toWorldSpaceCenterOfTile() {
        return new Vector(this.x * tileSize + halfTileSize, this.y * tileSize + halfTileSize);
    }

    /**
     * Converts the top left tile position of this vector
     * @returns {Vector}
     */
    snapWorldToTile() {
        return new Vector(Math.floor(this.x / tileSize) * tileSize, Math.floor(this.y / tileSize) * tileSize);
    }

    /**
     * Normalizes the vector, dividing by the length(), and return a new vector
     * @returns {Vector}
     */
    normalize() {
        const len = Math.max(1e-5, Math.hypot(this.x, this.y));
        return new Vector(this.x / len, this.y / len);
    }

    /**
     * Normalizes the vector, dividing by the length(), and return a new vector
     * @returns {Vector}
     */
    normalizeIfGreaterOne() {
        const len = Math.max(1, Math.hypot(this.x, this.y));
        return new Vector(this.x / len, this.y / len);
    }

    /**
     * Returns the normalized vector to the other point
     * @param {Vector} v
     * @returns {Vector}
     */
    normalizedDirection(v) {
        const dx = v.x - this.x;
        const dy = v.y - this.y;
        const len = Math.max(1e-5, Math.hypot(dx, dy));
        return new Vector(dx / len, dy / len);
    }

    /**
     * Returns a perpendicular vector
     * @returns {Vector}
     */
    findPerpendicular() {
        return new Vector(-this.y, this.x);
    }

    /**
     * Returns the unnormalized direction to the other point
     * @param {Vector} v
     * @returns {Vector}
     */
    direction(v) {
        return new Vector(v.x - this.x, v.y - this.y);
    }

    /**
     * Returns a string representation of the vector
     * @returns {string}
     */
    toString() {
        return this.x + "," + this.y;
    }

    /**
     * Compares both vectors for exact equality. Does not do an epsilon compare
     * @param {Vector} v
     * @returns {Boolean}
     */
    equals(v) {
        return this.x === v.x && this.y === v.y;
    }

    /**
     * Rotates this vector
     * @param {number} angle
     * @returns {Vector} new vector
     */
    rotated(angle) {
        const sin = Math.sin(angle);
        const cos = Math.cos(angle);
        return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
    }

    /**
     * Rotates this vector
     * @param {number} angle
     * @returns {Vector} this vector
     */
    rotateInplaceFastMultipleOf90(angle) {
        // const sin = Math.sin(angle);
        // const cos = Math.cos(angle);
        // let sin = 0, cos = 1;
        window.assert(angle >= 0 && angle <= 360, "Invalid angle, please clamp first: " + angle);

        switch (angle) {
            case 0:
            case 360: {
                return this;
            }
            case 90: {
                // sin = 1;
                // cos = 0;

                const x = this.x;
                this.x = -this.y;
                this.y = x;
                return this;
            }
            case 180: {
                // sin = 0
                // cos = -1
                this.x = -this.x;
                this.y = -this.y;
                return this;
            }
            case 270: {
                // sin = -1
                // cos = 0
                const x = this.x;
                this.x = this.y;
                this.y = -x;
                return this;
            }
            default: {
                window.assert(false, "Invalid fast inplace rotation: " + angle);
                return this;
            }
        }
        // return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
    }

    /**
     * Rotates this vector
     * @param {number} angle
     * @returns {Vector} new vector
     */
    rotateFastMultipleOf90(angle) {
        window.assert(angle >= 0 && angle <= 360, "Invalid angle, please clamp first: " + angle);

        switch (angle) {
            case 360:
            case 0: {
                return new Vector(this.x, this.y);
            }
            case 90: {
                return new Vector(-this.y, this.x);
            }
            case 180: {
                return new Vector(-this.x, -this.y);
            }
            case 270: {
                return new Vector(this.y, -this.x);
            }
            default: {
                window.assert(false, "Invalid fast inplace rotation: " + angle);
                return new Vector();
            }
        }
    }

    /**
     * Helper method to rotate a direction
     * @param {enumDirection} direction
     * @param {number} angle
     * @returns {enumDirection}
     */
    static transformDirectionFromMultipleOf90(direction, angle) {
        if (angle === 0 || angle === 360) {
            return direction;
        }
        window.assert(angle >= 0 && angle <= 360, "Invalid angle: " + angle);
        switch (direction) {
            case enumDirection.top: {
                switch (angle) {
                    case 90:
                        return enumDirection.right;
                    case 180:
                        return enumDirection.bottom;
                    case 270:
                        return enumDirection.left;
                    default:
                        window.assert(false, "Invalid angle: " + angle);
                        return;
                }
            }

            case enumDirection.right: {
                switch (angle) {
                    case 90:
                        return enumDirection.bottom;
                    case 180:
                        return enumDirection.left;
                    case 270:
                        return enumDirection.top;
                    default:
                        window.assert(false, "Invalid angle: " + angle);
                        return;
                }
            }

            case enumDirection.bottom: {
                switch (angle) {
                    case 90:
                        return enumDirection.left;
                    case 180:
                        return enumDirection.top;
                    case 270:
                        return enumDirection.right;
                    default:
                        window.assert(false, "Invalid angle: " + angle);
                        return;
                }
            }

            case enumDirection.left: {
                switch (angle) {
                    case 90:
                        return enumDirection.top;
                    case 180:
                        return enumDirection.right;
                    case 270:
                        return enumDirection.bottom;
                    default:
                        window.assert(false, "Invalid angle: " + angle);
                        return;
                }
            }
            default:
                window.assert(false, "Invalid angle: " + angle);
                return;
        }
    }

    /**
     * Compares both vectors for epsilon equality
     * @param {Vector} v
     * @returns {Boolean}
     */
    equalsEpsilon(v, epsilon = 1e-5) {
        return Math.abs(this.x - v.x) < 1e-5 && Math.abs(this.y - v.y) < epsilon;
    }

    /**
     * Returns the angle
     * @returns {number} 0 .. 2 PI
     */
    angle() {
        return Math.atan2(this.y, this.x) + Math.PI / 2;
    }

    /**
     * Serializes the vector to a string
     * @returns {string}
     */
    serializeTile() {
        return String.fromCharCode(33 + this.x) + String.fromCharCode(33 + this.y);
    }

    /**
     * Creates a simple representation of the vector
     */
    serializeSimple() {
        return { x: this.x, y: this.y };
    }

    /**
     * @returns {number}
     */
    serializeTileToInt() {
        return this.x + this.y * 256;
    }

    /**
     *
     * @param {number} i
     * @returns {Vector}
     */
    static deserializeTileFromInt(i) {
        const x = i % 256;
        const y = Math.floor(i / 256);
        return new Vector(x, y);
    }

    /**
     * Deserializes a vector from a string
     * @param {string} s
     * @returns {Vector}
     */
    static deserializeTile(s) {
        return new Vector(s.charCodeAt(0) - 33, s.charCodeAt(1) - 33);
    }

    /**
     * Deserializes a vector from a serialized json object
     * @param {object} obj
     * @returns {Vector}
     */
    static fromSerializedObject(obj) {
        if (obj) {
            return new Vector(obj.x || 0, obj.y || 0);
        }
    }
}

/**
 * Interpolates two vectors, for a = 0, returns v1 and for a = 1 return v2, otherwise interpolate
 * @param {Vector} v1
 * @param {Vector} v2
 * @param {number} a
 */
function mixVector(v1, v2, a) {
    return new Vector(v1.x * (1 - a) + v2.x * a, v1.y * (1 - a) + v2.y * a);
}

/**
 * Mapping from string direction to actual vector
 * @enum {Vector}
 */
const enumDirectionToVector = {
    top: new Vector(0, -1),
    right: new Vector(1, 0),
    bottom: new Vector(0, 1),
    left: new Vector(-1, 0),
};


/***/ }),

/***/ "./src/js/game/automatic_save.js":
/*!***************************************!*\
  !*** ./src/js/game/automatic_save.js ***!
  \***************************************/
/*! exports provided: enumSavePriority, AutomaticSave */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumSavePriority", function() { return enumSavePriority; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AutomaticSave", function() { return AutomaticSave; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");




// How important it is that a savegame is created
/**
 * @enum {number}
 */
const enumSavePriority = {
    regular: 2,
    asap: 100,
};

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("autosave");

class AutomaticSave {
    constructor(root) {
        /** @type {GameRoot} */
        this.root = root;

        // Store the current maximum save importance
        this.saveImportance = enumSavePriority.regular;

        this.lastSaveAttempt = -1000;
    }

    setSaveImportance(importance) {
        this.saveImportance = Math.max(this.saveImportance, importance);
    }

    doSave() {
        if (false) {}

        this.root.gameState.doSave();
        this.saveImportance = enumSavePriority.regular;
    }

    update() {
        if (!this.root.gameInitialized) {
            // Bad idea
            return;
        }

        const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds();
        if (!saveInterval) {
            // Disabled
            return;
        }

        // Check when the last save was, but make sure that if it fails, we don't spam
        const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate());

        const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0;
        let shouldSave = false;

        switch (this.saveImportance) {
            case enumSavePriority.asap:
                // High always should save
                shouldSave = true;
                break;

            case enumSavePriority.regular:
                // Could determine if there is a good / bad point here
                shouldSave = secondsSinceLastSave > saveInterval;
                break;

            default:
                window.assert(false, "Unknown save prio: " + this.saveImportance);
                break;
        }
        if (shouldSave) {
            logger.log("Saving automatically");
            this.lastSaveAttempt = Date.now();
            this.doSave();
        }
    }
}


/***/ }),

/***/ "./src/js/game/base_item.js":
/*!**********************************!*\
  !*** ./src/js/game/base_item.js ***!
  \**********************************/
/*! exports provided: itemTypes, BaseItem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "itemTypes", function() { return itemTypes; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseItem", function() { return BaseItem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");




/** @type {ItemType[]} **/
const itemTypes = ["shape", "color", "boolean"];

/**
 * Class for items on belts etc. Not an entity for performance reasons
 */
class BaseItem extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__["BasicSerializableObject"] {
    constructor() {
        super();
    }

    static getId() {
        return "base_item";
    }

    /** @returns {object} */
    static getSchema() {
        return {};
    }

    /** @returns {ItemType} **/
    getItemType() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "shape";
    }

    /**
     * Returns a string id of the item
     * @returns {string}
     */
    getAsCopyableKey() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "";
    }

    /**
     * Returns if the item equals the other itme
     * @param {BaseItem} other
     * @returns {boolean}
     */
    equals(other) {
        if (this.getItemType() !== other.getItemType()) {
            return false;
        }
        return this.equalsImpl(other);
    }

    /**
     * Override for custom comparison
     * @abstract
     * @param {BaseItem} other
     * @returns {boolean}
     */
    equalsImpl(other) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }

    /**
     * Draws the item to a canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} size
     */
    drawFullSizeOnCanvas(context, size) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Draws the item at the given position
     * @param {number} x
     * @param {number} y
     * @param {DrawParameters} parameters
     * @param {number=} diameter
     */
    drawItemCenteredClipped(x, y, parameters, diameter = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter) {
        if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) {
            this.drawItemCenteredImpl(x, y, parameters, diameter);
        }
    }

    /**
     * INTERNAL
     * @param {number} x
     * @param {number} y
     * @param {DrawParameters} parameters
     * @param {number=} diameter
     */
    drawItemCenteredImpl(x, y, parameters, diameter = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    getBackgroundColorAsResource() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "";
    }
}


/***/ }),

/***/ "./src/js/game/belt_path.js":
/*!**********************************!*\
  !*** ./src/js/game/belt_path.js ***!
  \**********************************/
/*! exports provided: BeltPath */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltPath", function() { return BeltPath; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./item_resolver */ "./src/js/game/item_resolver.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");












const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("belt_path");

// Helpers for more semantic access into interleaved arrays
const _nextDistance = 0;
const _item = 1;

const DEBUG =  false && false;

/**
 * Stores a path of belts, used for optimizing performance
 */
class BeltPath extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["BasicSerializableObject"] {
    static getId() {
        return "BeltPath";
    }

    static getSchema() {
        return {
            entityPath: _savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["types"].array(_savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["types"].entity),
            items: _savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["types"].array(_savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["types"].pair(_savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["types"].ufloat, _item_resolver__WEBPACK_IMPORTED_MODULE_9__["typeItemSingleton"])),
            spacingToFirstItem: _savegame_serialization__WEBPACK_IMPORTED_MODULE_6__["types"].ufloat,
        };
    }

    /**
     * Creates a path from a serialized object
     * @param {GameRoot} root
     * @param {Object} data
     * @returns {BeltPath|string}
     */
    static fromSerialized(root, data) {
        // Create fake object which looks like a belt path but skips the constructor
        const fakeObject = /** @type {BeltPath} */ (Object.create(BeltPath.prototype));
        fakeObject.root = root;

        // Deserialize the data
        const errorCodeDeserialize = fakeObject.deserialize(data);
        if (errorCodeDeserialize) {
            return errorCodeDeserialize;
        }

        // Compute other properties
        fakeObject.init(false);

        return fakeObject;
    }

    /**
     * @param {GameRoot} root
     * @param {Array<Entity>} entityPath
     */
    constructor(root, entityPath) {
        super();
        this.root = root;

        window.assert(entityPath.length > 0, "invalid entity path");
        this.entityPath = entityPath;

        /**
         * Stores the items sorted, and their distance to the previous item (or start)
         * Layout: [distanceToNext, item]
         * @type {Array<[number, BaseItem]>}
         */
        this.items = [];

        /**
         * Stores the spacing to the first item
         */

        this.init();

        if (false) {}
    }
    /**
     * Initializes the path by computing the properties which are not saved
     * @param {boolean} computeSpacing Whether to also compute the spacing
     */
    init(computeSpacing = true) {
        this.onPathChanged();

        this.totalLength = this.computeTotalLength();

        if (computeSpacing) {
            this.spacingToFirstItem = this.totalLength;
        }

        /**
         * Current bounds of this path
         * @type {Rectangle}
         */
        this.worldBounds = this.computeBounds();

        // Connect the belts
        for (let i = 0; i < this.entityPath.length; ++i) {
            this.entityPath[i].components.Belt.assignedPath = this;
        }
    }

    /**
     * Returns whether this path can accept a new item
     * @returns {boolean}
     */
    canAcceptItem() {
        return this.spacingToFirstItem >= _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;
    }

    /**
     * Tries to accept the item
     * @param {BaseItem} item
     */
    tryAcceptItem(item) {
        if (this.spacingToFirstItem >= _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts) {
            // So, since we already need one tick to accept this item we will add this directly.
            const beltProgressPerTick =
                this.root.hubGoals.getBeltBaseSpeed() *
                this.root.dynamicTickrate.deltaSeconds *
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;

            // First, compute how much progress we can make *at max*
            const maxProgress = Math.max(0, this.spacingToFirstItem - _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts);
            const initialProgress = Math.min(maxProgress, beltProgressPerTick);

            this.items.unshift([this.spacingToFirstItem - initialProgress, item]);
            this.spacingToFirstItem = initialProgress;

            if (false) {}

            return true;
        }
        return false;
    }

    /**
     * SLOW / Tries to find the item closest to the given tile
     * @param {Vector} tile
     * @returns {BaseItem|null}
     */
    findItemAtTile(tile) {
        // @TODO: This breaks color blind mode otherwise
        return null;
    }

    /**
     * Computes the tile bounds of the path
     * @returns {Rectangle}
     */
    computeBounds() {
        let bounds = this.entityPath[0].components.StaticMapEntity.getTileSpaceBounds();
        for (let i = 1; i < this.entityPath.length; ++i) {
            const staticComp = this.entityPath[i].components.StaticMapEntity;
            const otherBounds = staticComp.getTileSpaceBounds();
            bounds = bounds.getUnion(otherBounds);
        }
        return bounds.allScaled(_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize);
    }

    /**
     * Recomputes cache variables once the path was changed
     */
    onPathChanged() {
        this.acceptorTarget = this.computeAcceptingEntityAndSlot();

        /**
         * How many items past the first item are compressed
         */
        this.numCompressedItemsAfterFirstItem = 0;
    }

    /**
     * Called by the belt system when the surroundings changed
     */
    onSurroundingsChanged() {
        this.onPathChanged();
    }

    /**
     * Finds the entity which accepts our items
     * @param {boolean=} debug_Silent Whether debug output should be silent
     * @return {{ entity: Entity, slot: number, direction?: enumDirection }}
     */
    computeAcceptingEntityAndSlot(debug_Silent = false) {
        DEBUG && !debug_Silent && logger.log("Recomputing acceptor target");

        const lastEntity = this.entityPath[this.entityPath.length - 1];
        const lastStatic = lastEntity.components.StaticMapEntity;
        const lastBeltComp = lastEntity.components.Belt;

        // Figure out where and into which direction we eject items
        const ejectSlotWsTile = lastStatic.localTileToWorld(new _core_vector__WEBPACK_IMPORTED_MODULE_5__["Vector"](0, 0));
        const ejectSlotWsDirection = lastStatic.localDirectionToWorld(lastBeltComp.direction);
        const ejectSlotWsDirectionVector = _core_vector__WEBPACK_IMPORTED_MODULE_5__["enumDirectionToVector"][ejectSlotWsDirection];
        const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);

        // Try to find the given acceptor component to take the item
        const targetEntity = this.root.map.getLayerContentXY(
            ejectSlotTargetWsTile.x,
            ejectSlotTargetWsTile.y,
            "regular"
        );

        if (targetEntity) {
            DEBUG && !debug_Silent && logger.log("  Found target entity", targetEntity.uid);
            const targetStaticComp = targetEntity.components.StaticMapEntity;
            const targetBeltComp = targetEntity.components.Belt;

            // Check for belts (special case)
            if (targetBeltComp) {
                const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(_core_vector__WEBPACK_IMPORTED_MODULE_5__["enumDirection"].top);
                DEBUG &&
                    !debug_Silent &&
                    logger.log(
                        "  Entity is accepting items from",
                        ejectSlotWsDirection,
                        "vs",
                        beltAcceptingDirection,
                        "Rotation:",
                        targetStaticComp.rotation
                    );
                if (ejectSlotWsDirection === beltAcceptingDirection) {
                    return {
                        entity: targetEntity,
                        direction: null,
                        slot: 0,
                    };
                }
            }

            // Check for item acceptors
            const targetAcceptorComp = targetEntity.components.ItemAcceptor;
            if (!targetAcceptorComp) {
                // Entity doesn't accept items
                return;
            }

            const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection);
            const matchingSlot = targetAcceptorComp.findMatchingSlot(
                targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
                ejectingDirection
            );

            if (!matchingSlot) {
                // No matching slot found
                return;
            }

            return {
                entity: targetEntity,
                slot: matchingSlot.index,
                direction: _core_vector__WEBPACK_IMPORTED_MODULE_5__["enumInvertedDirections"][ejectingDirection],
            };
        }
    }

    // Following code will be compiled out outside of dev versions
    /* dev:start */

    /**
     * Helper to throw an error on mismatch
     * @param {string} change
     * @param {Array<any>} reason
     */
    debug_failIntegrity(change, ...reason) {
        throw new Error("belt path invalid (" + change + "): " + reason.map(i => "" + i).join(" "));
    }

    /**
     * Checks if this path is valid
     */
    debug_checkIntegrity(currentChange = "change") {
        const fail = (...args) => this.debug_failIntegrity(currentChange, ...args);

        // Check for empty path
        if (this.entityPath.length === 0) {
            return fail("Belt path is empty");
        }

        // Check for mismatching length
        const totalLength = this.computeTotalLength();
        if (!Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["epsilonCompare"])(this.totalLength, totalLength, 0.01)) {
            return this.debug_failIntegrity(
                currentChange,
                "Total length mismatch, stored =",
                this.totalLength,
                "but correct is",
                totalLength
            );
        }

        // Check for misconnected entities
        for (let i = 0; i < this.entityPath.length - 1; ++i) {
            const entity = this.entityPath[i];
            if (entity.destroyed) {
                return fail("Reference to destroyed entity " + entity.uid);
            }

            const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity);
            if (!followUp) {
                return fail(
                    "Follow up entity for the",
                    i,
                    "-th entity (total length",
                    this.entityPath.length,
                    ") was null!"
                );
            }
            if (followUp !== this.entityPath[i + 1]) {
                return fail(
                    "Follow up entity mismatch, stored is",
                    this.entityPath[i + 1].uid,
                    "but real one is",
                    followUp.uid
                );
            }
            if (entity.components.Belt.assignedPath !== this) {
                return fail(
                    "Entity with uid",
                    entity.uid,
                    "doesn't have this path assigned, but this path contains the entity."
                );
            }
        }

        // Check spacing
        if (this.spacingToFirstItem > this.totalLength + 0.005) {
            return fail(
                currentChange,
                "spacing to first item (",
                this.spacingToFirstItem,
                ") is greater than total length (",
                this.totalLength,
                ")"
            );
        }

        // Check distance if empty
        if (this.items.length === 0 && !Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["epsilonCompare"])(this.spacingToFirstItem, this.totalLength, 0.01)) {
            return fail(
                currentChange,
                "Path is empty but spacing to first item (",
                this.spacingToFirstItem,
                ") does not equal total length (",
                this.totalLength,
                ")"
            );
        }

        // Check items etc
        let currentPos = this.spacingToFirstItem;
        for (let i = 0; i < this.items.length; ++i) {
            const item = this.items[i];

            if (item[_nextDistance] < 0 || item[_nextDistance] > this.totalLength + 0.02) {
                return fail(
                    "Item has invalid offset to next item: ",
                    item[_nextDistance],
                    "(total length:",
                    this.totalLength,
                    ")"
                );
            }

            currentPos += item[_nextDistance];
        }

        // Check the total sum matches
        if (!Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["epsilonCompare"])(currentPos, this.totalLength, 0.01)) {
            return fail(
                "total sum (",
                currentPos,
                ") of first item spacing (",
                this.spacingToFirstItem,
                ") and items does not match total length (",
                this.totalLength,
                ") -> items: " + this.items.map(i => i[_nextDistance]).join("|")
            );
        }

        // Check bounds
        const actualBounds = this.computeBounds();
        if (!actualBounds.equalsEpsilon(this.worldBounds, 0.01)) {
            return fail("Bounds are stale");
        }

        // Check acceptor
        const acceptor = this.computeAcceptingEntityAndSlot(true);
        if (!!acceptor !== !!this.acceptorTarget) {
            return fail("Acceptor target mismatch, acceptor", !!acceptor, "vs stored", !!this.acceptorTarget);
        }

        if (acceptor) {
            if (this.acceptorTarget.entity !== acceptor.entity) {
                return fail(
                    "Mismatching entity on acceptor target:",
                    acceptor.entity.uid,
                    "vs",
                    this.acceptorTarget.entity.uid
                );
            }

            if (this.acceptorTarget.slot !== acceptor.slot) {
                return fail(
                    "Mismatching entity on acceptor target:",
                    acceptor.slot,
                    "vs stored",
                    this.acceptorTarget.slot
                );
            }

            if (this.acceptorTarget.direction !== acceptor.direction) {
                return fail(
                    "Mismatching direction on acceptor target:",
                    acceptor.direction,
                    "vs stored",
                    this.acceptorTarget.direction
                );
            }
        }

        // Check first nonzero offset
        let firstNonzero = 0;
        for (let i = this.items.length - 2; i >= 0; --i) {
            if (this.items[i][_nextDistance] < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts + 1e-5) {
                ++firstNonzero;
            } else {
                break;
            }
        }

        // Should warn, but this check isn't actually accurate
        // if (firstNonzero !== this.numCompressedItemsAfterFirstItem) {
        //     console.warn(
        //         "First nonzero index is " +
        //             firstNonzero +
        //             " but stored is " +
        //             this.numCompressedItemsAfterFirstItem
        //     );
        // }
    }

    /* dev:end */

    /**
     * Extends the belt path by the given belt
     * @param {Entity} entity
     */
    extendOnEnd(entity) {
        DEBUG && logger.log("Extending belt path by entity at", entity.components.StaticMapEntity.origin);

        const beltComp = entity.components.Belt;

        // Append the entity
        this.entityPath.push(entity);
        this.onPathChanged();

        // Extend the path length
        const additionalLength = beltComp.getEffectiveLengthTiles();
        this.totalLength += additionalLength;
        DEBUG && logger.log("  Extended total length by", additionalLength, "to", this.totalLength);

        // If we have no item, just update the distance to the first item
        if (this.items.length === 0) {
            this.spacingToFirstItem = this.totalLength;
            DEBUG && logger.log("  Extended spacing to first to", this.totalLength, "(= total length)");
        } else {
            // Otherwise, update the next-distance of the last item
            const lastItem = this.items[this.items.length - 1];
            DEBUG &&
                logger.log(
                    "  Extended spacing of last item from",
                    lastItem[_nextDistance],
                    "to",
                    lastItem[_nextDistance] + additionalLength
                );
            lastItem[_nextDistance] += additionalLength;
        }

        // Assign reference
        beltComp.assignedPath = this;

        // Update bounds
        this.worldBounds = this.computeBounds();

        if (false) {}
    }

    /**
     * Extends the path with the given entity on the beginning
     * @param {Entity} entity
     */
    extendOnBeginning(entity) {
        const beltComp = entity.components.Belt;

        DEBUG && logger.log("Extending the path on the beginning");

        // All items on that belt are simply lost (for now)

        const length = beltComp.getEffectiveLengthTiles();

        // Extend the length of this path
        this.totalLength += length;

        // Simply adjust the first item spacing cuz we have no items contained
        this.spacingToFirstItem += length;

        // Set handles and append entity
        beltComp.assignedPath = this;
        this.entityPath.unshift(entity);
        this.onPathChanged();

        // Update bounds
        this.worldBounds = this.computeBounds();

        if (false) {}
    }

    /**
     * Returns if the given entity is the end entity of the path
     * @param {Entity} entity
     * @returns {boolean}
     */
    isEndEntity(entity) {
        return this.entityPath[this.entityPath.length - 1] === entity;
    }

    /**
     * Returns if the given entity is the start entity of the path
     * @param {Entity} entity
     * @returns {boolean}
     */
    isStartEntity(entity) {
        return this.entityPath[0] === entity;
    }

    /**
     * Splits this path at the given entity by removing it, and
     * returning the new secondary paht
     * @param {Entity} entity
     * @returns {BeltPath}
     */
    deleteEntityOnPathSplitIntoTwo(entity) {
        DEBUG && logger.log("Splitting path at entity", entity.components.StaticMapEntity.origin);

        // First, find where the current path ends
        const beltComp = entity.components.Belt;
        beltComp.assignedPath = null;

        const entityLength = beltComp.getEffectiveLengthTiles();
        window.assert(this.entityPath.indexOf(entity) >= 0, "Entity not contained for split");
        window.assert(this.entityPath.indexOf(entity) !== 0, "Entity is first");
        window.assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last");

        let firstPathEntityCount = 0;
        let firstPathLength = 0;
        let firstPathEndEntity = null;

        for (let i = 0; i < this.entityPath.length; ++i) {
            const otherEntity = this.entityPath[i];
            if (otherEntity === entity) {
                DEBUG && logger.log("Found entity at", i, "of length", firstPathLength);
                break;
            }

            ++firstPathEntityCount;
            firstPathEndEntity = otherEntity;
            firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles();
        }

        DEBUG &&
            logger.log(
                "First path ends at",
                firstPathLength,
                "and entity",
                firstPathEndEntity.components.StaticMapEntity.origin,
                "and has",
                firstPathEntityCount,
                "entities"
            );

        // Compute length of second path
        const secondPathLength = this.totalLength - firstPathLength - entityLength;
        const secondPathStart = firstPathLength + entityLength;
        const secondEntities = this.entityPath.splice(firstPathEntityCount + 1);
        DEBUG &&
            logger.log(
                "Second path starts at",
                secondPathStart,
                "and has a length of ",
                secondPathLength,
                "with",
                secondEntities.length,
                "entities"
            );

        // Remove the last item
        this.entityPath.pop();

        DEBUG && logger.log("Splitting", this.items.length, "items");
        DEBUG &&
            logger.log(
                "Old items are",
                this.items.map(i => i[_nextDistance])
            );

        // Create second path
        const secondPath = new BeltPath(this.root, secondEntities);

        // Remove all items which are no longer relevant and transfer them to the second path
        let itemPos = this.spacingToFirstItem;
        for (let i = 0; i < this.items.length; ++i) {
            const item = this.items[i];
            const distanceToNext = item[_nextDistance];

            DEBUG && logger.log("  Checking item at", itemPos, "with distance of", distanceToNext, "to next");

            // Check if this item is past the first path
            if (itemPos >= firstPathLength) {
                // Remove it from the first path
                this.items.splice(i, 1);
                i -= 1;
                DEBUG &&
                    logger.log("     Removed item from first path since its no longer contained @", itemPos);

                // Check if its on the second path (otherwise its on the removed belt and simply lost)
                if (itemPos >= secondPathStart) {
                    // Put item on second path
                    secondPath.items.push([distanceToNext, item[_item]]);
                    DEBUG &&
                        logger.log(
                            "     Put item to second path @",
                            itemPos,
                            "with distance to next =",
                            distanceToNext
                        );

                    // If it was the first item, adjust the distance to the first item
                    if (secondPath.items.length === 1) {
                        DEBUG && logger.log("       Sinc it was the first, set sapcing of first to", itemPos);
                        secondPath.spacingToFirstItem = itemPos - secondPathStart;
                    }
                } else {
                    DEBUG && logger.log("    Item was on the removed belt, so its gone - forever!");
                }
            } else {
                // Seems this item is on the first path (so all good), so just make sure it doesn't
                // have a nextDistance which is bigger than the total path length
                const clampedDistanceToNext = Math.min(itemPos + distanceToNext, firstPathLength) - itemPos;
                if (clampedDistanceToNext < distanceToNext) {
                    DEBUG &&
                        logger.log(
                            "Correcting next distance (first path) from",
                            distanceToNext,
                            "to",
                            clampedDistanceToNext
                        );
                    item[_nextDistance] = clampedDistanceToNext;
                }
            }

            // Advance items
            itemPos += distanceToNext;
        }

        DEBUG &&
            logger.log(
                "New items are",
                this.items.map(i => i[_nextDistance])
            );

        DEBUG &&
            logger.log(
                "And second path items are",
                secondPath.items.map(i => i[_nextDistance])
            );

        // Adjust our total length
        this.totalLength = firstPathLength;

        // Make sure that if we are empty, we set our first distance properly
        if (this.items.length === 0) {
            this.spacingToFirstItem = this.totalLength;
        }

        this.onPathChanged();
        secondPath.onPathChanged();

        // Update bounds
        this.worldBounds = this.computeBounds();

        if (false) {}

        return secondPath;
    }

    /**
     * Deletes the last entity
     * @param {Entity} entity
     */
    deleteEntityOnEnd(entity) {
        window.assert(
            this.entityPath[this.entityPath.length - 1] === entity,
            "Not actually the last entity (instead " + this.entityPath.indexOf(entity) + ")"
        );

        // Ok, first remove the entity
        const beltComp = entity.components.Belt;
        const beltLength = beltComp.getEffectiveLengthTiles();

        DEBUG &&
            logger.log(
                "Deleting last entity on path with length",
                this.entityPath.length,
                "(reducing",
                this.totalLength,
                " by",
                beltLength,
                ")"
            );
        this.totalLength -= beltLength;
        this.entityPath.pop();
        this.onPathChanged();

        DEBUG &&
            logger.log(
                "  New path has length of",
                this.totalLength,
                "with",
                this.entityPath.length,
                "entities"
            );

        // This is just for sanity
        beltComp.assignedPath = null;

        // Clean up items
        if (this.items.length === 0) {
            // Simple case with no items, just update the first item spacing
            this.spacingToFirstItem = this.totalLength;
        } else {
            // Ok, make sure we simply drop all items which are no longer contained
            let itemOffset = this.spacingToFirstItem;
            let lastItemOffset = itemOffset;

            DEBUG && logger.log("  Adjusting", this.items.length, "items");

            for (let i = 0; i < this.items.length; ++i) {
                const item = this.items[i];

                // Get rid of items past this path
                if (itemOffset >= this.totalLength) {
                    DEBUG && logger.log("Dropping item (current index=", i, ")");
                    this.items.splice(i, 1);
                    i -= 1;
                    continue;
                }

                DEBUG && logger.log("Item", i, "is at", itemOffset, "with next offset", item[_nextDistance]);
                lastItemOffset = itemOffset;
                itemOffset += item[_nextDistance];
            }

            // If we still have an item, make sure the last item matches
            if (this.items.length > 0) {
                // We can easily compute the next distance since we know where the last item is now
                const lastDistance = this.totalLength - lastItemOffset;
                window.assert(
                    lastDistance >= 0.0,
                    "Last item distance mismatch: " +
                        lastDistance +
                        " -> Total length was " +
                        this.totalLength +
                        " and lastItemOffset was " +
                        lastItemOffset
                );

                DEBUG &&
                    logger.log(
                        "Adjusted distance of last item: it is at",
                        lastItemOffset,
                        "so it has a distance of",
                        lastDistance,
                        "to the end (",
                        this.totalLength,
                        ")"
                    );
                this.items[this.items.length - 1][_nextDistance] = lastDistance;
            } else {
                DEBUG && logger.log("  Removed all items so we'll update spacing to total length");

                // We removed all items so update our spacing
                this.spacingToFirstItem = this.totalLength;
            }
        }

        // Update bounds
        this.worldBounds = this.computeBounds();

        if (false) {}
    }

    /**
     * Deletes the entity of the start of the path
     * @see deleteEntityOnEnd
     * @param {Entity} entity
     */
    deleteEntityOnStart(entity) {
        window.assert(
            entity === this.entityPath[0],
            "Not actually the start entity (instead " + this.entityPath.indexOf(entity) + ")"
        );

        // Ok, first remove the entity
        const beltComp = entity.components.Belt;
        const beltLength = beltComp.getEffectiveLengthTiles();

        DEBUG &&
            logger.log(
                "Deleting first entity on path with length",
                this.entityPath.length,
                "(reducing",
                this.totalLength,
                " by",
                beltLength,
                ")"
            );
        this.totalLength -= beltLength;
        this.entityPath.shift();
        this.onPathChanged();

        DEBUG &&
            logger.log(
                "  New path has length of",
                this.totalLength,
                "with",
                this.entityPath.length,
                "entities"
            );

        // This is just for sanity
        beltComp.assignedPath = null;

        // Clean up items
        if (this.items.length === 0) {
            // Simple case with no items, just update the first item spacing
            this.spacingToFirstItem = this.totalLength;
        } else {
            // Simple case, we had no item on the beginning -> all good
            if (this.spacingToFirstItem >= beltLength) {
                DEBUG &&
                    logger.log(
                        "  No item on the first place, so we can just adjust the spacing (spacing=",
                        this.spacingToFirstItem,
                        ") removed =",
                        beltLength
                    );
                this.spacingToFirstItem -= beltLength;
            } else {
                // Welp, okay we need to drop all items which are < beltLength and adjust
                // the other item offsets as well

                DEBUG &&
                    logger.log(
                        "  We have at least one item in the beginning, drop those and adjust spacing (first item @",
                        this.spacingToFirstItem,
                        ") since we removed",
                        beltLength,
                        "length from path"
                    );
                DEBUG &&
                    logger.log(
                        "    Items:",
                        this.items.map(i => i[_nextDistance])
                    );

                // Find offset to first item
                let itemOffset = this.spacingToFirstItem;
                for (let i = 0; i < this.items.length; ++i) {
                    const item = this.items[i];
                    if (itemOffset <= beltLength) {
                        DEBUG &&
                            logger.log(
                                "  -> Dropping item with index",
                                i,
                                "at",
                                itemOffset,
                                "since it was on the removed belt"
                            );
                        // This item must be dropped
                        this.items.splice(i, 1);
                        i -= 1;
                        itemOffset += item[_nextDistance];
                        continue;
                    } else {
                        // This item can be kept, thus its the first we know
                        break;
                    }
                }

                if (this.items.length > 0) {
                    DEBUG &&
                        logger.log(
                            "  Offset of first non-dropped item was at:",
                            itemOffset,
                            "-> setting spacing to it (total length=",
                            this.totalLength,
                            ")"
                        );

                    this.spacingToFirstItem = itemOffset - beltLength;
                    window.assert(
                        this.spacingToFirstItem >= 0.0,
                        "Invalid spacing after delete on start: " + this.spacingToFirstItem
                    );
                } else {
                    DEBUG && logger.log("  We dropped all items, simply set spacing to total length");
                    // We dropped all items, simple one
                    this.spacingToFirstItem = this.totalLength;
                }
            }
        }

        // Update bounds
        this.worldBounds = this.computeBounds();

        if (false) {}
    }

    /**
     * Extends the path by the given other path
     * @param {BeltPath} otherPath
     */
    extendByPath(otherPath) {
        window.assert(otherPath !== this, "Circular path dependency");

        const entities = otherPath.entityPath;
        DEBUG && logger.log("Extending path by other path, starting to add entities");

        const oldLength = this.totalLength;

        DEBUG && logger.log("  Adding", entities.length, "new entities, current length =", this.totalLength);

        // First, append entities
        for (let i = 0; i < entities.length; ++i) {
            const entity = entities[i];
            const beltComp = entity.components.Belt;

            // Add to path and update references
            this.entityPath.push(entity);
            beltComp.assignedPath = this;

            // Update our length
            const additionalLength = beltComp.getEffectiveLengthTiles();
            this.totalLength += additionalLength;
        }

        DEBUG &&
            logger.log(
                "  Path is now",
                this.entityPath.length,
                "entities and has a length of",
                this.totalLength
            );

        // Now, update the distance of our last item
        if (this.items.length !== 0) {
            const lastItem = this.items[this.items.length - 1];
            lastItem[_nextDistance] += otherPath.spacingToFirstItem;
            DEBUG &&
                logger.log("  Add distance to last item, effectively being", lastItem[_nextDistance], "now");
        } else {
            // Seems we have no items, update our first item distance
            this.spacingToFirstItem = oldLength + otherPath.spacingToFirstItem;
            DEBUG &&
                logger.log(
                    "  We had no items, so our new spacing to first is old length (",
                    oldLength,
                    ") plus others spacing to first (",
                    otherPath.spacingToFirstItem,
                    ") =",
                    this.spacingToFirstItem
                );
        }

        DEBUG && logger.log("  Pushing", otherPath.items.length, "items from other path");

        // Aaand push the other paths items
        for (let i = 0; i < otherPath.items.length; ++i) {
            const item = otherPath.items[i];
            this.items.push([item[_nextDistance], item[_item]]);
        }

        // Update bounds
        this.worldBounds = this.computeBounds();

        this.onPathChanged();

        if (false) {}
    }

    /**
     * Computes the total length of the path
     * @returns {number}
     */
    computeTotalLength() {
        let length = 0;
        for (let i = 0; i < this.entityPath.length; ++i) {
            const entity = this.entityPath[i];
            length += entity.components.Belt.getEffectiveLengthTiles();
        }
        return length;
    }

    /**
     * Performs one tick
     */
    update() {
        if (false) {}

        // Divide by item spacing on belts since we use throughput and not speed
        let beltSpeed =
            this.root.hubGoals.getBeltBaseSpeed() *
            this.root.dynamicTickrate.deltaSeconds *
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;

        if (false) {}

        // Store whether this is the first item we processed, so premature
        // item ejection is available
        let isFirstItemProcessed = true;

        // Store how much velocity (strictly its distance, not velocity) we have to distribute over all items
        let remainingVelocity = beltSpeed;

        // Store the last item we processed, so we can skip clashed ones
        let lastItemProcessed;

        for (lastItemProcessed = this.items.length - 1; lastItemProcessed >= 0; --lastItemProcessed) {
            const nextDistanceAndItem = this.items[lastItemProcessed];

            // Compute how much spacing we need at least
            const minimumSpacing =
                lastItemProcessed === this.items.length - 1 ? 0 : _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;

            // Compute how much we can advance
            const clampedProgress = Math.max(
                0,
                Math.min(remainingVelocity, nextDistanceAndItem[_nextDistance] - minimumSpacing)
            );

            // Reduce our velocity by the amount we consumed
            remainingVelocity -= clampedProgress;

            // Reduce the spacing
            nextDistanceAndItem[_nextDistance] -= clampedProgress;

            // Advance all items behind by the progress we made
            this.spacingToFirstItem += clampedProgress;

            // If the last item can be ejected, eject it and reduce the spacing, because otherwise
            // we lose velocity
            if (isFirstItemProcessed && nextDistanceAndItem[_nextDistance] < 1e-7) {
                // Store how much velocity we "lost" because we bumped the item to the end of the
                // belt but couldn't move it any farther. We need this to tell the item acceptor
                // animation to start a tad later, so everything matches up. Yes I'm a perfectionist.
                const excessVelocity = beltSpeed - clampedProgress;

                // Try to directly get rid of the item
                if (this.tryHandOverItem(nextDistanceAndItem[_item], excessVelocity)) {
                    this.items.pop();

                    const itemBehind = this.items[lastItemProcessed - 1];
                    if (itemBehind && this.numCompressedItemsAfterFirstItem > 0) {
                        // So, with the next tick we will skip this item, but it actually has the potential
                        // to process farther -> If we don't advance here, we loose a tiny bit of progress
                        // every tick which causes the belt to be slower than it actually is.
                        // Also see #999
                        const fixupProgress = Math.max(
                            0,
                            Math.min(remainingVelocity, itemBehind[_nextDistance])
                        );

                        // See above
                        itemBehind[_nextDistance] -= fixupProgress;
                        remainingVelocity -= fixupProgress;
                        this.spacingToFirstItem += fixupProgress;
                    }

                    // Reduce the number of compressed items since the first item no longer exists
                    this.numCompressedItemsAfterFirstItem = Math.max(
                        0,
                        this.numCompressedItemsAfterFirstItem - 1
                    );
                }
            }

            if (isFirstItemProcessed) {
                // Skip N null items after first items
                lastItemProcessed -= this.numCompressedItemsAfterFirstItem;
            }

            isFirstItemProcessed = false;
            if (remainingVelocity < 1e-7) {
                break;
            }
        }

        // Compute compressed item count
        this.numCompressedItemsAfterFirstItem = Math.max(
            0,
            this.numCompressedItemsAfterFirstItem,
            this.items.length - 2 - lastItemProcessed
        );

        // Check if we have an item which is ready to be emitted
        const lastItem = this.items[this.items.length - 1];
        if (lastItem && lastItem[_nextDistance] === 0 && this.acceptorTarget) {
            if (this.tryHandOverItem(lastItem[_item])) {
                this.items.pop();
                this.numCompressedItemsAfterFirstItem = Math.max(
                    0,
                    this.numCompressedItemsAfterFirstItem - 1
                );
            }
        }

        if (false) {}
    }

    /**
     * Tries to hand over the item to the end entity
     * @param {BaseItem} item
     */
    tryHandOverItem(item, remainingProgress = 0.0) {
        if (!this.acceptorTarget) {
            return;
        }

        const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor;

        // Check if the acceptor has a filter for example
        if (targetAcceptorComp && !targetAcceptorComp.canAcceptItem(this.acceptorTarget.slot, item)) {
            // Well, this item is not accepted
            return false;
        }

        // Try to pass over
        if (
            this.root.systemMgr.systems.itemEjector.tryPassOverItem(
                item,
                this.acceptorTarget.entity,
                this.acceptorTarget.slot
            )
        ) {
            // Trigger animation on the acceptor comp
            const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor;
            if (targetAcceptorComp) {
                if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
                    targetAcceptorComp.onItemAccepted(
                        this.acceptorTarget.slot,
                        this.acceptorTarget.direction,
                        item,
                        remainingProgress
                    );
                }
            }

            return true;
        }

        return false;
    }

    /**
     * Computes a world space position from the given progress
     * @param {number} progress
     * @returns {Vector}
     */
    computePositionFromProgress(progress) {
        let currentLength = 0;

        // floating point issues ..
        window.assert(progress <= this.totalLength + 0.02, "Progress too big: " + progress);

        for (let i = 0; i < this.entityPath.length; ++i) {
            const beltComp = this.entityPath[i].components.Belt;
            const localLength = beltComp.getEffectiveLengthTiles();

            if (currentLength + localLength >= progress || i === this.entityPath.length - 1) {
                // Min required here due to floating point issues
                const localProgress = Math.min(1.0, progress - currentLength);

                window.assert(localProgress >= 0.0, "Invalid local progress: " + localProgress);
                const localSpace = beltComp.transformBeltToLocalSpace(localProgress);
                return this.entityPath[i].components.StaticMapEntity.localTileToWorld(localSpace);
            }
            currentLength += localLength;
        }

        window.assert(false, "invalid progress: " + progress + " (max: " + this.totalLength + ")");
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    drawDebug(parameters) {
        if (!parameters.visibleRect.containsRect(this.worldBounds)) {
            return;
        }

        parameters.context.fillStyle = "#d79a25";
        parameters.context.strokeStyle = "#d79a25";
        parameters.context.beginPath();

        for (let i = 0; i < this.entityPath.length; ++i) {
            const entity = this.entityPath[i];
            const pos = entity.components.StaticMapEntity;
            const worldPos = pos.origin.toWorldSpaceCenterOfTile();

            if (i === 0) {
                parameters.context.moveTo(worldPos.x, worldPos.y);
            } else {
                parameters.context.lineTo(worldPos.x, worldPos.y);
            }
        }
        parameters.context.stroke();

        // Items
        let progress = this.spacingToFirstItem;
        for (let i = 0; i < this.items.length; ++i) {
            const nextDistanceAndItem = this.items[i];
            const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile();
            parameters.context.fillStyle = "#268e4d";
            parameters.context.beginRoundedRect(worldPos.x - 5, worldPos.y - 5, 10, 10, 3);
            parameters.context.fill();
            parameters.context.font = "6px GameFont";
            parameters.context.fillStyle = "#111";
            parameters.context.fillText(
                "" + Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["round4Digits"])(nextDistanceAndItem[_nextDistance]),
                worldPos.x + 5,
                worldPos.y + 2
            );
            progress += nextDistanceAndItem[_nextDistance];

            if (this.items.length - 1 - this.numCompressedItemsAfterFirstItem === i) {
                parameters.context.fillStyle = "red";
                parameters.context.fillRect(worldPos.x + 5, worldPos.y, 20, 3);
            }
        }

        for (let i = 0; i < this.entityPath.length; ++i) {
            const entity = this.entityPath[i];
            parameters.context.fillStyle = "#d79a25";
            const pos = entity.components.StaticMapEntity;
            const worldPos = pos.origin.toWorldSpaceCenterOfTile();
            parameters.context.beginCircle(worldPos.x, worldPos.y, i === 0 ? 5 : 3);
            parameters.context.fill();
        }

        for (let progress = 0; progress <= this.totalLength + 0.01; progress += 0.2) {
            const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile();
            parameters.context.fillStyle = "red";
            parameters.context.beginCircle(worldPos.x, worldPos.y, 1);
            parameters.context.fill();
        }

        const firstItemIndicator = this.computePositionFromProgress(
            this.spacingToFirstItem
        ).toWorldSpaceCenterOfTile();
        parameters.context.fillStyle = "purple";
        parameters.context.fillRect(firstItemIndicator.x - 3, firstItemIndicator.y - 1, 6, 2);
    }

    /**
     * Checks if this belt path should render simplified
     */
    checkIsPotatoMode() {
        // POTATO Mode: Only show items when belt is hovered
        if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
            return false;
        }

        if (this.root.currentLayer !== "regular") {
            // Not in regular layer
            return true;
        }

        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            // Mouse not registered
            return true;
        }

        const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
        const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
        if (!contents || !contents.components.Belt) {
            // Nothing below
            return true;
        }

        if (contents.components.Belt.assignedPath !== this) {
            // Not this path
            return true;
        }
        return false;
    }

    /**
     * Draws the path
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        if (!parameters.visibleRect.containsRect(this.worldBounds)) {
            return;
        }

        if (this.items.length === 0) {
            // Early out
            return;
        }

        if (this.checkIsPotatoMode()) {
            const firstItem = this.items[0];
            if (this.entityPath.length > 1 && firstItem) {
                const medianBeltIndex = Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["clamp"])(
                    Math.round(this.entityPath.length / 2 - 1),
                    0,
                    this.entityPath.length - 1
                );
                const medianBelt = this.entityPath[medianBeltIndex];
                const beltComp = medianBelt.components.Belt;
                const staticComp = medianBelt.components.StaticMapEntity;
                const centerPosLocal = beltComp.transformBeltToLocalSpace(
                    this.entityPath.length % 2 === 0 ? beltComp.getEffectiveLengthTiles() : 0.5
                );
                const centerPos = staticComp.localTileToWorld(centerPosLocal).toWorldSpaceCenterOfTile();

                parameters.context.globalAlpha = 0.5;
                firstItem[_item].drawItemCenteredClipped(centerPos.x, centerPos.y, parameters);
                parameters.context.globalAlpha = 1;
            }

            return;
        }

        let currentItemPos = this.spacingToFirstItem;
        let currentItemIndex = 0;

        let trackPos = 0.0;

        // Iterate whole track and check items
        for (let i = 0; i < this.entityPath.length; ++i) {
            const entity = this.entityPath[i];
            const beltComp = entity.components.Belt;
            const beltLength = beltComp.getEffectiveLengthTiles();

            // Check if the current items are on the belt
            while (trackPos + beltLength >= currentItemPos - 1e-5) {
                // It's on the belt, render it now
                const staticComp = entity.components.StaticMapEntity;
                window.assert(
                    currentItemPos - trackPos >= 0,
                    "invalid track pos: " + currentItemPos + " vs " + trackPos + " (l  =" + beltLength + ")"
                );

                const localPos = beltComp.transformBeltToLocalSpace(currentItemPos - trackPos);
                const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile();

                const distanceAndItem = this.items[currentItemIndex];

                distanceAndItem[_item].drawItemCenteredClipped(
                    worldPos.x,
                    worldPos.y,
                    parameters,
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter
                );

                // Check for the next item
                currentItemPos += distanceAndItem[_nextDistance];
                ++currentItemIndex;

                if (currentItemIndex >= this.items.length) {
                    // We rendered all items
                    return;
                }
            }

            trackPos += beltLength;
        }
    }
}


/***/ }),

/***/ "./src/js/game/blueprint.js":
/*!**********************************!*\
  !*** ./src/js/game/blueprint.js ***!
  \**********************************/
/*! exports provided: Blueprint */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Blueprint", function() { return Blueprint; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");







class Blueprint {
    /**
     * @param {Array<Entity>} entities
     */
    constructor(entities) {
        this.entities = entities;
    }

    /**
     * Returns the layer of this blueprint
     * @returns {Layer}
     */
    get layer() {
        if (this.entities.length === 0) {
            return "regular";
        }
        return this.entities[0].layer;
    }

    /**
     * Creates a new blueprint from the given entity uids
     * @param {GameRoot} root
     * @param {Array<number>} uids
     */
    static fromUids(root, uids) {
        const newEntities = [];

        let averagePosition = new _core_vector__WEBPACK_IMPORTED_MODULE_3__["Vector"]();

        // First, create a copy
        for (let i = 0; i < uids.length; ++i) {
            const entity = root.entityMgr.findByUid(uids[i]);
            window.assert(entity, "Entity for blueprint not found:" + uids[i]);

            const clone = entity.clone();
            newEntities.push(clone);

            const pos = entity.components.StaticMapEntity.getTileSpaceBounds().getCenter();
            averagePosition.addInplace(pos);
        }

        averagePosition.divideScalarInplace(uids.length);
        const blueprintOrigin = averagePosition.subScalars(0.5, 0.5).floor();

        for (let i = 0; i < uids.length; ++i) {
            newEntities[i].components.StaticMapEntity.origin.subInplace(blueprintOrigin);
        }

        // Now, make sure the origin is 0,0
        return new Blueprint(newEntities);
    }

    /**
     * Returns the cost of this blueprint in shapes
     */
    getCost() {
        if (false) {}
        return Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["findNiceIntegerValue"])(4 * Math.pow(this.entities.length, 1.1));
    }

    /**
     * Draws the blueprint at the given origin
     * @param {DrawParameters} parameters
     */
    draw(parameters, tile) {
        parameters.context.globalAlpha = 0.8;
        for (let i = 0; i < this.entities.length; ++i) {
            const entity = this.entities[i];
            const staticComp = entity.components.StaticMapEntity;
            const newPos = staticComp.origin.add(tile);

            const rect = staticComp.getTileSpaceBounds();
            rect.moveBy(tile.x, tile.y);

            if (!parameters.root.logic.checkCanPlaceEntity(entity, tile)) {
                parameters.context.globalAlpha = 0.3;
            } else {
                parameters.context.globalAlpha = 1;
            }

            staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0, newPos);
        }
        parameters.context.globalAlpha = 1;
    }

    /**
     * Rotates the blueprint clockwise
     */
    rotateCw() {
        for (let i = 0; i < this.entities.length; ++i) {
            const entity = this.entities[i];
            const staticComp = entity.components.StaticMapEntity;

            staticComp.rotation = (staticComp.rotation + 90) % 360;
            staticComp.originalRotation = (staticComp.originalRotation + 90) % 360;
            staticComp.origin = staticComp.origin.rotateFastMultipleOf90(90);
        }
    }

    /**
     * Rotates the blueprint counter clock wise
     */
    rotateCcw() {
        // Well ...
        for (let i = 0; i < 3; ++i) {
            this.rotateCw();
        }
    }

    /**
     * Checks if the blueprint can be placed at the given tile
     * @param {GameRoot} root
     * @param {Vector} tile
     */
    canPlace(root, tile) {
        let anyPlaceable = false;

        for (let i = 0; i < this.entities.length; ++i) {
            const entity = this.entities[i];
            if (root.logic.checkCanPlaceEntity(entity, tile)) {
                anyPlaceable = true;
            }
        }

        return anyPlaceable;
    }

    /**
     * @param {GameRoot} root
     */
    canAfford(root) {
        return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost();
    }

    /**
     * Attempts to place the blueprint at the given tile
     * @param {GameRoot} root
     * @param {Vector} tile
     */
    tryPlace(root, tile) {
        return root.logic.performBulkOperation(() => {
            let anyPlaced = false;
            for (let i = 0; i < this.entities.length; ++i) {
                const entity = this.entities[i];
                if (!root.logic.checkCanPlaceEntity(entity, tile)) {
                    continue;
                }

                const clone = entity.clone();
                clone.components.StaticMapEntity.origin.addInplace(tile);
                root.logic.freeEntityAreaBeforeBuild(clone);
                root.map.placeStaticEntity(clone);
                root.entityMgr.registerEntity(clone);
                anyPlaced = true;
            }
            return anyPlaced;
        });
    }
}


/***/ }),

/***/ "./src/js/game/building_codes.js":
/*!***************************************!*\
  !*** ./src/js/game/building_codes.js ***!
  \***************************************/
/*! exports provided: gBuildingVariants, registerBuildingVariant, getBuildingDataFromCode, buildBuildingCodeCache, getCodeFromBuildingData */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gBuildingVariants", function() { return gBuildingVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "registerBuildingVariant", function() { return registerBuildingVariant; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBuildingDataFromCode", function() { return getBuildingDataFromCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBuildingCodeCache", function() { return buildBuildingCodeCache; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getCodeFromBuildingData", function() { return getCodeFromBuildingData; });


/**
 * @typedef {{
 *   metaClass: typeof MetaBuilding,
 *   metaInstance?: MetaBuilding,
 *   variant?: string,
 *   rotationVariant?: number,
 *   tileSize?: Vector,
 *   sprite?: AtlasSprite,
 *   blueprintSprite?: AtlasSprite,
 *   silhouetteColor?: string
 * }} BuildingVariantIdentifier
 */

/**
 * Stores a lookup table for all building variants (for better performance)
 * @type {Object<number, BuildingVariantIdentifier>}
 */
const gBuildingVariants = {
    // Set later
};

/**
 * Mapping from 'metaBuildingId/variant/rotationVariant' to building code
 * @type {Map<string, number>}
 */
const variantsCache = new Map();

/**
 * Registers a new variant
 * @param {number} code
 * @param {typeof MetaBuilding} meta
 * @param {string} variant
 * @param {number} rotationVariant
 */
function registerBuildingVariant(
    code,
    meta,
    variant = "default" /* @TODO: Circular dependency, actually its defaultBuildingVariant */,
    rotationVariant = 0
) {
    window.assert(!gBuildingVariants[code], "Duplicate id: " + code);
    gBuildingVariants[code] = {
        metaClass: meta,
        variant,
        rotationVariant,
        // @ts-ignore
        tileSize: new meta().getDimensions(variant),
    };
}

/**
 *
 * @param {number} code
 * @returns {BuildingVariantIdentifier}
 */
function getBuildingDataFromCode(code) {
    window.assert(gBuildingVariants[code], "Invalid building code: " + code);
    return gBuildingVariants[code];
}

/**
 * Builds the cache for the codes
 */
function buildBuildingCodeCache() {
    for (const code in gBuildingVariants) {
        const data = gBuildingVariants[code];
        const hash = data.metaInstance.getId() + "/" + data.variant + "/" + data.rotationVariant;
        variantsCache.set(hash, +code);
    }
}

/**
 * Finds the code for a given variant
 * @param {MetaBuilding} metaBuilding
 * @param {string} variant
 * @param {number} rotationVariant
 * @returns {number}
 */
function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) {
    const hash = metaBuilding.getId() + "/" + variant + "/" + rotationVariant;
    const result = variantsCache.get(hash);
    if (false) {}
    return result;
}


/***/ }),

/***/ "./src/js/game/buildings/analyzer.js":
/*!*******************************************!*\
  !*** ./src/js/game/buildings/analyzer.js ***!
  \*******************************************/
/*! exports provided: MetaAnalyzerBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaAnalyzerBuilding", function() { return MetaAnalyzerBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");









const overlayMatrix = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([1, 1, 0, 1, 1, 1, 0, 1, 0]);

class MetaAnalyzerBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_5__["MetaBuilding"] {
    constructor() {
        super("analyzer");
    }

    getSilhouetteColor() {
        return "#3a52bc";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_virtual_processing);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 1);
    }

    getRenderPins() {
        // We already have it included
        return false;
    }

    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) {
        return overlayMatrix[rotation];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalAcceptor,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["LogicGateComponent"]({
                type: _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].analyzer,
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/balancer.js":
/*!*******************************************!*\
  !*** ./src/js/game/buildings/balancer.js ***!
  \*******************************************/
/*! exports provided: enumBalancerVariants, MetaBalancerBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumBalancerVariants", function() { return enumBalancerVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaBalancerBuilding", function() { return MetaBalancerBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _components_belt_underlays__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../components/belt_underlays */ "./src/js/game/components/belt_underlays.js");












/** @enum {string} */
const enumBalancerVariants = {
    merger: "merger",
    mergerInverse: "merger-inverse",
    splitter: "splitter",
    splitterInverse: "splitter-inverse",
};

const overlayMatrices = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_5__["defaultBuildingVariant"]]: null,
    [enumBalancerVariants.merger]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["generateMatrixRotations"])([0, 1, 0, 0, 1, 1, 0, 1, 0]),
    [enumBalancerVariants.mergerInverse]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["generateMatrixRotations"])([0, 1, 0, 1, 1, 0, 0, 1, 0]),
    [enumBalancerVariants.splitter]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["generateMatrixRotations"])([0, 1, 0, 0, 1, 1, 0, 1, 0]),
    [enumBalancerVariants.splitterInverse]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["generateMatrixRotations"])([0, 1, 0, 1, 1, 0, 0, 1, 0]),
};

class MetaBalancerBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_5__["MetaBuilding"] {
    constructor() {
        super("balancer");
    }

    getDimensions(variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_5__["defaultBuildingVariant"]:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](2, 1);
            case enumBalancerVariants.merger:
            case enumBalancerVariants.mergerInverse:
            case enumBalancerVariants.splitter:
            case enumBalancerVariants.splitterInverse:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
            default:
                window.assert(false, "Unknown balancer variant: " + variant);
        }
    }

    /**
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     * @returns {Array<number>|null}
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        const matrix = overlayMatrices[variant];
        if (matrix) {
            return matrix[rotation];
        }
        return null;
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        let speedMultiplier = 2;
        switch (variant) {
            case enumBalancerVariants.merger:
            case enumBalancerVariants.mergerInverse:
            case enumBalancerVariants.splitter:
            case enumBalancerVariants.splitterInverse:
                speedMultiplier = 1;
        }

        const speed =
            (root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].balancer) / 2) * speedMultiplier;
        return [[_translations__WEBPACK_IMPORTED_MODULE_8__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["formatItemsPerSecond"])(speed)]];
    }

    getSilhouetteColor() {
        return "#555759";
    }

    /**
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        let available = [_meta_building__WEBPACK_IMPORTED_MODULE_5__["defaultBuildingVariant"]];

        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_merger)) {
            available.push(enumBalancerVariants.merger, enumBalancerVariants.mergerInverse);
        }

        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_splitter)) {
            available.push(enumBalancerVariants.splitter, enumBalancerVariants.splitterInverse);
        }

        return available;
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_balancer);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_1__["ItemAcceptorComponent"]({
                slots: [], // set later
            })
        );

        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["ItemProcessorComponent"]({
                inputsPerCharge: 1,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].balancer,
            })
        );

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_2__["ItemEjectorComponent"]({
                slots: [], // set later
                renderFloatingItems: false,
            })
        );

        entity.addComponent(new _components_belt_underlays__WEBPACK_IMPORTED_MODULE_10__["BeltUnderlaysComponent"]({ underlays: [] }));
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_5__["defaultBuildingVariant"]: {
                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                    },
                ]);

                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                ]);

                entity.components.BeltUnderlays.underlays = [
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                ];

                break;
            }
            case enumBalancerVariants.merger:
            case enumBalancerVariants.mergerInverse: {
                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        directions: [
                            variant === enumBalancerVariants.mergerInverse
                                ? _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left
                                : _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                        ],
                    },
                ]);

                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                ]);

                entity.components.BeltUnderlays.underlays = [
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                ];

                break;
            }
            case enumBalancerVariants.splitter:
            case enumBalancerVariants.splitterInverse: {
                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                    },
                ]);

                entity.components.ItemEjector.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction:
                            variant === enumBalancerVariants.splitterInverse
                                ? _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left
                                : _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                    },
                ]);

                entity.components.BeltUnderlays.underlays = [
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top },
                ];

                break;
            }
            default:
                window.assert(false, "Unknown balancer variant: " + variant);
        }
    }
}


/***/ }),

/***/ "./src/js/game/buildings/belt.js":
/*!***************************************!*\
  !*** ./src/js/game/buildings/belt.js ***!
  \***************************************/
/*! exports provided: arrayBeltVariantToRotation, beltOverlayMatrices, MetaBeltBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "arrayBeltVariantToRotation", function() { return arrayBeltVariantToRotation; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "beltOverlayMatrices", function() { return beltOverlayMatrices; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaBeltBuilding", function() { return MetaBeltBuilding; });
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_belt__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/belt */ "./src/js/game/components/belt.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../theme */ "./src/js/game/theme.js");











const arrayBeltVariantToRotation = [_core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].top, _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].left, _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].right];

const beltOverlayMatrices = {
    [_core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].top]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 1, 0, 0, 1, 0, 0, 1, 0]),
    [_core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].left]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 0, 0, 1, 1, 0, 0, 1, 0]),
    [_core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].right]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 0, 0, 0, 1, 1, 0, 1, 0]),
};

class MetaBeltBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_7__["MetaBuilding"] {
    constructor() {
        super("belt");
    }

    getSilhouetteColor() {
        return _theme__WEBPACK_IMPORTED_MODULE_9__["THEME"].map.chunkOverview.beltColor;
    }

    getPlacementSound() {
        return _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].placeBelt;
    }

    getHasDirectionLockAvailable() {
        return true;
    }
    getStayInPlacementMode() {
        return true;
    }

    getRotateAutomaticallyWhilePlacing() {
        return true;
    }

    getSprite() {
        return null;
    }

    getIsReplaceable() {
        return true;
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const beltSpeed = root.hubGoals.getBeltBaseSpeed();
        return [[_translations__WEBPACK_IMPORTED_MODULE_4__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["formatItemsPerSecond"])(beltSpeed)]];
    }

    getPreviewSprite(rotationVariant) {
        switch (arrayBeltVariantToRotation[rotationVariant]) {
            case _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].top: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/buildings/belt_top.png");
            }
            case _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].left: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/buildings/belt_left.png");
            }
            case _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].right: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/buildings/belt_right.png");
            }
            default: {
                window.assert(false, "Invalid belt rotation variant");
            }
        }
    }

    getBlueprintSprite(rotationVariant) {
        switch (arrayBeltVariantToRotation[rotationVariant]) {
            case _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].top: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/blueprints/belt_top.png");
            }
            case _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].left: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/blueprints/belt_left.png");
            }
            case _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].right: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/blueprints/belt_right.png");
            }
            default: {
                window.assert(false, "Invalid belt rotation variant");
            }
        }
    }

    /**
     *
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return beltOverlayMatrices[entity.components.Belt.direction][rotation];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_belt__WEBPACK_IMPORTED_MODULE_5__["BeltComponent"]({
                direction: _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].top, // updated later
            })
        );
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     */
    updateVariants(entity, rotationVariant) {
        entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant];
    }

    /**
     * Should compute the optimal rotation variant on the given tile
     * @param {object} param0
     * @param {GameRoot} param0.root
     * @param {Vector} param0.tile
     * @param {number} param0.rotation
     * @param {string} param0.variant
     * @param {Layer} param0.layer
     * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
     */
    computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
        const topDirection = _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumAngleToDirection"][rotation];
        const rightDirection = _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumAngleToDirection"][(rotation + 90) % 360];
        const bottomDirection = _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumAngleToDirection"][(rotation + 180) % 360];
        const leftDirection = _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumAngleToDirection"][(rotation + 270) % 360];

        const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile);

        let hasBottomEjector = false;
        let hasRightEjector = false;
        let hasLeftEjector = false;

        let hasTopAcceptor = false;
        let hasLeftAcceptor = false;
        let hasRightAcceptor = false;

        // Check all ejectors
        for (let i = 0; i < ejectors.length; ++i) {
            const ejector = ejectors[i];

            if (ejector.toDirection === topDirection) {
                hasBottomEjector = true;
            } else if (ejector.toDirection === leftDirection) {
                hasRightEjector = true;
            } else if (ejector.toDirection === rightDirection) {
                hasLeftEjector = true;
            }
        }

        // Check all acceptors
        for (let i = 0; i < acceptors.length; ++i) {
            const acceptor = acceptors[i];
            if (acceptor.fromDirection === bottomDirection) {
                hasTopAcceptor = true;
            } else if (acceptor.fromDirection === rightDirection) {
                hasLeftAcceptor = true;
            } else if (acceptor.fromDirection === leftDirection) {
                hasRightAcceptor = true;
            }
        }

        // Soo .. if there is any ejector below us we always prioritize
        // this ejector
        if (!hasBottomEjector) {
            // When something ejects to us from the left and nothing from the right,
            // do a curve from the left to the top

            if (hasRightEjector && !hasLeftEjector) {
                return {
                    rotation: (rotation + 270) % 360,
                    rotationVariant: 2,
                };
            }

            // When something ejects to us from the right and nothing from the left,
            // do a curve from the right to the top
            if (hasLeftEjector && !hasRightEjector) {
                return {
                    rotation: (rotation + 90) % 360,
                    rotationVariant: 1,
                };
            }
        }

        // When there is a top acceptor, ignore sides
        // NOTICE: This makes the belt prefer side turns *way* too much!
        if (!hasTopAcceptor) {
            // When there is an acceptor to the right but no acceptor to the left,
            // do a turn to the right
            if (hasRightAcceptor && !hasLeftAcceptor) {
                return {
                    rotation,
                    rotationVariant: 2,
                };
            }

            // When there is an acceptor to the left but no acceptor to the right,
            // do a turn to the left
            if (hasLeftAcceptor && !hasRightAcceptor) {
                return {
                    rotation,
                    rotationVariant: 1,
                };
            }
        }

        return {
            rotation,
            rotationVariant: 0,
        };
    }
}


/***/ }),

/***/ "./src/js/game/buildings/comparator.js":
/*!*********************************************!*\
  !*** ./src/js/game/buildings/comparator.js ***!
  \*********************************************/
/*! exports provided: MetaComparatorBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaComparatorBuilding", function() { return MetaComparatorBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");








class MetaComparatorBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_4__["MetaBuilding"] {
    constructor() {
        super("comparator");
    }

    getSilhouetteColor() {
        return "#823cab";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_virtual_processing);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getRenderPins() {
        // We already have it included
        return false;
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalAcceptor,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["LogicGateComponent"]({
                type: _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].compare,
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/constant_signal.js":
/*!**************************************************!*\
  !*** ./src/js/game/buildings/constant_signal.js ***!
  \**************************************************/
/*! exports provided: MetaConstantSignalBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaConstantSignalBuilding", function() { return MetaConstantSignalBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _components_constant_signal__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/constant_signal */ "./src/js/game/components/constant_signal.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");









const overlayMatrix = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 1, 1, 1]);

class MetaConstantSignalBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_3__["MetaBuilding"] {
    constructor() {
        super("constant_signal");
    }

    getSilhouetteColor() {
        return "#2b84fd";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_constant_signal);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getRenderPins() {
        return false;
    }

    getSpecialOverlayRenderMatrix(rotation) {
        return overlayMatrix[rotation];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalEjector,
                    },
                ],
            })
        );
        entity.addComponent(new _components_constant_signal__WEBPACK_IMPORTED_MODULE_5__["ConstantSignalComponent"]({}));
    }
}


/***/ }),

/***/ "./src/js/game/buildings/cutter.js":
/*!*****************************************!*\
  !*** ./src/js/game/buildings/cutter.js ***!
  \*****************************************/
/*! exports provided: enumCutterVariants, MetaCutterBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumCutterVariants", function() { return enumCutterVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaCutterBuilding", function() { return MetaCutterBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");











/** @enum {string} */
const enumCutterVariants = { quad: "quad" };

class MetaCutterBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_7__["MetaBuilding"] {
    constructor() {
        super("cutter");
    }

    getSilhouetteColor() {
        return "#7dcda2";
    }

    getDimensions(variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 1);
            case enumCutterVariants.quad:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](4, 1);
            default:
                window.assert(false, "Unknown cutter variant: " + variant);
        }
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const speed = root.hubGoals.getProcessorBaseSpeed(
            variant === enumCutterVariants.quad
                ? _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutterQuad
                : _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutter
        );
        return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
    }

    /**
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_cutter_quad)) {
            return [_meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"], enumCutterVariants.quad];
        }
        return super.getAvailableVariants(root);
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_cutter_and_trash);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["ItemProcessorComponent"]({
                inputsPerCharge: 1,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutter,
            })
        );
        entity.addComponent(new _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__["ItemEjectorComponent"]({}));
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "shape",
                    },
                ],
            })
        );
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]: {
                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                ]);
                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutter;
                break;
            }
            case enumCutterVariants.quad: {
                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](3, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                ]);
                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutterQuad;
                break;
            }

            default:
                window.assert(false, "Unknown painter variant: " + variant);
        }
    }
}


/***/ }),

/***/ "./src/js/game/buildings/display.js":
/*!******************************************!*\
  !*** ./src/js/game/buildings/display.js ***!
  \******************************************/
/*! exports provided: MetaDisplayBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaDisplayBuilding", function() { return MetaDisplayBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _components_display__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/display */ "./src/js/game/components/display.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");








class MetaDisplayBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_3__["MetaBuilding"] {
    constructor() {
        super("display");
    }

    getSilhouetteColor() {
        return "#aaaaaa";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_display);
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getShowWiresLayerPreview() {
        return true;
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalAcceptor,
                    },
                ],
            })
        );
        entity.addComponent(new _components_display__WEBPACK_IMPORTED_MODULE_5__["DisplayComponent"]());
    }
}


/***/ }),

/***/ "./src/js/game/buildings/filter.js":
/*!*****************************************!*\
  !*** ./src/js/game/buildings/filter.js ***!
  \*****************************************/
/*! exports provided: MetaFilterBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaFilterBuilding", function() { return MetaFilterBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_filter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/filter */ "./src/js/game/components/filter.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");












class MetaFilterBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_8__["MetaBuilding"] {
    constructor() {
        super("filter");
    }

    getSilhouetteColor() {
        return "#c45c2e";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_10__["enumHubGoalRewards"].reward_filter);
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 1);
    }

    getShowWiresLayerPreview() {
        return true;
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const beltSpeed = root.hubGoals.getBeltBaseSpeed();
        return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(beltSpeed)]];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["enumPinSlotType"].logicalAcceptor,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_4__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_5__["ItemEjectorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right,
                    },
                ],
            })
        );

        entity.addComponent(new _components_filter__WEBPACK_IMPORTED_MODULE_3__["FilterComponent"]());
    }
}


/***/ }),

/***/ "./src/js/game/buildings/hub.js":
/*!**************************************!*\
  !*** ./src/js/game/buildings/hub.js ***!
  \**************************************/
/*! exports provided: MetaHubBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaHubBuilding", function() { return MetaHubBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_hub__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/hub */ "./src/js/game/components/hub.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");








class MetaHubBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_5__["MetaBuilding"] {
    constructor() {
        super("hub");
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](4, 4);
    }

    getSilhouetteColor() {
        return "#eb5555";
    }

    getIsRotateable() {
        return false;
    }

    getBlueprintSprite() {
        return null;
    }

    getSprite() {
        // We render it ourself
        return null;
    }

    getIsRemovable() {
        return false;
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(new _components_hub__WEBPACK_IMPORTED_MODULE_1__["HubComponent"]());
        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["ItemProcessorComponent"]({
                inputsPerCharge: 1,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].hub,
            })
        );

        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 2),
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["enumPinSlotType"].logicalEjector,
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_2__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top, _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](2, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](3, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top, _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 3),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom, _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 3),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](2, 3),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](3, 3),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom, _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 1),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 2),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 3),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](3, 1),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](3, 2),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](3, 3),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right],
                        filter: "shape",
                    },
                ],
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/item_producer.js":
/*!************************************************!*\
  !*** ./src/js/game/buildings/item_producer.js ***!
  \************************************************/
/*! exports provided: MetaItemProducerBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaItemProducerBuilding", function() { return MetaItemProducerBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_producer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_producer */ "./src/js/game/components/item_producer.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");







class MetaItemProducerBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_5__["MetaBuilding"] {
    constructor() {
        super("item_producer");
    }

    getSilhouetteColor() {
        return "#b37dcd";
    }

    getShowWiresLayerPreview() {
        return true;
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_1__["ItemEjectorComponent"]({
                slots: [{ pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top }],
            })
        );
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalAcceptor,
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom,
                    },
                ],
            })
        );
        entity.addComponent(new _components_item_producer__WEBPACK_IMPORTED_MODULE_2__["ItemProducerComponent"]());
    }
}


/***/ }),

/***/ "./src/js/game/buildings/lever.js":
/*!****************************************!*\
  !*** ./src/js/game/buildings/lever.js ***!
  \****************************************/
/*! exports provided: MetaLeverBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaLeverBuilding", function() { return MetaLeverBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _components_lever__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/lever */ "./src/js/game/components/lever.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");








class MetaLeverBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_3__["MetaBuilding"] {
    constructor() {
        super("lever");
    }

    getSilhouetteColor() {
        // @todo: Render differently based on if its activated or not
        return "#1a678b";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_wires_painter_and_levers);
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getSprite() {
        return null;
    }

    getShowWiresLayerPreview() {
        return true;
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalEjector,
                    },
                ],
            })
        );

        entity.addComponent(new _components_lever__WEBPACK_IMPORTED_MODULE_5__["LeverComponent"]({}));
    }
}


/***/ }),

/***/ "./src/js/game/buildings/logic_gate.js":
/*!*********************************************!*\
  !*** ./src/js/game/buildings/logic_gate.js ***!
  \*********************************************/
/*! exports provided: enumLogicGateVariants, enumVariantToGate, MetaLogicGateBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumLogicGateVariants", function() { return enumLogicGateVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumVariantToGate", function() { return enumVariantToGate; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaLogicGateBuilding", function() { return MetaLogicGateBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");









/** @enum {string} */
const enumLogicGateVariants = {
    not: "not",
    xor: "xor",
    or: "or",
};

/** @enum {string} */
const enumVariantToGate = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_3__["defaultBuildingVariant"]]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].and,
    [enumLogicGateVariants.not]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].not,
    [enumLogicGateVariants.xor]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].xor,
    [enumLogicGateVariants.or]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].or,
};

const overlayMatrices = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_3__["defaultBuildingVariant"]]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 0, 1, 1]),
    [enumLogicGateVariants.xor]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 0, 1, 1]),
    [enumLogicGateVariants.or]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 0, 1, 1]),
    [enumLogicGateVariants.not]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["generateMatrixRotations"])([0, 1, 0, 0, 1, 0, 0, 1, 0]),
};

const colors = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_3__["defaultBuildingVariant"]]: "#f48d41",
    [enumLogicGateVariants.xor]: "#f4a241",
    [enumLogicGateVariants.or]: "#f4d041",
    [enumLogicGateVariants.not]: "#f44184",
};

class MetaLogicGateBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_3__["MetaBuilding"] {
    constructor() {
        super("logic_gate");
    }

    getSilhouetteColor(variant) {
        return colors[variant];
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_logic_gates);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) {
        return overlayMatrices[variant][rotation];
    }

    getAvailableVariants() {
        return [
            _meta_building__WEBPACK_IMPORTED_MODULE_3__["defaultBuildingVariant"],
            enumLogicGateVariants.or,
            enumLogicGateVariants.not,
            enumLogicGateVariants.xor,
        ];
    }

    getRenderPins() {
        // We already have it included
        return false;
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     */
    updateVariants(entity, rotationVariant, variant) {
        const gateType = enumVariantToGate[variant];
        entity.components.LogicGate.type = gateType;

        const pinComp = entity.components.WiredPins;

        switch (gateType) {
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].and:
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].xor:
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].or: {
                pinComp.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalAcceptor,
                    },
                ]);
                break;
            }

            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["enumLogicGateType"].not: {
                pinComp.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["enumPinSlotType"].logicalAcceptor,
                    },
                ]);
                break;
            }

            default:
                window.assert("unknown logic gate type: " + gateType);
        }
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_1__["WiredPinsComponent"]({
                slots: [],
            })
        );

        entity.addComponent(new _components_logic_gate__WEBPACK_IMPORTED_MODULE_5__["LogicGateComponent"]({}));
    }
}


/***/ }),

/***/ "./src/js/game/buildings/miner.js":
/*!****************************************!*\
  !*** ./src/js/game/buildings/miner.js ***!
  \****************************************/
/*! exports provided: enumMinerVariants, MetaMinerBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumMinerVariants", function() { return enumMinerVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaMinerBuilding", function() { return MetaMinerBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_miner__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/miner */ "./src/js/game/components/miner.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");










/** @enum {string} */
const enumMinerVariants = { chainable: "chainable" };

const overlayMatrix = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_4__["defaultBuildingVariant"]]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_8__["generateMatrixRotations"])([1, 1, 1, 1, 0, 1, 1, 1, 1]),
    [enumMinerVariants.chainable]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_8__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 1, 1, 1]),
};

class MetaMinerBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_4__["MetaBuilding"] {
    constructor() {
        super("miner");
    }

    getSilhouetteColor() {
        return "#b37dcd";
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const speed = root.hubGoals.getMinerBaseSpeed();
        return [[_translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_8__["formatItemsPerSecond"])(speed)]];
    }

    /**
     *
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_miner_chainable)) {
            return [enumMinerVariants.chainable];
        }
        return super.getAvailableVariants(root);
    }

    /**
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return overlayMatrix[variant][rotation];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(new _components_miner__WEBPACK_IMPORTED_MODULE_2__["MinerComponent"]({}));
        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_1__["ItemEjectorComponent"]({
                slots: [{ pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top }],
            })
        );
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        entity.components.Miner.chainable = variant === enumMinerVariants.chainable;
    }
}


/***/ }),

/***/ "./src/js/game/buildings/mixer.js":
/*!****************************************!*\
  !*** ./src/js/game/buildings/mixer.js ***!
  \****************************************/
/*! exports provided: MetaMixerBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaMixerBuilding", function() { return MetaMixerBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");











class MetaMixerBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_7__["MetaBuilding"] {
    constructor() {
        super("mixer");
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 1);
    }

    getSilhouetteColor() {
        return "#cdbb7d";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_mixer);
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].mixer);
        return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["ItemProcessorComponent"]({
                inputsPerCharge: 2,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].mixer,
            })
        );

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__["ItemEjectorComponent"]({
                slots: [{ pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top }],
            })
        );
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "color",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "color",
                    },
                ],
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/painter.js":
/*!******************************************!*\
  !*** ./src/js/game/buildings/painter.js ***!
  \******************************************/
/*! exports provided: enumPainterVariants, MetaPainterBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumPainterVariants", function() { return enumPainterVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaPainterBuilding", function() { return MetaPainterBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");












/** @enum {string} */
const enumPainterVariants = { mirrored: "mirrored", double: "double", quad: "quad" };

class MetaPainterBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_7__["MetaBuilding"] {
    constructor() {
        super("painter");
    }

    getDimensions(variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]:
            case enumPainterVariants.mirrored:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 1);
            case enumPainterVariants.double:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 2);
            case enumPainterVariants.quad:
                return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](4, 1);
            default:
                window.assert(false, "Unknown painter variant: " + variant);
        }
    }

    getSilhouetteColor() {
        return "#cd9b7d";
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]:
            case enumPainterVariants.mirrored: {
                const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painter);
                return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
            }
            case enumPainterVariants.double: {
                const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painterDouble);
                return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed, true)]];
            }
            case enumPainterVariants.quad: {
                const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painterQuad);
                return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
            }
        }
    }

    /**
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        let variants = [_meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"], enumPainterVariants.mirrored];
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_painter_double)) {
            variants.push(enumPainterVariants.double);
        }
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_wires_painter_and_levers)) {
            variants.push(enumPainterVariants.quad);
        }
        return variants;
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_painter);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(new _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["ItemProcessorComponent"]({}));

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__["ItemEjectorComponent"]({
                slots: [{ pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right }],
            })
        );
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top],
                        filter: "color",
                    },
                ],
            })
        );
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]:
            case enumPainterVariants.mirrored: {
                // REGULAR PAINTER

                if (entity.components.WiredPins) {
                    entity.removeComponent(_components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["WiredPinsComponent"]);
                }

                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        directions: [
                            variant === _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"] ? _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top : _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        ],
                        filter: "color",
                    },
                ]);

                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right },
                ]);

                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painter;
                entity.components.ItemProcessor.processingRequirement = null;
                entity.components.ItemProcessor.inputsPerCharge = 2;

                break;
            }

            case enumPainterVariants.double: {
                // DOUBLE PAINTER

                if (entity.components.WiredPins) {
                    entity.removeComponent(_components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["WiredPinsComponent"]);
                }

                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 1),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top],
                        filter: "color",
                    },
                ]);

                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right },
                ]);

                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painterDouble;
                entity.components.ItemProcessor.processingRequirement = null;
                entity.components.ItemProcessor.inputsPerCharge = 3;
                break;
            }

            case enumPainterVariants.quad: {
                // QUAD PAINTER

                if (!entity.components.WiredPins) {
                    entity.addComponent(new _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["WiredPinsComponent"]({ slots: [] }));
                }

                entity.components.WiredPins.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](3, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["enumPinSlotType"].logicalAcceptor,
                    },
                ]);

                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "color",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "color",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "color",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](3, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "color",
                    },
                ]);

                entity.components.ItemEjector.setSlots([
                    { pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top },
                ]);

                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painterQuad;
                entity.components.ItemProcessor.processingRequirement =
                    _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorRequirements"].painterQuad;
                entity.components.ItemProcessor.inputsPerCharge = 5;

                break;
            }

            default:
                window.assert(false, "Unknown painter variant: " + variant);
        }
    }
}


/***/ }),

/***/ "./src/js/game/buildings/reader.js":
/*!*****************************************!*\
  !*** ./src/js/game/buildings/reader.js ***!
  \*****************************************/
/*! exports provided: MetaReaderBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaReaderBuilding", function() { return MetaReaderBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../components/belt_underlays */ "./src/js/game/components/belt_underlays.js");
/* harmony import */ var _components_belt_reader__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../components/belt_reader */ "./src/js/game/components/belt_reader.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");













const overlayMatrix = Object(_core_utils__WEBPACK_IMPORTED_MODULE_11__["generateMatrixRotations"])([0, 1, 0, 0, 1, 0, 0, 1, 0]);

class MetaReaderBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_6__["MetaBuilding"] {
    constructor() {
        super("reader");
    }

    getSilhouetteColor() {
        return "#25fff2";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_10__["enumHubGoalRewards"].reward_belt_reader);
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getShowWiresLayerPreview() {
        return true;
    }

    /**
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     * @returns {Array<number>|null}
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return overlayMatrix[rotation];
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_4__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_4__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_4__["enumPinSlotType"].logicalEjector,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_1__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_2__["ItemEjectorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["ItemProcessorComponent"]({
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].reader,
                inputsPerCharge: 1,
            })
        );

        entity.addComponent(
            new _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["BeltUnderlaysComponent"]({
                underlays: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                    },
                ],
            })
        );

        entity.addComponent(new _components_belt_reader__WEBPACK_IMPORTED_MODULE_9__["BeltReaderComponent"]());
    }
}


/***/ }),

/***/ "./src/js/game/buildings/rotater.js":
/*!******************************************!*\
  !*** ./src/js/game/buildings/rotater.js ***!
  \******************************************/
/*! exports provided: enumRotaterVariants, MetaRotaterBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumRotaterVariants", function() { return enumRotaterVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaRotaterBuilding", function() { return MetaRotaterBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");











/** @enum {string} */
const enumRotaterVariants = { ccw: "ccw", rotate180: "rotate180" };

const overlayMatrices = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([0, 1, 1, 1, 1, 0, 0, 1, 1]),
    [enumRotaterVariants.ccw]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([1, 1, 0, 0, 1, 1, 1, 1, 0]),
    [enumRotaterVariants.rotate180]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([1, 1, 0, 1, 1, 1, 0, 1, 1]),
};

class MetaRotaterBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_7__["MetaBuilding"] {
    constructor() {
        super("rotater");
    }

    getSilhouetteColor() {
        return "#7dc6cd";
    }

    /**
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     * @returns {Array<number>|null}
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        const matrix = overlayMatrices[variant];
        if (matrix) {
            return matrix[rotation];
        }
        return null;
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]: {
                const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater);
                return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
            }
            case enumRotaterVariants.ccw: {
                const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotaterCCW);
                return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
            }
            case enumRotaterVariants.rotate180: {
                const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater180);
                return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
            }
        }
    }

    /**
     *
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        let variants = [_meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]];
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_rotater_ccw)) {
            variants.push(enumRotaterVariants.ccw);
        }
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_rotater_180)) {
            variants.push(enumRotaterVariants.rotate180);
        }
        return variants;
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_rotater);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["ItemProcessorComponent"]({
                inputsPerCharge: 1,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater,
            })
        );

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__["ItemEjectorComponent"]({
                slots: [{ pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top }],
            })
        );
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "shape",
                    },
                ],
            })
        );
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        switch (variant) {
            case _meta_building__WEBPACK_IMPORTED_MODULE_7__["defaultBuildingVariant"]: {
                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater;
                break;
            }
            case enumRotaterVariants.ccw: {
                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotaterCCW;
                break;
            }
            case enumRotaterVariants.rotate180: {
                entity.components.ItemProcessor.type = _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater180;
                break;
            }
            default:
                window.assert(false, "Unknown rotater variant: " + variant);
        }
    }
}


/***/ }),

/***/ "./src/js/game/buildings/stacker.js":
/*!******************************************!*\
  !*** ./src/js/game/buildings/stacker.js ***!
  \******************************************/
/*! exports provided: MetaStackerBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaStackerBuilding", function() { return MetaStackerBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");











class MetaStackerBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_7__["MetaBuilding"] {
    constructor() {
        super("stacker");
    }

    getSilhouetteColor() {
        return "#9fcd7d";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 1);
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const speed = root.hubGoals.getProcessorBaseSpeed(_components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].stacker);
        return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatItemsPerSecond"])(speed)]];
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_stacker);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["ItemProcessorComponent"]({
                inputsPerCharge: 2,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].stacker,
            })
        );

        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__["ItemEjectorComponent"]({
                slots: [{ pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0), direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top }],
            })
        );
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "shape",
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                        filter: "shape",
                    },
                ],
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/storage.js":
/*!******************************************!*\
  !*** ./src/js/game/buildings/storage.js ***!
  \******************************************/
/*! exports provided: MetaStorageBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaStorageBuilding", function() { return MetaStorageBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_storage__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../components/storage */ "./src/js/game/components/storage.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");












const storageSize = 5000;

class MetaStorageBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_8__["MetaBuilding"] {
    constructor() {
        super("storage");
    }

    getSilhouetteColor() {
        return "#bbdf6d";
    }

    /**
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        return [[_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.buildingPlacement.infoTexts.storage, Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["formatBigNumber"])(storageSize)]];
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](2, 2);
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_10__["enumHubGoalRewards"].reward_storage);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        // Required, since the item processor needs this.
        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_4__["ItemEjectorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_3__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 1),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 1),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_storage__WEBPACK_IMPORTED_MODULE_5__["StorageComponent"]({
                maximumStorage: storageSize,
            })
        );

        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 1),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 1),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["enumPinSlotType"].logicalEjector,
                    },
                ],
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/transistor.js":
/*!*********************************************!*\
  !*** ./src/js/game/buildings/transistor.js ***!
  \*********************************************/
/*! exports provided: enumTransistorVariants, MetaTransistorBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumTransistorVariants", function() { return enumTransistorVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaTransistorBuilding", function() { return MetaTransistorBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");









/** @enum {string} */
const enumTransistorVariants = {
    mirrored: "mirrored",
};

const overlayMatrices = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_5__["defaultBuildingVariant"]]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([0, 1, 0, 1, 1, 0, 0, 1, 0]),
    [enumTransistorVariants.mirrored]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([0, 1, 0, 0, 1, 1, 0, 1, 0]),
};

class MetaTransistorBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_5__["MetaBuilding"] {
    constructor() {
        super("transistor");
    }

    getSilhouetteColor() {
        return "#bc3a61";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_logic_gates);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 1);
    }

    getAvailableVariants() {
        return [_meta_building__WEBPACK_IMPORTED_MODULE_5__["defaultBuildingVariant"], enumTransistorVariants.mirrored];
    }

    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) {
        return overlayMatrices[variant][rotation];
    }

    getRenderPins() {
        // We already have it included
        return false;
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     */
    updateVariants(entity, rotationVariant, variant) {
        entity.components.WiredPins.slots[1].direction =
            variant === enumTransistorVariants.mirrored ? _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right : _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left;
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["WiredPinsComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalAcceptor,
                    },
                ],
            })
        );

        entity.addComponent(
            new _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["LogicGateComponent"]({
                type: _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].transistor,
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/trash.js":
/*!****************************************!*\
  !*** ./src/js/game/buildings/trash.js ***!
  \****************************************/
/*! exports provided: MetaTrashBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaTrashBuilding", function() { return MetaTrashBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");









const overlayMatrix = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([1, 1, 0, 1, 1, 1, 0, 1, 1]);

class MetaTrashBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_5__["MetaBuilding"] {
    constructor() {
        super("trash");
    }

    getIsRotateable() {
        return false;
    }

    getSilhouetteColor() {
        return "#ed1d5d";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 1);
    }

    getSpecialOverlayRenderMatrix(rotation) {
        return overlayMatrix[rotation];
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_cutter_and_trash);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_2__["ItemAcceptorComponent"]({
                slots: [
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [
                            _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top,
                            _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].right,
                            _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom,
                            _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].left,
                        ],
                    },
                ],
            })
        );
        entity.addComponent(
            new _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["ItemProcessorComponent"]({
                inputsPerCharge: 1,
                processorType: _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].trash,
            })
        );
    }
}


/***/ }),

/***/ "./src/js/game/buildings/underground_belt.js":
/*!***************************************************!*\
  !*** ./src/js/game/buildings/underground_belt.js ***!
  \***************************************************/
/*! exports provided: arrayUndergroundRotationVariantToMode, enumUndergroundBeltVariants, enumUndergroundBeltVariantToTier, MetaUndergroundBeltBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "arrayUndergroundRotationVariantToMode", function() { return arrayUndergroundRotationVariantToMode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumUndergroundBeltVariants", function() { return enumUndergroundBeltVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumUndergroundBeltVariantToTier", function() { return enumUndergroundBeltVariantToTier; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaUndergroundBeltBuilding", function() { return MetaUndergroundBeltBuilding; });
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/underground_belt */ "./src/js/game/components/underground_belt.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");













/** @enum {string} */
const arrayUndergroundRotationVariantToMode = [
    _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].sender,
    _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].receiver,
];

/** @enum {string} */
const enumUndergroundBeltVariants = { tier2: "tier2" };

const enumUndergroundBeltVariantToTier = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"]]: 0,
    [enumUndergroundBeltVariants.tier2]: 1,
};

const colorsByRotationVariant = ["#6d9dff", "#71ff9c"];

const overlayMatrices = [
    // Sender
    Object(_core_utils__WEBPACK_IMPORTED_MODULE_10__["generateMatrixRotations"])([1, 1, 1, 0, 1, 0, 0, 1, 0]),

    // Receiver
    Object(_core_utils__WEBPACK_IMPORTED_MODULE_10__["generateMatrixRotations"])([0, 1, 0, 0, 1, 0, 1, 1, 1]),
];

class MetaUndergroundBeltBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_6__["MetaBuilding"] {
    constructor() {
        super("underground_belt");
    }

    getSilhouetteColor(variant, rotationVariant) {
        return colorsByRotationVariant[rotationVariant];
    }

    getFlipOrientationAfterPlacement() {
        return true;
    }

    getStayInPlacementMode() {
        return true;
    }

    /**
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return overlayMatrices[rotationVariant][rotation];
    }

    /**
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        const rangeTiles =
            _core_config__WEBPACK_IMPORTED_MODULE_8__["globalConfig"].undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]];

        const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed();
        return [
            [
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.buildingPlacement.infoTexts.range,
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.buildingPlacement.infoTexts.tiles.replace("<x>", "" + rangeTiles),
            ],
            [_translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.buildingPlacement.infoTexts.speed, Object(_core_utils__WEBPACK_IMPORTED_MODULE_10__["formatItemsPerSecond"])(beltSpeed)],
        ];
    }

    /**
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        if (root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_underground_belt_tier_2)) {
            return [_meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"], enumUndergroundBeltVariants.tier2];
        }
        return super.getAvailableVariants(root);
    }

    /**
     * @param {number} rotationVariant
     * @param {string} variant
     */
    getPreviewSprite(rotationVariant, variant) {
        let suffix = "";
        if (variant !== _meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"]) {
            suffix = "-" + variant;
        }

        switch (arrayUndergroundRotationVariantToMode[rotationVariant]) {
            case _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].sender:
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/buildings/underground_belt_entry" + suffix + ".png");
            case _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].receiver:
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/buildings/underground_belt_exit" + suffix + ".png");
            default:
                window.assert(false, "Invalid rotation variant");
        }
    }

    /**
     * @param {number} rotationVariant
     * @param {string} variant
     */
    getBlueprintSprite(rotationVariant, variant) {
        let suffix = "";
        if (variant !== _meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"]) {
            suffix = "-" + variant;
        }

        switch (arrayUndergroundRotationVariantToMode[rotationVariant]) {
            case _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].sender:
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/blueprints/underground_belt_entry" + suffix + ".png");
            case _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].receiver:
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/blueprints/underground_belt_exit" + suffix + ".png");
            default:
                window.assert(false, "Invalid rotation variant");
        }
    }

    /**
     * @param {number} rotationVariant
     * @param {string} variant
     */
    getSprite(rotationVariant, variant) {
        return this.getPreviewSprite(rotationVariant, variant);
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].reward_tunnel);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        // Required, since the item processor needs this.
        entity.addComponent(
            new _components_item_ejector__WEBPACK_IMPORTED_MODULE_3__["ItemEjectorComponent"]({
                slots: [],
            })
        );

        entity.addComponent(new _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["UndergroundBeltComponent"]({}));
        entity.addComponent(
            new _components_item_acceptor__WEBPACK_IMPORTED_MODULE_2__["ItemAcceptorComponent"]({
                slots: [],
            })
        );
    }

    /**
     * Should compute the optimal rotation variant on the given tile
     * @param {object} param0
     * @param {GameRoot} param0.root
     * @param {Vector} param0.tile
     * @param {number} param0.rotation
     * @param {string} param0.variant
     * @param {Layer} param0.layer
     * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
     */
    computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
        const searchDirection = _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumAngleToDirection"][rotation];
        const searchVector = _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirectionToVector"][searchDirection];
        const tier = enumUndergroundBeltVariantToTier[variant];

        const targetRotation = (rotation + 180) % 360;
        const targetSenderRotation = rotation;

        for (
            let searchOffset = 1;
            searchOffset <= _core_config__WEBPACK_IMPORTED_MODULE_8__["globalConfig"].undergroundBeltMaxTilesByTier[tier];
            ++searchOffset
        ) {
            tile = tile.addScalars(searchVector.x, searchVector.y);

            const contents = root.map.getTileContent(tile, "regular");
            if (contents) {
                const undergroundComp = contents.components.UndergroundBelt;
                if (undergroundComp && undergroundComp.tier === tier) {
                    const staticComp = contents.components.StaticMapEntity;
                    if (staticComp.rotation === targetRotation) {
                        if (undergroundComp.mode !== _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].sender) {
                            // If we encounter an underground receiver on our way which is also faced in our direction, we don't accept that
                            break;
                        }
                        return {
                            rotation: targetRotation,
                            rotationVariant: 1,
                            connectedEntities: [contents],
                        };
                    } else if (staticComp.rotation === targetSenderRotation) {
                        // Draw connections to receivers
                        if (undergroundComp.mode === _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].receiver) {
                            return {
                                rotation: rotation,
                                rotationVariant: 0,
                                connectedEntities: [contents],
                            };
                        } else {
                            break;
                        }
                    }
                }
            }
        }

        return {
            rotation,
            rotationVariant: 0,
        };
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        entity.components.UndergroundBelt.tier = enumUndergroundBeltVariantToTier[variant];

        switch (arrayUndergroundRotationVariantToMode[rotationVariant]) {
            case _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].sender: {
                entity.components.UndergroundBelt.mode = _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].sender;
                entity.components.ItemEjector.setSlots([]);
                entity.components.ItemAcceptor.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        directions: [_core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].bottom],
                    },
                ]);
                return;
            }
            case _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].receiver: {
                entity.components.UndergroundBelt.mode = _components_underground_belt__WEBPACK_IMPORTED_MODULE_4__["enumUndergroundBeltMode"].receiver;
                entity.components.ItemAcceptor.setSlots([]);
                entity.components.ItemEjector.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_1__["enumDirection"].top,
                    },
                ]);
                return;
            }
            default:
                window.assert(false, "Invalid rotation variant");
        }
    }
}


/***/ }),

/***/ "./src/js/game/buildings/virtual_processor.js":
/*!****************************************************!*\
  !*** ./src/js/game/buildings/virtual_processor.js ***!
  \****************************************************/
/*! exports provided: enumVirtualProcessorVariants, enumVariantToGate, MetaVirtualProcessorBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumVirtualProcessorVariants", function() { return enumVirtualProcessorVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumVariantToGate", function() { return enumVariantToGate; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaVirtualProcessorBuilding", function() { return MetaVirtualProcessorBuilding; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _cutter__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./cutter */ "./src/js/game/buildings/cutter.js");
/* harmony import */ var _painter__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./painter */ "./src/js/game/buildings/painter.js");
/* harmony import */ var _rotater__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./rotater */ "./src/js/game/buildings/rotater.js");
/* harmony import */ var _stacker__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./stacker */ "./src/js/game/buildings/stacker.js");












/** @enum {string} */
const enumVirtualProcessorVariants = {
    rotater: "rotater",
    unstacker: "unstacker",
    stacker: "stacker",
    painter: "painter",
};

/** @enum {string} */
const enumVariantToGate = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_4__["defaultBuildingVariant"]]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].cutter,
    [enumVirtualProcessorVariants.rotater]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].rotater,
    [enumVirtualProcessorVariants.unstacker]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].unstacker,
    [enumVirtualProcessorVariants.stacker]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].stacker,
    [enumVirtualProcessorVariants.painter]: _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].painter,
};

const colors = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_4__["defaultBuildingVariant"]]: new _cutter__WEBPACK_IMPORTED_MODULE_7__["MetaCutterBuilding"]().getSilhouetteColor(),
    [enumVirtualProcessorVariants.rotater]: new _rotater__WEBPACK_IMPORTED_MODULE_9__["MetaRotaterBuilding"]().getSilhouetteColor(),
    [enumVirtualProcessorVariants.unstacker]: new _stacker__WEBPACK_IMPORTED_MODULE_10__["MetaStackerBuilding"]().getSilhouetteColor(),
    [enumVirtualProcessorVariants.stacker]: new _stacker__WEBPACK_IMPORTED_MODULE_10__["MetaStackerBuilding"]().getSilhouetteColor(),
    [enumVirtualProcessorVariants.painter]: new _painter__WEBPACK_IMPORTED_MODULE_8__["MetaPainterBuilding"]().getSilhouetteColor(),
};

class MetaVirtualProcessorBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_4__["MetaBuilding"] {
    constructor() {
        super("virtual_processor");
    }

    getSilhouetteColor(variant) {
        return colors[variant];
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_virtual_processing);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](1, 1);
    }

    getAvailableVariants() {
        return [
            _meta_building__WEBPACK_IMPORTED_MODULE_4__["defaultBuildingVariant"],
            enumVirtualProcessorVariants.rotater,
            enumVirtualProcessorVariants.stacker,
            enumVirtualProcessorVariants.painter,
            enumVirtualProcessorVariants.unstacker,
        ];
    }

    getRenderPins() {
        // We already have it included
        return false;
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     */
    updateVariants(entity, rotationVariant, variant) {
        const gateType = enumVariantToGate[variant];
        entity.components.LogicGate.type = gateType;
        const pinComp = entity.components.WiredPins;
        switch (gateType) {
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].cutter:
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].unstacker: {
                pinComp.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalAcceptor,
                    },
                ]);
                break;
            }
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].rotater: {
                pinComp.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalAcceptor,
                    },
                ]);
                break;
            }
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].stacker:
            case _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["enumLogicGateType"].painter: {
                pinComp.setSlots([
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalEjector,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalAcceptor,
                    },
                    {
                        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
                        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
                        type: _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["enumPinSlotType"].logicalAcceptor,
                    },
                ]);
                break;
            }
            default:
                window.assert("unknown logic gate type: " + gateType);
        }
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(
            new _components_wired_pins__WEBPACK_IMPORTED_MODULE_2__["WiredPinsComponent"]({
                slots: [],
            })
        );

        entity.addComponent(new _components_logic_gate__WEBPACK_IMPORTED_MODULE_1__["LogicGateComponent"]({}));
    }
}


/***/ }),

/***/ "./src/js/game/buildings/wire.js":
/*!***************************************!*\
  !*** ./src/js/game/buildings/wire.js ***!
  \***************************************/
/*! exports provided: arrayWireRotationVariantToType, wireOverlayMatrices, wireVariants, MetaWireBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "arrayWireRotationVariantToType", function() { return arrayWireRotationVariantToType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "wireOverlayMatrices", function() { return wireOverlayMatrices; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "wireVariants", function() { return wireVariants; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaWireBuilding", function() { return MetaWireBuilding; });
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _components_wire__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/wire */ "./src/js/game/components/wire.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");










const arrayWireRotationVariantToType = [
    _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].forward,
    _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn,
    _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split,
    _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].cross,
];

const wireOverlayMatrices = {
    [_components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].forward]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 1, 0, 0, 1, 0, 0, 1, 0]),
    [_components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 0, 0, 1, 1, 1, 0, 1, 0]),
    [_components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 0, 0, 0, 1, 1, 0, 1, 0]),
    [_components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].cross]: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 0, 1, 0]),
};

/** @enum {string} */
const wireVariants = {
    second: "second",
};

const enumWireVariantToVariant = {
    [_meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"]]: _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireVariant"].first,
    [wireVariants.second]: _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireVariant"].second,
};

class MetaWireBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_6__["MetaBuilding"] {
    constructor() {
        super("wire");
    }

    getHasDirectionLockAvailable() {
        return true;
    }

    getSilhouetteColor() {
        return "#61ef6f";
    }

    getAvailableVariants() {
        return [_meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"], wireVariants.second];
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](1, 1);
    }

    getStayInPlacementMode() {
        return true;
    }

    getPlacementSound() {
        return _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].placeBelt;
    }

    getRotateAutomaticallyWhilePlacing() {
        return true;
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    getSprite() {
        return null;
    }

    getIsReplaceable() {
        return true;
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_8__["enumHubGoalRewards"].reward_wires_painter_and_levers);
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(new _components_wire__WEBPACK_IMPORTED_MODULE_4__["WireComponent"]({}));
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {
        entity.components.Wire.type = arrayWireRotationVariantToType[rotationVariant];
        entity.components.Wire.variant = enumWireVariantToVariant[variant];
    }

    /**
     *
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return wireOverlayMatrices[entity.components.Wire.type][rotation];
    }

    /**
     *
     * @param {number} rotationVariant
     * @param {string} variant
     * @returns {import("../../core/draw_utils").AtlasSprite}
     */
    getPreviewSprite(rotationVariant, variant) {
        const wireVariant = enumWireVariantToVariant[variant];
        switch (arrayWireRotationVariantToType[rotationVariant]) {
            case _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].forward: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/wires/sets/" + wireVariant + "_forward.png");
            }
            case _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/wires/sets/" + wireVariant + "_turn.png");
            }
            case _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/wires/sets/" + wireVariant + "_split.png");
            }
            case _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].cross: {
                return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite("sprites/wires/sets/" + wireVariant + "_cross.png");
            }
            default: {
                window.assert(false, "Invalid wire rotation variant");
            }
        }
    }

    getBlueprintSprite(rotationVariant, variant) {
        return this.getPreviewSprite(rotationVariant, variant);
    }

    /**
     * Should compute the optimal rotation variant on the given tile
     * @param {object} param0
     * @param {GameRoot} param0.root
     * @param {Vector} param0.tile
     * @param {number} param0.rotation
     * @param {string} param0.variant
     * @param {string} param0.layer
     * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
     */
    computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
        const wireVariant = enumWireVariantToVariant[variant];
        const connections = {
            top: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].top }),
            right: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].right }),
            bottom: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].bottom }),
            left: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: _core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirection"].left }),
        };

        let flag = 0;
        flag |= connections.top ? 0x1000 : 0;
        flag |= connections.right ? 0x100 : 0;
        flag |= connections.bottom ? 0x10 : 0;
        flag |= connections.left ? 0x1 : 0;

        let targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].forward;

        // First, reset rotation
        rotation = 0;

        switch (flag) {
            case 0x0000:
                // Nothing
                break;

            case 0x0001:
                // Left
                rotation += 90;
                break;

            case 0x0010:
                // Bottom
                // END
                break;

            case 0x0011:
                // Bottom | Left
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn;
                rotation += 90;
                break;

            case 0x0100:
                // Right
                rotation += 90;
                break;

            case 0x0101:
                // Right | Left
                rotation += 90;
                break;

            case 0x0110:
                // Right | Bottom
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn;
                break;

            case 0x0111:
                // Right | Bottom | Left
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split;
                break;

            case 0x1000:
                // Top
                break;

            case 0x1001:
                // Top | Left
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn;
                rotation += 180;
                break;

            case 0x1010:
                // Top | Bottom
                break;

            case 0x1011:
                // Top | Bottom | Left
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split;
                rotation += 90;
                break;

            case 0x1100:
                // Top | Right
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].turn;
                rotation -= 90;
                break;

            case 0x1101:
                // Top | Right | Left
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split;
                rotation += 180;
                break;

            case 0x1110:
                // Top | Right | Bottom
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].split;
                rotation -= 90;
                break;

            case 0x1111:
                // Top | Right | Bottom | Left
                targetType = _components_wire__WEBPACK_IMPORTED_MODULE_4__["enumWireType"].cross;
                break;
        }

        return {
            // Clamp rotation
            rotation: (rotation + 360 * 10) % 360,
            rotationVariant: arrayWireRotationVariantToType.indexOf(targetType),
        };
    }
}


/***/ }),

/***/ "./src/js/game/buildings/wire_tunnel.js":
/*!**********************************************!*\
  !*** ./src/js/game/buildings/wire_tunnel.js ***!
  \**********************************************/
/*! exports provided: MetaWireTunnelBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaWireTunnelBuilding", function() { return MetaWireTunnelBuilding; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_wire_tunnel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/wire_tunnel */ "./src/js/game/components/wire_tunnel.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");








const wireTunnelOverlayMatrix = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["generateMatrixRotations"])([0, 1, 0, 1, 1, 1, 0, 1, 0]);

class MetaWireTunnelBuilding extends _meta_building__WEBPACK_IMPORTED_MODULE_4__["MetaBuilding"] {
    constructor() {
        super("wire_tunnel");
    }

    getSilhouetteColor() {
        return "#777a86";
    }

    /**
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_wires_painter_and_levers);
    }

    /**
     *
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return wireTunnelOverlayMatrix[rotation];
    }

    getIsRotateable() {
        return false;
    }

    getDimensions() {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"](1, 1);
    }

    /** @returns {"wires"} **/
    getLayer() {
        return "wires";
    }

    /**
     * Creates the entity at the given location
     * @param {Entity} entity
     */
    setupEntityComponents(entity) {
        entity.addComponent(new _components_wire_tunnel__WEBPACK_IMPORTED_MODULE_2__["WireTunnelComponent"]());
    }
}


/***/ }),

/***/ "./src/js/game/camera.js":
/*!*******************************!*\
  !*** ./src/js/game/camera.js ***!
  \*******************************/
/*! exports provided: USER_INTERACT_MOVE, USER_INTERACT_ZOOM, USER_INTERACT_TOUCHEND, enumMouseButton, Camera */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "USER_INTERACT_MOVE", function() { return USER_INTERACT_MOVE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "USER_INTERACT_ZOOM", function() { return USER_INTERACT_ZOOM; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "USER_INTERACT_TOUCHEND", function() { return USER_INTERACT_TOUCHEND; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumMouseButton", function() { return enumMouseButton; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Camera", function() { return Camera; });
/* harmony import */ var _core_click_detector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");











const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("camera");

const USER_INTERACT_MOVE = "move";
const USER_INTERACT_ZOOM = "zoom";
const USER_INTERACT_TOUCHEND = "touchend";

const velocitySmoothing = 0.5;
const velocityFade = 0.98;
const velocityStrength = 0.4;
const velocityMax = 20;
const ticksBeforeErasingVelocity = 10;

/**
 * @enum {string}
 */
const enumMouseButton = {
    left: "left",
    middle: "middle",
    right: "right",
};

class Camera extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_7__["BasicSerializableObject"] {
    constructor(root) {
        super();

        /** @type {GameRoot} */
        this.root = root;

        // Zoom level, 2 means double size

        // Find optimal initial zoom

        this.zoomLevel = this.findInitialZoom();
        this.clampZoomLevel();

        /** @type {Vector} */
        this.center = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);

        // Input handling
        this.currentlyMoving = false;
        this.lastMovingPosition = null;
        this.lastMovingPositionLastTick = null;
        this.numTicksStandingStill = null;
        this.cameraUpdateTimeBucket = 0.0;
        this.didMoveSinceTouchStart = false;
        this.currentlyPinching = false;
        this.lastPinchPositions = null;

        this.keyboardForce = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"]();

        // Signal which gets emitted once the user changed something
        this.userInteraction = new _core_signal__WEBPACK_IMPORTED_MODULE_4__["Signal"]();

        /** @type {Vector} */
        this.currentShake = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);

        /** @type {Vector} */
        this.currentPan = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);

        // Set desired pan (camera movement)
        /** @type {Vector} */
        this.desiredPan = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);

        // Set desired camera center
        /** @type {Vector} */
        this.desiredCenter = null;

        // Set desired camera zoom
        /** @type {number} */
        this.desiredZoom = null;

        /** @type {Vector} */
        this.touchPostMoveVelocity = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);

        // Handlers
        this.downPreHandler = /** @type {TypedSignal<[Vector, enumMouseButton]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_4__["Signal"]());
        this.movePreHandler = /** @type {TypedSignal<[Vector]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_4__["Signal"]());
        // this.pinchPreHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal());
        this.upPostHandler = /** @type {TypedSignal<[Vector]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_4__["Signal"]());

        this.internalInitEvents();
        this.clampZoomLevel();
        this.bindKeys();
        if (false) {}
    }

    // Serialization
    static getId() {
        return "Camera";
    }

    static getSchema() {
        return {
            zoomLevel: _savegame_serialization__WEBPACK_IMPORTED_MODULE_7__["types"].float,
            center: _savegame_serialization__WEBPACK_IMPORTED_MODULE_7__["types"].vector,
        };
    }

    deserialize(data) {
        const errorCode = super.deserialize(data);
        if (errorCode) {
            return errorCode;
        }

        // Safety
        this.clampZoomLevel();
    }

    // Simple getters & setters

    addScreenShake(amount) {
        const currentShakeAmount = this.currentShake.length();
        const scale = 1 / (1 + 3 * currentShakeAmount);
        this.currentShake.x = this.currentShake.x + 2 * (Math.random() - 0.5) * scale * amount;
        this.currentShake.y = this.currentShake.y + 2 * (Math.random() - 0.5) * scale * amount;
    }

    /**
     * Sets a point in world space to focus on
     * @param {Vector} center
     */
    setDesiredCenter(center) {
        this.desiredCenter = center.copy();
        this.currentlyMoving = false;
    }

    /**
     * Sets a desired zoom level
     * @param {number} zoom
     */
    setDesiredZoom(zoom) {
        this.desiredZoom = zoom;
    }

    /**
     * Returns if this camera is currently moving by a non-user interaction
     */
    isCurrentlyMovingToDesiredCenter() {
        return this.desiredCenter !== null;
    }

    /**
     * Sets the camera pan, every frame the camera will move by this amount
     * @param {Vector} pan
     */
    setPan(pan) {
        this.desiredPan = pan.copy();
    }

    /**
     * Finds a good initial zoom level
     */
    findInitialZoom() {
        const desiredWorldSpaceWidth = 15 * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize;
        const zoomLevelX = this.root.gameWidth / desiredWorldSpaceWidth;
        const zoomLevelY = this.root.gameHeight / desiredWorldSpaceWidth;

        const finalLevel = Math.min(zoomLevelX, zoomLevelY);
        window.assert(
            Number.isFinite(finalLevel) && finalLevel > 0,
            "Invalid zoom level computed for initial zoom: " + finalLevel
        );
        return finalLevel;
    }

    /**
     * Clears all animations
     */
    clearAnimations() {
        this.touchPostMoveVelocity.x = 0;
        this.touchPostMoveVelocity.y = 0;
        this.desiredCenter = null;
        this.desiredPan.x = 0;
        this.desiredPan.y = 0;
        this.currentPan.x = 0;
        this.currentPan.y = 0;
        this.currentlyPinching = false;
        this.currentlyMoving = false;
        this.lastMovingPosition = null;
        this.didMoveSinceTouchStart = false;
        this.desiredZoom = null;
    }

    /**
     * Returns if the user is currently interacting with the camera
     * @returns {boolean} true if the user interacts
     */
    isCurrentlyInteracting() {
        if (this.currentlyPinching) {
            return true;
        }
        if (this.currentlyMoving) {
            // Only interacting if moved at least once
            return this.didMoveSinceTouchStart;
        }
        if (this.touchPostMoveVelocity.lengthSquare() > 1) {
            return true;
        }
        return false;
    }

    /**
     * Returns if in the next frame the viewport will change
     * @returns {boolean} true if it willchange
     */
    viewportWillChange() {
        return this.desiredCenter !== null || this.desiredZoom !== null || this.isCurrentlyInteracting();
    }

    /**
     * Cancels all interactions, that is user interaction and non user interaction
     */
    cancelAllInteractions() {
        this.touchPostMoveVelocity = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);
        this.desiredCenter = null;
        this.currentlyMoving = false;
        this.currentlyPinching = false;
        this.desiredZoom = null;
    }

    /**
     * Returns effective viewport width
     */
    getViewportWidth() {
        return this.root.gameWidth / this.zoomLevel;
    }

    /**
     * Returns effective viewport height
     */
    getViewportHeight() {
        return this.root.gameHeight / this.zoomLevel;
    }

    /**
     * Returns effective world space viewport left
     */
    getViewportLeft() {
        return this.center.x - this.getViewportWidth() / 2 + (this.currentShake.x * 10) / this.zoomLevel;
    }

    /**
     * Returns effective world space viewport right
     */
    getViewportRight() {
        return this.center.x + this.getViewportWidth() / 2 + (this.currentShake.x * 10) / this.zoomLevel;
    }

    /**
     * Returns effective world space viewport top
     */
    getViewportTop() {
        return this.center.y - this.getViewportHeight() / 2 + (this.currentShake.x * 10) / this.zoomLevel;
    }

    /**
     * Returns effective world space viewport bottom
     */
    getViewportBottom() {
        return this.center.y + this.getViewportHeight() / 2 + (this.currentShake.x * 10) / this.zoomLevel;
    }

    /**
     * Returns the visible world space rect
     * @returns {Rectangle}
     */
    getVisibleRect() {
        return _core_rectangle__WEBPACK_IMPORTED_MODULE_3__["Rectangle"].fromTRBL(
            Math.floor(this.getViewportTop()),
            Math.ceil(this.getViewportRight()),
            Math.ceil(this.getViewportBottom()),
            Math.floor(this.getViewportLeft())
        );
    }

    getIsMapOverlayActive() {
        return this.zoomLevel < _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkOverviewMinZoom;
    }

    /**
     * Attaches all event listeners
     */
    internalInitEvents() {
        this.eventListenerTouchStart = this.onTouchStart.bind(this);
        this.eventListenerTouchEnd = this.onTouchEnd.bind(this);
        this.eventListenerTouchMove = this.onTouchMove.bind(this);
        this.eventListenerMousewheel = this.onMouseWheel.bind(this);
        this.eventListenerMouseDown = this.onMouseDown.bind(this);
        this.eventListenerMouseMove = this.onMouseMove.bind(this);
        this.eventListenerMouseUp = this.onMouseUp.bind(this);

        if (_core_config__WEBPACK_IMPORTED_MODULE_1__["SUPPORT_TOUCH"]) {
            this.root.canvas.addEventListener("touchstart", this.eventListenerTouchStart);
            this.root.canvas.addEventListener("touchend", this.eventListenerTouchEnd);
            this.root.canvas.addEventListener("touchcancel", this.eventListenerTouchEnd);
            this.root.canvas.addEventListener("touchmove", this.eventListenerTouchMove);
        }

        this.root.canvas.addEventListener("wheel", this.eventListenerMousewheel);
        this.root.canvas.addEventListener("mousedown", this.eventListenerMouseDown);
        this.root.canvas.addEventListener("mousemove", this.eventListenerMouseMove);
        window.addEventListener("mouseup", this.eventListenerMouseUp);
        // this.root.canvas.addEventListener("mouseout", this.eventListenerMouseUp);
    }

    /**
     * Cleans up all event listeners
     */
    cleanup() {
        if (_core_config__WEBPACK_IMPORTED_MODULE_1__["SUPPORT_TOUCH"]) {
            this.root.canvas.removeEventListener("touchstart", this.eventListenerTouchStart);
            this.root.canvas.removeEventListener("touchend", this.eventListenerTouchEnd);
            this.root.canvas.removeEventListener("touchcancel", this.eventListenerTouchEnd);
            this.root.canvas.removeEventListener("touchmove", this.eventListenerTouchMove);
        }

        this.root.canvas.removeEventListener("wheel", this.eventListenerMousewheel);
        this.root.canvas.removeEventListener("mousedown", this.eventListenerMouseDown);
        this.root.canvas.removeEventListener("mousemove", this.eventListenerMouseMove);
        window.removeEventListener("mouseup", this.eventListenerMouseUp);
        // this.root.canvas.removeEventListener("mouseout", this.eventListenerMouseUp);
    }

    /**
     * Binds the arrow keys
     */
    bindKeys() {
        const mapper = this.root.keyMapper;
        mapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveUp).add(() => (this.keyboardForce.y = -1));
        mapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveDown).add(() => (this.keyboardForce.y = 1));
        mapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveRight).add(() => (this.keyboardForce.x = 1));
        mapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveLeft).add(() => (this.keyboardForce.x = -1));

        mapper
            .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapZoomIn)
            .add(() => (this.desiredZoom = this.zoomLevel * 1.2));
        mapper
            .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapZoomOut)
            .add(() => (this.desiredZoom = this.zoomLevel / 1.2));

        mapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.centerMap).add(() => this.centerOnMap());
    }

    centerOnMap() {
        this.desiredCenter = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);
    }

    /**
     * Converts from screen to world space
     * @param {Vector} screen
     * @returns {Vector} world space
     */
    screenToWorld(screen) {
        const centerSpace = screen.subScalars(this.root.gameWidth / 2, this.root.gameHeight / 2);
        return centerSpace.divideScalar(this.zoomLevel).add(this.center);
    }

    /**
     * Converts from world to screen space
     * @param {Vector} world
     * @returns {Vector} screen space
     */
    worldToScreen(world) {
        const screenSpace = world.sub(this.center).multiplyScalar(this.zoomLevel);
        return screenSpace.addScalars(this.root.gameWidth / 2, this.root.gameHeight / 2);
    }

    /**
     * Returns if a point is on screen
     * @param {Vector} point
     * @returns {boolean} true if its on screen
     */
    isWorldPointOnScreen(point) {
        const rect = this.getVisibleRect();
        return rect.containsPoint(point.x, point.y);
    }

    /**
     * Returns if we can further zoom in
     * @returns {boolean}
     */
    canZoomIn() {
        const maxLevel = this.root.app.platformWrapper.getMaximumZoom();
        return this.zoomLevel <= maxLevel - 0.01;
    }

    /**
     * Returns if we can further zoom out
     * @returns {boolean}
     */
    canZoomOut() {
        const minLevel = this.root.app.platformWrapper.getMinimumZoom();
        return this.zoomLevel >= minLevel + 0.01;
    }

    // EVENTS

    /**
     * Checks if the mouse event is too close after a touch event and thus
     * should get ignored
     */
    checkPreventDoubleMouse() {
        if (performance.now() - _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["clickDetectorGlobals"].lastTouchTime < 1000.0) {
            return false;
        }
        return true;
    }

    /**
     * Mousedown handler
     * @param {MouseEvent} event
     */
    onMouseDown(event) {
        if (event.cancelable) {
            event.preventDefault();
            // event.stopPropagation();
        }

        if (!this.checkPreventDoubleMouse()) {
            return;
        }

        this.touchPostMoveVelocity = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);
        if (event.button === 0) {
            this.combinedSingleTouchStartHandler(event.clientX, event.clientY);
        } else if (event.button === 1) {
            this.downPreHandler.dispatch(new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](event.clientX, event.clientY), enumMouseButton.middle);
        } else if (event.button === 2) {
            this.downPreHandler.dispatch(new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](event.clientX, event.clientY), enumMouseButton.right);
        }
        return false;
    }

    /**
     * Mousemove handler
     * @param {MouseEvent} event
     */
    onMouseMove(event) {
        if (event.cancelable) {
            event.preventDefault();
            // event.stopPropagation();
        }

        if (!this.checkPreventDoubleMouse()) {
            return;
        }

        if (event.button === 0) {
            this.combinedSingleTouchMoveHandler(event.clientX, event.clientY);
        }

        // Clamp everything afterwards
        this.clampZoomLevel();
        return false;
    }

    /**
     * Mouseup handler
     * @param {MouseEvent=} event
     */
    onMouseUp(event) {
        if (event) {
            if (event.cancelable) {
                event.preventDefault();
                // event.stopPropagation();
            }
        }

        if (!this.checkPreventDoubleMouse()) {
            return;
        }

        this.combinedSingleTouchStopHandler(event.clientX, event.clientY);
        return false;
    }

    /**
     * Mousewheel event
     * @param {WheelEvent} event
     */
    onMouseWheel(event) {
        if (event.cancelable) {
            event.preventDefault();
            // event.stopPropagation();
        }
        const prevZoom = this.zoomLevel;

        const scale = 1 + 0.15 * this.root.app.settings.getScrollWheelSensitivity();
        window.assert(Number.isFinite(scale), "Got invalid scale in mouse wheel event: " + event.deltaY);
        window.assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel);
        this.zoomLevel *= event.deltaY < 0 ? scale : 1 / scale;
        window.assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *after* wheel: " + this.zoomLevel);

        this.clampZoomLevel();
        this.desiredZoom = null;

        let mousePosition = this.root.app.mousePosition;
        if (!this.root.app.settings.getAllSettings().zoomToCursor) {
            mousePosition = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](this.root.gameWidth / 2, this.root.gameHeight / 2);
        }

        if (mousePosition) {
            const worldPos = this.root.camera.screenToWorld(mousePosition);
            const worldDelta = worldPos.sub(this.center);
            const actualDelta = this.zoomLevel / prevZoom - 1;
            this.center = this.center.add(worldDelta.multiplyScalar(actualDelta));
            this.desiredCenter = null;
        }

        return false;
    }

    /**
     * Touch start handler
     * @param {TouchEvent} event
     */
    onTouchStart(event) {
        if (event.cancelable) {
            event.preventDefault();
            // event.stopPropagation();
        }

        _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["clickDetectorGlobals"].lastTouchTime = performance.now();
        this.touchPostMoveVelocity = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);

        if (event.touches.length === 1) {
            const touch = event.touches[0];
            this.combinedSingleTouchStartHandler(touch.clientX, touch.clientY);
        } else if (event.touches.length === 2) {
            // if (this.pinchPreHandler.dispatch() === STOP_PROPAGATION) {
            //     // Something prevented pinching
            //     return false;
            // }

            const touch1 = event.touches[0];
            const touch2 = event.touches[1];
            this.currentlyMoving = false;
            this.currentlyPinching = true;
            this.lastPinchPositions = [
                new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](touch1.clientX, touch1.clientY),
                new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](touch2.clientX, touch2.clientY),
            ];
        }
        return false;
    }

    /**
     * Touch move handler
     * @param {TouchEvent} event
     */
    onTouchMove(event) {
        if (event.cancelable) {
            event.preventDefault();
            // event.stopPropagation();
        }

        _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["clickDetectorGlobals"].lastTouchTime = performance.now();

        if (event.touches.length === 1) {
            const touch = event.touches[0];
            this.combinedSingleTouchMoveHandler(touch.clientX, touch.clientY);
        } else if (event.touches.length === 2) {
            if (this.currentlyPinching) {
                const touch1 = event.touches[0];
                const touch2 = event.touches[1];

                const newPinchPositions = [
                    new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](touch1.clientX, touch1.clientY),
                    new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](touch2.clientX, touch2.clientY),
                ];

                // Get distance of taps last time and now
                const lastDistance = this.lastPinchPositions[0].distance(this.lastPinchPositions[1]);
                const thisDistance = newPinchPositions[0].distance(newPinchPositions[1]);

                // IMPORTANT to do math max here to avoid NaN and causing an invalid zoom level
                const difference = thisDistance / Math.max(0.001, lastDistance);

                // Find old center of zoom
                let oldCenter = this.lastPinchPositions[0].centerPoint(this.lastPinchPositions[1]);

                // Find new center of zoom
                let center = newPinchPositions[0].centerPoint(newPinchPositions[1]);

                // Compute movement
                let movement = oldCenter.sub(center);
                this.center.x += movement.x / this.zoomLevel;
                this.center.y += movement.y / this.zoomLevel;

                // Compute zoom
                center = center.sub(new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](this.root.gameWidth / 2, this.root.gameHeight / 2));

                // Apply zoom
                window.assert(
                    Number.isFinite(difference),
                    "Invalid pinch difference: " +
                        difference +
                        "(last=" +
                        lastDistance +
                        ", new = " +
                        thisDistance +
                        ")"
                );
                this.zoomLevel *= difference;

                // Stick to pivot point
                const correcture = center.multiplyScalar(difference - 1).divideScalar(this.zoomLevel);

                this.center = this.center.add(correcture);
                this.lastPinchPositions = newPinchPositions;
                this.userInteraction.dispatch(USER_INTERACT_MOVE);

                // Since we zoomed, abort any programmed zooming
                if (this.desiredZoom) {
                    this.desiredZoom = null;
                }
            }
        }

        // Clamp everything afterwards
        this.clampZoomLevel();
        return false;
    }

    /**
     * Touch end and cancel handler
     * @param {TouchEvent=} event
     */
    onTouchEnd(event) {
        if (event) {
            if (event.cancelable) {
                event.preventDefault();
                // event.stopPropagation();
            }
        }

        _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["clickDetectorGlobals"].lastTouchTime = performance.now();
        if (event.changedTouches.length === 0) {
            logger.warn("Touch end without changed touches");
        }

        const touch = event.changedTouches[0];
        this.combinedSingleTouchStopHandler(touch.clientX, touch.clientY);
        return false;
    }

    /**
     * Internal touch start handler
     * @param {number} x
     * @param {number} y
     */
    combinedSingleTouchStartHandler(x, y) {
        const pos = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](x, y);
        if (this.downPreHandler.dispatch(pos, enumMouseButton.left) === _core_signal__WEBPACK_IMPORTED_MODULE_4__["STOP_PROPAGATION"]) {
            // Somebody else captured it
            return;
        }

        this.touchPostMoveVelocity = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](0, 0);
        this.currentlyMoving = true;
        this.lastMovingPosition = pos;
        this.lastMovingPositionLastTick = null;
        this.numTicksStandingStill = 0;
        this.didMoveSinceTouchStart = false;
    }

    /**
     * Internal touch move handler
     * @param {number} x
     * @param {number} y
     */
    combinedSingleTouchMoveHandler(x, y) {
        const pos = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](x, y);
        if (this.movePreHandler.dispatch(pos) === _core_signal__WEBPACK_IMPORTED_MODULE_4__["STOP_PROPAGATION"]) {
            // Somebody else captured it
            return;
        }

        if (!this.currentlyMoving) {
            return false;
        }

        let delta = this.lastMovingPosition.sub(pos).divideScalar(this.zoomLevel);
        if (false) {}

        this.didMoveSinceTouchStart = this.didMoveSinceTouchStart || delta.length() > 0;
        this.center = this.center.add(delta);

        this.touchPostMoveVelocity = this.touchPostMoveVelocity
            .multiplyScalar(velocitySmoothing)
            .add(delta.multiplyScalar(1 - velocitySmoothing));

        this.lastMovingPosition = pos;
        this.userInteraction.dispatch(USER_INTERACT_MOVE);

        // Since we moved, abort any programmed moving
        if (this.desiredCenter) {
            this.desiredCenter = null;
        }
    }

    /**
     * Internal touch stop handler
     */
    combinedSingleTouchStopHandler(x, y) {
        if (this.currentlyMoving || this.currentlyPinching) {
            this.currentlyMoving = false;
            this.currentlyPinching = false;
            this.lastMovingPosition = null;
            this.lastMovingPositionLastTick = null;
            this.numTicksStandingStill = 0;
            this.lastPinchPositions = null;
            this.userInteraction.dispatch(USER_INTERACT_TOUCHEND);
            this.didMoveSinceTouchStart = false;
        }
        this.upPostHandler.dispatch(new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"](x, y));
    }

    /**
     * Clamps the camera zoom level within the allowed range
     */
    clampZoomLevel() {
        if (false) {}
        const wrapper = this.root.app.platformWrapper;

        window.assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *before* clamp: " + this.zoomLevel);
        this.zoomLevel = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["clamp"])(this.zoomLevel, wrapper.getMinimumZoom(), wrapper.getMaximumZoom());
        window.assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *after* clamp: " + this.zoomLevel);

        if (this.desiredZoom) {
            this.desiredZoom = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["clamp"])(this.desiredZoom, wrapper.getMinimumZoom(), wrapper.getMaximumZoom());
        }
    }

    /**
     * Updates the camera
     * @param {number} dt Delta time in milliseconds
     */
    update(dt) {
        dt = Math.min(dt, 33);
        this.cameraUpdateTimeBucket += dt;

        // Simulate movement of N FPS
        const updatesPerFrame = 4;
        const physicsStepSizeMs = 1000.0 / (60.0 * updatesPerFrame);

        let now = this.root.time.systemNow() - 3 * physicsStepSizeMs;

        while (this.cameraUpdateTimeBucket > physicsStepSizeMs) {
            now += physicsStepSizeMs;
            this.cameraUpdateTimeBucket -= physicsStepSizeMs;

            this.internalUpdatePanning(now, physicsStepSizeMs);
            this.internalUpdateMousePanning(now, physicsStepSizeMs);
            this.internalUpdateZooming(now, physicsStepSizeMs);
            this.internalUpdateCentering(now, physicsStepSizeMs);
            this.internalUpdateShake(now, physicsStepSizeMs);
            this.internalUpdateKeyboardForce(now, physicsStepSizeMs);
        }
        this.clampZoomLevel();
    }

    /**
     * Prepares a context to transform it
     * @param {CanvasRenderingContext2D} context
     */
    transform(context) {
        if (false) {}

        this.clampZoomLevel();
        const zoom = this.zoomLevel;

        context.transform(
            // Scale, skew, rotate
            zoom,
            0,
            0,
            zoom,

            // Translate
            -zoom * this.getViewportLeft(),
            -zoom * this.getViewportTop()
        );
    }

    /**
     * Internal shake handler
     * @param {number} now Time now in seconds
     * @param {number} dt Delta time
     */
    internalUpdateShake(now, dt) {
        this.currentShake = this.currentShake.multiplyScalar(0.92);
    }

    /**
     * Internal pan handler
     * @param {number} now Time now in seconds
     * @param {number} dt Delta time
     */
    internalUpdatePanning(now, dt) {
        const baseStrength = velocityStrength * this.root.app.platformWrapper.getTouchPanStrength();

        this.touchPostMoveVelocity = this.touchPostMoveVelocity.multiplyScalar(velocityFade);

        // Check if the camera is being dragged but standing still: if not, zero out `touchPostMoveVelocity`.
        if (this.currentlyMoving && this.desiredCenter === null) {
            if (
                this.lastMovingPositionLastTick !== null &&
                this.lastMovingPositionLastTick.equalsEpsilon(this.lastMovingPosition)
            ) {
                this.numTicksStandingStill++;
            } else {
                this.numTicksStandingStill = 0;
            }
            this.lastMovingPositionLastTick = this.lastMovingPosition.copy();

            if (this.numTicksStandingStill >= ticksBeforeErasingVelocity) {
                this.touchPostMoveVelocity.x = 0;
                this.touchPostMoveVelocity.y = 0;
            }
        }
        // Check influence of past points
        if (!this.currentlyMoving && !this.currentlyPinching) {
            const len = this.touchPostMoveVelocity.length();
            if (len >= velocityMax) {
                this.touchPostMoveVelocity.x = (this.touchPostMoveVelocity.x * velocityMax) / len;
                this.touchPostMoveVelocity.y = (this.touchPostMoveVelocity.y * velocityMax) / len;
            }

            this.center = this.center.add(this.touchPostMoveVelocity.multiplyScalar(baseStrength));

            // Panning
            this.currentPan = Object(_core_vector__WEBPACK_IMPORTED_MODULE_6__["mixVector"])(this.currentPan, this.desiredPan, 0.06);
            this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel));
        }
    }

    /**
     * Internal screen panning handler
     * @param {number} now
     * @param {number} dt
     */
    internalUpdateMousePanning(now, dt) {
        if (!this.root.app.focused) {
            return;
        }

        if (!this.root.app.settings.getAllSettings().enableMousePan) {
            // Not enabled
            return;
        }

        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            return;
        }

        if (this.root.hud.shouldPauseGame() || this.root.hud.hasBlockingOverlayOpen()) {
            return;
        }

        if (this.desiredCenter || this.desiredZoom || this.currentlyMoving || this.currentlyPinching) {
            // Performing another method of movement right now
            return;
        }

        if (
            mousePos.x < 0 ||
            mousePos.y < 0 ||
            mousePos.x > this.root.gameWidth ||
            mousePos.y > this.root.gameHeight
        ) {
            // Out of screen
            return;
        }

        const panAreaPixels = 2;

        const panVelocity = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"]();
        if (mousePos.x < panAreaPixels) {
            panVelocity.x -= 1;
        }
        if (mousePos.x > this.root.gameWidth - panAreaPixels) {
            panVelocity.x += 1;
        }

        if (mousePos.y < panAreaPixels) {
            panVelocity.y -= 1;
        }
        if (mousePos.y > this.root.gameHeight - panAreaPixels) {
            panVelocity.y += 1;
        }

        this.center = this.center.add(
            panVelocity.multiplyScalar(
                ((0.5 * dt) / this.zoomLevel) * this.root.app.settings.getMovementSpeed()
            )
        );
    }

    /**
     * Updates the non user interaction zooming
     * @param {number} now Time now in seconds
     * @param {number} dt Delta time
     */
    internalUpdateZooming(now, dt) {
        if (!this.currentlyPinching && this.desiredZoom !== null) {
            const diff = this.zoomLevel - this.desiredZoom;
            if (Math.abs(diff) > 0.0001) {
                let fade = 0.94;
                if (diff > 0) {
                    // Zoom out faster than in
                    fade = 0.9;
                }

                window.assert(Number.isFinite(this.desiredZoom), "Desired zoom is NaN: " + this.desiredZoom);
                window.assert(Number.isFinite(fade), "Zoom fade is NaN: " + fade);
                this.zoomLevel = this.zoomLevel * fade + this.desiredZoom * (1 - fade);
                window.assert(Number.isFinite(this.zoomLevel), "Zoom level is NaN after fade: " + this.zoomLevel);
            } else {
                this.zoomLevel = this.desiredZoom;
                this.desiredZoom = null;
            }
        }
    }

    /**
     * Updates the non user interaction centering
     * @param {number} now Time now in seconds
     * @param {number} dt Delta time
     */
    internalUpdateCentering(now, dt) {
        if (!this.currentlyMoving && this.desiredCenter !== null) {
            const diff = this.center.direction(this.desiredCenter);
            const length = diff.length();
            const tolerance = 1 / this.zoomLevel;
            if (length > tolerance) {
                const movement = diff.multiplyScalar(Math.min(1, dt * 0.008));
                this.center.x += movement.x;
                this.center.y += movement.y;
            } else {
                this.desiredCenter = null;
            }
        }
    }

    /**
     * Updates the keyboard forces
     * @param {number} now
     * @param {number} dt Delta time
     */
    internalUpdateKeyboardForce(now, dt) {
        if (!this.currentlyMoving && this.desiredCenter == null) {
            const limitingDimension = Math.min(this.root.gameWidth, this.root.gameHeight);

            const moveAmount = ((limitingDimension / 2048) * dt) / this.zoomLevel;

            let forceX = 0;
            let forceY = 0;

            const actionMapper = this.root.keyMapper;
            if (actionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveUp).pressed) {
                forceY -= 1;
            }

            if (actionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveDown).pressed) {
                forceY += 1;
            }

            if (actionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveLeft).pressed) {
                forceX -= 1;
            }

            if (actionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveRight).pressed) {
                forceX += 1;
            }

            let movementSpeed =
                this.root.app.settings.getMovementSpeed() *
                (actionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].navigation.mapMoveFaster).pressed ? 4 : 1);

            this.center.x += moveAmount * forceX * movementSpeed;
            this.center.y += moveAmount * forceY * movementSpeed;
        }
    }
}


/***/ }),

/***/ "./src/js/game/colors.js":
/*!*******************************!*\
  !*** ./src/js/game/colors.js ***!
  \*******************************/
/*! exports provided: enumColors, enumColorToShortcode, enumShortcodeToColor, enumColorsToHexCode, enumColorMixingResults */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumColors", function() { return enumColors; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumColorToShortcode", function() { return enumColorToShortcode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumShortcodeToColor", function() { return enumShortcodeToColor; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumColorsToHexCode", function() { return enumColorsToHexCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumColorMixingResults", function() { return enumColorMixingResults; });
/** @enum {string} */
const enumColors = {
    red: "red",
    green: "green",
    blue: "blue",

    yellow: "yellow",
    purple: "purple",
    cyan: "cyan",

    white: "white",
    uncolored: "uncolored",
};

/** @enum {string} */
const enumColorToShortcode = {
    [enumColors.red]: "r",
    [enumColors.green]: "g",
    [enumColors.blue]: "b",

    [enumColors.yellow]: "y",
    [enumColors.purple]: "p",
    [enumColors.cyan]: "c",

    [enumColors.white]: "w",
    [enumColors.uncolored]: "u",
};

/** @enum {enumColors} */
const enumShortcodeToColor = {};
for (const key in enumColorToShortcode) {
    enumShortcodeToColor[enumColorToShortcode[key]] = key;
}

/** @enum {string} */
const enumColorsToHexCode = {
    [enumColors.red]: "#ff666a",
    [enumColors.green]: "#78ff66",
    [enumColors.blue]: "#66a7ff",

    // red + green
    [enumColors.yellow]: "#fcf52a",

    // red + blue
    [enumColors.purple]: "#dd66ff",

    // blue + green
    [enumColors.cyan]: "#00fcff",

    // blue + green + red
    [enumColors.white]: "#ffffff",

    [enumColors.uncolored]: "#aaaaaa",
};

const c = enumColors;
/** @enum {Object.<string, string>} */
const enumColorMixingResults = {
    // 255, 0, 0
    [c.red]: {
        [c.green]: c.yellow,
        [c.blue]: c.purple,

        [c.yellow]: c.yellow,
        [c.purple]: c.purple,
        [c.cyan]: c.white,

        [c.white]: c.white,
    },

    // 0, 255, 0
    [c.green]: {
        [c.blue]: c.cyan,

        [c.yellow]: c.yellow,
        [c.purple]: c.white,
        [c.cyan]: c.cyan,

        [c.white]: c.white,
    },

    // 0, 255, 0
    [c.blue]: {
        [c.yellow]: c.white,
        [c.purple]: c.purple,
        [c.cyan]: c.cyan,

        [c.white]: c.white,
    },

    // 255, 255, 0
    [c.yellow]: {
        [c.purple]: c.white,
        [c.cyan]: c.white,
    },

    // 255, 0, 255
    [c.purple]: {
        [c.cyan]: c.white,
    },

    // 0, 255, 255
    [c.cyan]: {},

    //// SPECIAL COLORS

    // 255, 255, 255
    [c.white]: {
        // auto
    },

    // X, X, X
    [c.uncolored]: {
        // auto
    },
};

// Create same color lookups
for (const color in enumColors) {
    enumColorMixingResults[color][color] = color;
    enumColorMixingResults[color][c.white] = c.white;
    // Anything with uncolored is the same color
    enumColorMixingResults[color][c.uncolored] = color;
}

// Create reverse lookup and check color mixing lookups
for (const colorA in enumColorMixingResults) {
    for (const colorB in enumColorMixingResults[colorA]) {
        const resultColor = enumColorMixingResults[colorA][colorB];
        if (!enumColorMixingResults[colorB]) {
            enumColorMixingResults[colorB] = {
                [colorA]: resultColor,
            };
        } else {
            const existingResult = enumColorMixingResults[colorB][colorA];
            if (existingResult && existingResult !== resultColor) {
                window.assert(
                    false,
                    "invalid color mixing configuration, " +
                        colorA +
                        " + " +
                        colorB +
                        " is " +
                        resultColor +
                        " but " +
                        colorB +
                        " + " +
                        colorA +
                        " is " +
                        existingResult
                );
            }
            enumColorMixingResults[colorB][colorA] = resultColor;
        }
    }
}

for (const colorA in enumColorMixingResults) {
    for (const colorB in enumColorMixingResults) {
        if (!enumColorMixingResults[colorA][colorB]) {
            window.assert(false, "Color mixing of", colorA, "with", colorB, "is not defined");
        }
    }
}


/***/ }),

/***/ "./src/js/game/component.js":
/*!**********************************!*\
  !*** ./src/js/game/component.js ***!
  \**********************************/
/*! exports provided: Component */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Component", function() { return Component; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");


class Component extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["BasicSerializableObject"] {
    /**
     * Returns the components unique id
     * @returns {string}
     */
    static getId() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "unknown-component";
    }

    /**
     * Should return the schema used for serialization
     */
    static getSchema() {
        return {};
    }

    /**
     * Copy the current state to another component
     * @param {Component} otherComponent
     */
    copyAdditionalStateTo(otherComponent) {}

    /* dev:start */

    /**
     * Fixes typeof DerivedComponent is not assignable to typeof Component, compiled out
     * in non-dev builds
     */
    constructor(...args) {
        super();
    }

    /**
     * Returns a string representing the components data, only in dev builds
     * @returns {string}
     */
    getDebugString() {
        return null;
    }
    /* dev:end */
}

/**
 * TypeScript does not support Abstract Static methods (https://github.com/microsoft/TypeScript/issues/34516)
 * One workaround is to declare the type of the component and reference that for static methods
 * @typedef {typeof Component} StaticComponent
 */


/***/ }),

/***/ "./src/js/game/component_registry.js":
/*!*******************************************!*\
  !*** ./src/js/game/component_registry.js ***!
  \*******************************************/
/*! exports provided: initComponentRegistry */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initComponentRegistry", function() { return initComponentRegistry; });
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _components_static_map_entity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/static_map_entity */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _components_belt__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/belt */ "./src/js/game/components/belt.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_miner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./components/miner */ "./src/js/game/components/miner.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./components/underground_belt */ "./src/js/game/components/underground_belt.js");
/* harmony import */ var _components_hub__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./components/hub */ "./src/js/game/components/hub.js");
/* harmony import */ var _components_storage__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./components/storage */ "./src/js/game/components/storage.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _components_belt_underlays__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./components/belt_underlays */ "./src/js/game/components/belt_underlays.js");
/* harmony import */ var _components_wire__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./components/wire */ "./src/js/game/components/wire.js");
/* harmony import */ var _components_constant_signal__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./components/constant_signal */ "./src/js/game/components/constant_signal.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _components_lever__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./components/lever */ "./src/js/game/components/lever.js");
/* harmony import */ var _components_wire_tunnel__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./components/wire_tunnel */ "./src/js/game/components/wire_tunnel.js");
/* harmony import */ var _components_display__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./components/display */ "./src/js/game/components/display.js");
/* harmony import */ var _components_belt_reader__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./components/belt_reader */ "./src/js/game/components/belt_reader.js");
/* harmony import */ var _components_filter__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./components/filter */ "./src/js/game/components/filter.js");
/* harmony import */ var _components_item_producer__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./components/item_producer */ "./src/js/game/components/item_producer.js");






















function initComponentRegistry() {
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_static_map_entity__WEBPACK_IMPORTED_MODULE_1__["StaticMapEntityComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_belt__WEBPACK_IMPORTED_MODULE_2__["BeltComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_item_ejector__WEBPACK_IMPORTED_MODULE_3__["ItemEjectorComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_item_acceptor__WEBPACK_IMPORTED_MODULE_4__["ItemAcceptorComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_miner__WEBPACK_IMPORTED_MODULE_5__["MinerComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_item_processor__WEBPACK_IMPORTED_MODULE_6__["ItemProcessorComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["UndergroundBeltComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_hub__WEBPACK_IMPORTED_MODULE_8__["HubComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_storage__WEBPACK_IMPORTED_MODULE_9__["StorageComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_wired_pins__WEBPACK_IMPORTED_MODULE_10__["WiredPinsComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_belt_underlays__WEBPACK_IMPORTED_MODULE_11__["BeltUnderlaysComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_wire__WEBPACK_IMPORTED_MODULE_12__["WireComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_constant_signal__WEBPACK_IMPORTED_MODULE_13__["ConstantSignalComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_logic_gate__WEBPACK_IMPORTED_MODULE_14__["LogicGateComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_lever__WEBPACK_IMPORTED_MODULE_15__["LeverComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_wire_tunnel__WEBPACK_IMPORTED_MODULE_16__["WireTunnelComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_display__WEBPACK_IMPORTED_MODULE_17__["DisplayComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_belt_reader__WEBPACK_IMPORTED_MODULE_18__["BeltReaderComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_filter__WEBPACK_IMPORTED_MODULE_19__["FilterComponent"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].register(_components_item_producer__WEBPACK_IMPORTED_MODULE_20__["ItemProducerComponent"]);

    // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS

    // Sanity check - If this is thrown, you (=me, lol) forgot to add a new component here

    window.assert(
        // @ts-ignore
        __webpack_require__("./src/js/game/components sync .*\\.js/").keys().length ===
            _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].getNumEntries(),
        "Not all components are registered"
    );

    console.log("📦 There are", _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gComponentRegistry"].getNumEntries(), "components");
}


/***/ }),

/***/ "./src/js/game/components sync .*\\.js/":
/*!**********************************************************!*\
  !*** ./src/js/game/components sync nonrecursive .*\.js/ ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var map = {
	"./belt.js": "./src/js/game/components/belt.js",
	"./belt_reader.js": "./src/js/game/components/belt_reader.js",
	"./belt_underlays.js": "./src/js/game/components/belt_underlays.js",
	"./constant_signal.js": "./src/js/game/components/constant_signal.js",
	"./display.js": "./src/js/game/components/display.js",
	"./filter.js": "./src/js/game/components/filter.js",
	"./hub.js": "./src/js/game/components/hub.js",
	"./item_acceptor.js": "./src/js/game/components/item_acceptor.js",
	"./item_ejector.js": "./src/js/game/components/item_ejector.js",
	"./item_processor.js": "./src/js/game/components/item_processor.js",
	"./item_producer.js": "./src/js/game/components/item_producer.js",
	"./lever.js": "./src/js/game/components/lever.js",
	"./logic_gate.js": "./src/js/game/components/logic_gate.js",
	"./miner.js": "./src/js/game/components/miner.js",
	"./static_map_entity.js": "./src/js/game/components/static_map_entity.js",
	"./storage.js": "./src/js/game/components/storage.js",
	"./underground_belt.js": "./src/js/game/components/underground_belt.js",
	"./wire.js": "./src/js/game/components/wire.js",
	"./wire_tunnel.js": "./src/js/game/components/wire_tunnel.js",
	"./wired_pins.js": "./src/js/game/components/wired_pins.js"
};


function webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}
function webpackContextResolve(req) {
	if(!__webpack_require__.o(map, req)) {
		var e = new Error("Cannot find module '" + req + "'");
		e.code = 'MODULE_NOT_FOUND';
		throw e;
	}
	return map[req];
}
webpackContext.keys = function webpackContextKeys() {
	return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "./src/js/game/components sync .*\\.js/";

/***/ }),

/***/ "./src/js/game/components/belt.js":
/*!****************************************!*\
  !*** ./src/js/game/components/belt.js ***!
  \****************************************/
/*! exports provided: curvedBeltLength, FAKE_BELT_ACCEPTOR_SLOT, FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION, BeltComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "curvedBeltLength", function() { return curvedBeltLength; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FAKE_BELT_ACCEPTOR_SLOT", function() { return FAKE_BELT_ACCEPTOR_SLOT; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION", function() { return FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltComponent", function() { return BeltComponent; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _belt_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../belt_path */ "./src/js/game/belt_path.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");





const curvedBeltLength = /* Math.PI / 4 */ 0.78;

/** @type {import("./item_acceptor").ItemAcceptorSlot} */
const FAKE_BELT_ACCEPTOR_SLOT = {
    pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
    directions: [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].bottom],
};

/** @type {Object<enumDirection, import("./item_ejector").ItemEjectorSlot>} */
const FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION = {
    [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top]: {
        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top,
        item: null,
        progress: 0,
    },

    [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right]: {
        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right,
        item: null,
        progress: 0,
    },

    [_core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left]: {
        pos: new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0),
        direction: _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left,
        item: null,
        progress: 0,
    },
};

class BeltComponent extends _component__WEBPACK_IMPORTED_MODULE_3__["Component"] {
    static getId() {
        return "Belt";
    }

    /**
     *
     * @param {object} param0
     * @param {enumDirection=} param0.direction The direction of the belt
     */
    constructor({ direction = _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top }) {
        super();

        this.direction = direction;

        /**
         * The path this belt is contained in, not serialized
         * @type {BeltPath}
         */
        this.assignedPath = null;
    }

    /**
     * Returns the effective length of this belt in tile space
     * @returns {number}
     */
    getEffectiveLengthTiles() {
        return this.direction === _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top ? 1.0 : curvedBeltLength;
    }

    /**
     * Returns fake acceptor slot used for matching
     * @returns {import("./item_acceptor").ItemAcceptorSlot}
     */
    getFakeAcceptorSlot() {
        return FAKE_BELT_ACCEPTOR_SLOT;
    }

    /**
     * Returns fake acceptor slot used for matching
     * @returns {import("./item_ejector").ItemEjectorSlot}
     */
    getFakeEjectorSlot() {
        window.assert(
            FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION[this.direction],
            "Invalid belt direction: ",
            this.direction
        );
        return FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION[this.direction];
    }

    /**
     * Converts from belt space (0 = start of belt ... 1 = end of belt) to the local
     * belt coordinates (-0.5|-0.5 to 0.5|0.5)
     * @param {number} progress
     * @returns {Vector}
     */
    transformBeltToLocalSpace(progress) {
        window.assert(progress >= 0.0, "Invalid progress ( < 0): " + progress);
        switch (this.direction) {
            case _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].top:
                window.assert(progress <= 1.02, "Invalid progress: " + progress);
                return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0.5 - progress);

            case _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].right: {
                window.assert(progress <= curvedBeltLength + 0.02, "Invalid progress 2: " + progress);
                const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
                return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0.5 - 0.5 * Math.cos(arcProgress), 0.5 - 0.5 * Math.sin(arcProgress));
            }
            case _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirection"].left: {
                window.assert(progress <= curvedBeltLength + 0.02, "Invalid progress 3: " + progress);
                const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
                return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](-0.5 + 0.5 * Math.cos(arcProgress), 0.5 - 0.5 * Math.sin(arcProgress));
            }
            default:
                window.assert(false, "Invalid belt direction: " + this.direction);
                return new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](0, 0);
        }
    }
}


/***/ }),

/***/ "./src/js/game/components/belt_reader.js":
/*!***********************************************!*\
  !*** ./src/js/game/components/belt_reader.js ***!
  \***********************************************/
/*! exports provided: BeltReaderComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltReaderComponent", function() { return BeltReaderComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");





class BeltReaderComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "BeltReader";
    }

    static getSchema() {
        return {
            lastItem: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].nullable(_item_resolver__WEBPACK_IMPORTED_MODULE_2__["typeItemSingleton"]),
        };
    }

    constructor() {
        super();

        /**
         * Which items went through the reader, we only store the time
         * @type {Array<number>}
         */
        this.lastItemTimes = [];

        /**
         * Which item passed the reader last
         * @type {BaseItem}
         */
        this.lastItem = null;

        /**
         * Stores the last throughput we computed
         * @type {number}
         */
        this.lastThroughput = 0;

        /**
         * Stores when we last computed the throughput
         * @type {number}
         */
        this.lastThroughputComputation = 0;
    }
}


/***/ }),

/***/ "./src/js/game/components/belt_underlays.js":
/*!**************************************************!*\
  !*** ./src/js/game/components/belt_underlays.js ***!
  \**************************************************/
/*! exports provided: enumClippedBeltUnderlayType, BeltUnderlaysComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumClippedBeltUnderlayType", function() { return enumClippedBeltUnderlayType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltUnderlaysComponent", function() { return BeltUnderlaysComponent; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");



/**
 * Store which type an underlay is, this is cached so we can easily
 * render it.
 *
 * Full: Render underlay at top and bottom of tile
 * Bottom Only: Only render underlay at the bottom half
 * Top Only:
 * @enum {string}
 */
const enumClippedBeltUnderlayType = {
    full: "full",
    bottomOnly: "bottomOnly",
    topOnly: "topOnly",
    none: "none",
};

/**
 * @typedef {{
 *   pos: Vector,
 *   direction: enumDirection,
 *   cachedType?: enumClippedBeltUnderlayType
 * }} BeltUnderlayTile
 */

class BeltUnderlaysComponent extends _component__WEBPACK_IMPORTED_MODULE_1__["Component"] {
    static getId() {
        return "BeltUnderlays";
    }

    /**
     * @param {object} param0
     * @param {Array<BeltUnderlayTile>=} param0.underlays Where to render belt underlays
     */
    constructor({ underlays = [] }) {
        super();
        this.underlays = underlays;
    }
}


/***/ }),

/***/ "./src/js/game/components/constant_signal.js":
/*!***************************************************!*\
  !*** ./src/js/game/components/constant_signal.js ***!
  \***************************************************/
/*! exports provided: ConstantSignalComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ConstantSignalComponent", function() { return ConstantSignalComponent; });
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");






class ConstantSignalComponent extends _component__WEBPACK_IMPORTED_MODULE_2__["Component"] {
    static getId() {
        return "ConstantSignal";
    }

    static getSchema() {
        return {
            signal: _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].nullable(_item_resolver__WEBPACK_IMPORTED_MODULE_4__["typeItemSingleton"]),
        };
    }

    /**
     * Copy the current state to another component
     * @param {ConstantSignalComponent} otherComponent
     */
    copyAdditionalStateTo(otherComponent) {
        otherComponent.signal = this.signal;
    }

    /**
     *
     * @param {object} param0
     * @param {BaseItem=} param0.signal The signal to store
     */
    constructor({ signal = null }) {
        super();
        this.signal = signal;
    }
}


/***/ }),

/***/ "./src/js/game/components/display.js":
/*!*******************************************!*\
  !*** ./src/js/game/components/display.js ***!
  \*******************************************/
/*! exports provided: DisplayComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DisplayComponent", function() { return DisplayComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");


class DisplayComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "Display";
    }
}


/***/ }),

/***/ "./src/js/game/components/filter.js":
/*!******************************************!*\
  !*** ./src/js/game/components/filter.js ***!
  \******************************************/
/*! exports provided: FilterComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FilterComponent", function() { return FilterComponent; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");





/**
 * @typedef {{
 *   item: BaseItem,
 *   progress: number
 * }} PendingFilterItem
 */

class FilterComponent extends _component__WEBPACK_IMPORTED_MODULE_2__["Component"] {
    static getId() {
        return "Filter";
    }

    duplicateWithoutContents() {
        return new FilterComponent();
    }

    static getSchema() {
        return {
            pendingItemsToLeaveThrough: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].array(
                _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].structured({
                    item: _item_resolver__WEBPACK_IMPORTED_MODULE_3__["typeItemSingleton"],
                    progress: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].ufloat,
                })
            ),

            pendingItemsToReject: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].array(
                _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].structured({
                    item: _item_resolver__WEBPACK_IMPORTED_MODULE_3__["typeItemSingleton"],
                    progress: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].ufloat,
                })
            ),
        };
    }

    constructor() {
        super();

        /**
         * Items in queue to leave through
         * @type {Array<PendingFilterItem>}
         */
        this.pendingItemsToLeaveThrough = [];

        /**
         * Items in queue to reject
         * @type {Array<PendingFilterItem>}
         */
        this.pendingItemsToReject = [];
    }
}


/***/ }),

/***/ "./src/js/game/components/hub.js":
/*!***************************************!*\
  !*** ./src/js/game/components/hub.js ***!
  \***************************************/
/*! exports provided: HubComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HubComponent", function() { return HubComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");


class HubComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "Hub";
    }
}


/***/ }),

/***/ "./src/js/game/components/item_acceptor.js":
/*!*************************************************!*\
  !*** ./src/js/game/components/item_acceptor.js ***!
  \*************************************************/
/*! exports provided: ItemAcceptorComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemAcceptorComponent", function() { return ItemAcceptorComponent; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");





/** @typedef {{
 * pos: Vector,
 * directions: enumDirection[],
 * filter?: ItemType
 * }} ItemAcceptorSlot */

/**
 * Contains information about a slot plus its location
 * @typedef {{
 *  slot: ItemAcceptorSlot,
 *  index: number,
 *  acceptedDirection: enumDirection
 * }} ItemAcceptorLocatedSlot */

/** @typedef {{
 * pos: Vector,
 * directions: enumDirection[],
 * filter?: ItemType
 * }} ItemAcceptorSlotConfig */

class ItemAcceptorComponent extends _component__WEBPACK_IMPORTED_MODULE_3__["Component"] {
    static getId() {
        return "ItemAcceptor";
    }

    /**
     *
     * @param {object} param0
     * @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items
     */
    constructor({ slots = [] }) {
        super();

        /**
         * Fixes belt animations
         * @type {Array<{
         *  item: BaseItem,
         * slotIndex: number,
         * animProgress: number,
         * direction: enumDirection
         * }>}
         */
        this.itemConsumptionAnimations = [];

        this.setSlots(slots);
    }

    /**
     *
     * @param {Array<ItemAcceptorSlotConfig>} slots
     */
    setSlots(slots) {
        /** @type {Array<ItemAcceptorSlot>} */
        this.slots = [];
        for (let i = 0; i < slots.length; ++i) {
            const slot = slots[i];
            this.slots.push({
                pos: slot.pos,
                directions: slot.directions,

                // Which type of item to accept (shape | color | all) @see ItemType
                filter: slot.filter,
            });
        }
    }

    /**
     * Returns if this acceptor can accept a new item at slot N
     * @param {number} slotIndex
     * @param {BaseItem=} item
     */
    canAcceptItem(slotIndex, item) {
        const slot = this.slots[slotIndex];
        return !slot.filter || slot.filter === item.getItemType();
    }

    /**
     * Called when an item has been accepted so that
     * @param {number} slotIndex
     * @param {enumDirection} direction
     * @param {BaseItem} item
     * @param {number} remainingProgress World space remaining progress, can be set to set the start position of the item
     */
    onItemAccepted(slotIndex, direction, item, remainingProgress = 0.0) {
        this.itemConsumptionAnimations.push({
            item,
            slotIndex,
            direction,
            animProgress: Math.min(1, remainingProgress * 2),
        });
    }

    /**
     * Tries to find a slot which accepts the current item
     * @param {Vector} targetLocalTile
     * @param {enumDirection} fromLocalDirection
     * @returns {ItemAcceptorLocatedSlot|null}
     */
    findMatchingSlot(targetLocalTile, fromLocalDirection) {
        // We need to invert our direction since the acceptor specifies *from* which direction
        // it accepts items, but the ejector specifies *into* which direction it ejects items.
        // E.g.: Ejector ejects into "right" direction but acceptor accepts from "left" direction.
        const desiredDirection = _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumInvertedDirections"][fromLocalDirection];

        // Go over all slots and try to find a target slot
        for (let slotIndex = 0; slotIndex < this.slots.length; ++slotIndex) {
            const slot = this.slots[slotIndex];

            // Make sure the acceptor slot is on the right position
            if (!slot.pos.equals(targetLocalTile)) {
                continue;
            }

            // Check if the acceptor slot accepts items from our direction
            for (let i = 0; i < slot.directions.length; ++i) {
                // const localDirection = targetStaticComp.localDirectionToWorld(slot.directions[l]);
                if (desiredDirection === slot.directions[i]) {
                    return {
                        slot,
                        index: slotIndex,
                        acceptedDirection: desiredDirection,
                    };
                }
            }
        }

        return null;
    }
}


/***/ }),

/***/ "./src/js/game/components/item_ejector.js":
/*!************************************************!*\
  !*** ./src/js/game/components/item_ejector.js ***!
  \************************************************/
/*! exports provided: ItemEjectorComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemEjectorComponent", function() { return ItemEjectorComponent; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _belt_path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../belt_path */ "./src/js/game/belt_path.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");








/**
 * @typedef {{
 *    pos: Vector,
 *    direction: enumDirection,
 *    item: BaseItem,
 *    progress: number?,
 *    cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
 *    cachedBeltPath?: BeltPath,
 *    cachedTargetEntity?: Entity
 * }} ItemEjectorSlot
 */

class ItemEjectorComponent extends _component__WEBPACK_IMPORTED_MODULE_4__["Component"] {
    static getId() {
        return "ItemEjector";
    }

    static getSchema() {
        // The cachedDestSlot, cachedTargetEntity fields are not serialized.
        return {
            slots: _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].fixedSizeArray(
                _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].structured({
                    item: _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].nullable(_item_resolver__WEBPACK_IMPORTED_MODULE_6__["typeItemSingleton"]),
                    progress: _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].float,
                })
            ),
        };
    }

    /**
     *
     * @param {object} param0
     * @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on
     * @param {boolean=} param0.renderFloatingItems Whether to render items even if they are not connected
     */
    constructor({ slots = [], renderFloatingItems = true }) {
        super();

        this.setSlots(slots);
        this.renderFloatingItems = renderFloatingItems;
    }

    /**
     * @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on
     */
    setSlots(slots) {
        /** @type {Array<ItemEjectorSlot>} */
        this.slots = [];
        for (let i = 0; i < slots.length; ++i) {
            const slot = slots[i];
            this.slots.push({
                pos: slot.pos,
                direction: slot.direction,
                item: null,
                progress: 0,
                cachedDestSlot: null,
                cachedTargetEntity: null,
            });
        }
    }

    /**
     * Returns where this slot ejects to
     * @param {ItemEjectorSlot} slot
     * @returns {Vector}
     */
    getSlotTargetLocalTile(slot) {
        const directionVector = _core_vector__WEBPACK_IMPORTED_MODULE_0__["enumDirectionToVector"][slot.direction];
        return slot.pos.add(directionVector);
    }

    /**
     * Returns whether any slot ejects to the given local tile
     * @param {Vector} tile
     */
    anySlotEjectsToLocalTile(tile) {
        for (let i = 0; i < this.slots.length; ++i) {
            if (this.getSlotTargetLocalTile(this.slots[i]).equals(tile)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns if we can eject on a given slot
     * @param {number} slotIndex
     * @returns {boolean}
     */
    canEjectOnSlot(slotIndex) {
        window.assert(slotIndex >= 0 && slotIndex < this.slots.length, "Invalid ejector slot: " + slotIndex);
        return !this.slots[slotIndex].item;
    }

    /**
     * Returns the first free slot on this ejector or null if there is none
     * @returns {number?}
     */
    getFirstFreeSlot() {
        for (let i = 0; i < this.slots.length; ++i) {
            if (this.canEjectOnSlot(i)) {
                return i;
            }
        }
        return null;
    }

    /**
     * Tries to eject a given item
     * @param {number} slotIndex
     * @param {BaseItem} item
     * @returns {boolean}
     */
    tryEject(slotIndex, item) {
        if (!this.canEjectOnSlot(slotIndex)) {
            return false;
        }
        this.slots[slotIndex].item = item;
        this.slots[slotIndex].progress = 0;
        return true;
    }

    /**
     * Clears the given slot and returns the item it had
     * @param {number} slotIndex
     * @returns {BaseItem|null}
     */
    takeSlotItem(slotIndex) {
        const slot = this.slots[slotIndex];
        const item = slot.item;
        slot.item = null;
        slot.progress = 0.0;
        return item;
    }
}


/***/ }),

/***/ "./src/js/game/components/item_processor.js":
/*!**************************************************!*\
  !*** ./src/js/game/components/item_processor.js ***!
  \**************************************************/
/*! exports provided: enumItemProcessorTypes, enumItemProcessorRequirements, ItemProcessorComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumItemProcessorTypes", function() { return enumItemProcessorTypes; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumItemProcessorRequirements", function() { return enumItemProcessorRequirements; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemProcessorComponent", function() { return ItemProcessorComponent; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");




/** @enum {string} */
const enumItemProcessorTypes = {
    balancer: "balancer",
    cutter: "cutter",
    cutterQuad: "cutterQuad",
    rotater: "rotater",
    rotaterCCW: "rotaterCCW",
    rotater180: "rotater180",
    stacker: "stacker",
    trash: "trash",
    mixer: "mixer",
    painter: "painter",
    painterDouble: "painterDouble",
    painterQuad: "painterQuad",
    hub: "hub",
    filter: "filter",
    reader: "reader",
};

/** @enum {string} */
const enumItemProcessorRequirements = {
    painterQuad: "painterQuad",
};

/** @typedef {{
 *  item: BaseItem,
 *  requiredSlot?: number,
 *  preferredSlot?: number
 * }} EjectorItemToEject */

/** @typedef {{
 *  remainingTime: number,
 *  items: Array<EjectorItemToEject>,
 * }} EjectorCharge */

class ItemProcessorComponent extends _component__WEBPACK_IMPORTED_MODULE_2__["Component"] {
    static getId() {
        return "ItemProcessor";
    }

    static getSchema() {
        return {
            nextOutputSlot: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].uint,
        };
    }

    /**
     *
     * @param {object} param0
     * @param {enumItemProcessorTypes=} param0.processorType Which type of processor this is
     * @param {enumItemProcessorRequirements=} param0.processingRequirement Applied processing requirement
     * @param {number=} param0.inputsPerCharge How many items this machine needs until it can start working
     *
     */
    constructor({
        processorType = enumItemProcessorTypes.balancer,
        processingRequirement = null,
        inputsPerCharge = 1,
    }) {
        super();

        // Which slot to emit next, this is only a preference and if it can't emit
        // it will take the other one. Some machines ignore this (e.g. the balancer) to make
        // sure the outputs always match
        this.nextOutputSlot = 0;

        // Type of the processor
        this.type = processorType;

        // Type of processing requirement
        this.processingRequirement = processingRequirement;

        // How many inputs we need for one charge
        this.inputsPerCharge = inputsPerCharge;

        /**
         * Our current inputs
         * @type {Array<{ item: BaseItem, sourceSlot: number }>}
         */
        this.inputSlots = [];

        /**
         * What we are currently processing, empty if we don't produce anything rn
         * requiredSlot: Item *must* be ejected on this slot
         * preferredSlot: Item *can* be ejected on this slot, but others are fine too if the one is not usable
         * @type {Array<EjectorCharge>}
         */
        this.ongoingCharges = [];

        /**
         * How much processing time we have left from the last tick
         * @type {number}
         */
        this.bonusTime = 0;
    }

    /**
     * Tries to take the item
     * @param {BaseItem} item
     * @param {number} sourceSlot
     */
    tryTakeItem(item, sourceSlot) {
        if (this.type === enumItemProcessorTypes.hub || this.type === enumItemProcessorTypes.trash) {
            // Hub has special logic .. not really nice but efficient.
            this.inputSlots.push({ item, sourceSlot });
            return true;
        }

        // Check that we only take one item per slot
        for (let i = 0; i < this.inputSlots.length; ++i) {
            const slot = this.inputSlots[i];
            if (slot.sourceSlot === sourceSlot) {
                return false;
            }
        }

        this.inputSlots.push({ item, sourceSlot });
        return true;
    }
}


/***/ }),

/***/ "./src/js/game/components/item_producer.js":
/*!*************************************************!*\
  !*** ./src/js/game/components/item_producer.js ***!
  \*************************************************/
/*! exports provided: ItemProducerComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemProducerComponent", function() { return ItemProducerComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");


class ItemProducerComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "ItemProducer";
    }
}


/***/ }),

/***/ "./src/js/game/components/lever.js":
/*!*****************************************!*\
  !*** ./src/js/game/components/lever.js ***!
  \*****************************************/
/*! exports provided: LeverComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LeverComponent", function() { return LeverComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");



class LeverComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "Lever";
    }

    static getSchema() {
        return {
            toggled: _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].bool,
        };
    }

    /**
     * Copy the current state to another component
     * @param {LeverComponent} otherComponent
     */
    copyAdditionalStateTo(otherComponent) {
        otherComponent.toggled = this.toggled;
    }

    /**
     * @param {object} param0
     * @param {boolean=} param0.toggled
     */
    constructor({ toggled = false }) {
        super();
        this.toggled = toggled;
    }
}


/***/ }),

/***/ "./src/js/game/components/logic_gate.js":
/*!**********************************************!*\
  !*** ./src/js/game/components/logic_gate.js ***!
  \**********************************************/
/*! exports provided: enumLogicGateType, LogicGateComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumLogicGateType", function() { return enumLogicGateType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LogicGateComponent", function() { return LogicGateComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");


/** @enum {string} */
const enumLogicGateType = {
    and: "and",
    not: "not",
    xor: "xor",
    or: "or",
    transistor: "transistor",

    analyzer: "analyzer",
    rotater: "rotater",
    unstacker: "unstacker",
    cutter: "cutter",
    compare: "compare",
    stacker: "stacker",
    painter: "painter",
};

class LogicGateComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "LogicGate";
    }

    /**
     *
     * @param {object} param0
     * @param {enumLogicGateType=} param0.type
     */
    constructor({ type = enumLogicGateType.and }) {
        super();
        this.type = type;
    }
}


/***/ }),

/***/ "./src/js/game/components/miner.js":
/*!*****************************************!*\
  !*** ./src/js/game/components/miner.js ***!
  \*****************************************/
/*! exports provided: MinerComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MinerComponent", function() { return MinerComponent; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");






const chainBufferSize = 6;

class MinerComponent extends _component__WEBPACK_IMPORTED_MODULE_2__["Component"] {
    static getId() {
        return "Miner";
    }

    static getSchema() {
        // cachedMinedItem is not serialized.
        return {
            lastMiningTime: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].ufloat,
            itemChainBuffer: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].array(_item_resolver__WEBPACK_IMPORTED_MODULE_4__["typeItemSingleton"]),
        };
    }

    constructor({ chainable = false }) {
        super();
        this.lastMiningTime = 0;
        this.chainable = chainable;

        /**
         * Stores items from other miners which were chained to this
         * miner.
         * @type {Array<BaseItem>}
         */
        this.itemChainBuffer = [];

        /**
         * @type {BaseItem}
         */
        this.cachedMinedItem = null;

        /**
         * Which miner this miner ejects to, in case its a chainable one.
         * If the value is false, it means there is no entity, and we don't have to re-check
         * @type {Entity|null|false}
         */
        this.cachedChainedMiner = null;
    }

    /**
     *
     * @param {BaseItem} item
     */
    tryAcceptChainedItem(item) {
        if (this.itemChainBuffer.length > chainBufferSize) {
            // Well, this one is full
            return false;
        }

        this.itemChainBuffer.push(item);
        return true;
    }
}


/***/ }),

/***/ "./src/js/game/components/static_map_entity.js":
/*!*****************************************************!*\
  !*** ./src/js/game/components/static_map_entity.js ***!
  \*****************************************************/
/*! exports provided: StaticMapEntityComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StaticMapEntityComponent", function() { return StaticMapEntityComponent; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");









class StaticMapEntityComponent extends _component__WEBPACK_IMPORTED_MODULE_7__["Component"] {
    static getId() {
        return "StaticMapEntity";
    }

    static getSchema() {
        return {
            origin: _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__["types"].tileVector,
            rotation: _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__["types"].float,
            originalRotation: _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__["types"].float,

            // See building_codes.js
            code: _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__["types"].uint,
        };
    }

    /**
     * Returns the effective tile size
     * @returns {Vector}
     */
    getTileSize() {
        return Object(_building_codes__WEBPACK_IMPORTED_MODULE_6__["getBuildingDataFromCode"])(this.code).tileSize;
    }

    /**
     * Returns the sprite
     * @returns {AtlasSprite}
     */
    getSprite() {
        return Object(_building_codes__WEBPACK_IMPORTED_MODULE_6__["getBuildingDataFromCode"])(this.code).sprite;
    }

    /**
     * Returns the blueprint sprite
     * @returns {AtlasSprite}
     */
    getBlueprintSprite() {
        return Object(_building_codes__WEBPACK_IMPORTED_MODULE_6__["getBuildingDataFromCode"])(this.code).blueprintSprite;
    }

    /**
     * Returns the silhouette color
     * @returns {string}
     */
    getSilhouetteColor() {
        return Object(_building_codes__WEBPACK_IMPORTED_MODULE_6__["getBuildingDataFromCode"])(this.code).silhouetteColor;
    }

    /**
     * Returns the meta building
     * @returns {import("../meta_building").MetaBuilding}
     */
    getMetaBuilding() {
        return Object(_building_codes__WEBPACK_IMPORTED_MODULE_6__["getBuildingDataFromCode"])(this.code).metaInstance;
    }

    /**
     * Returns the buildings variant
     * @returns {string}
     */
    getVariant() {
        return Object(_building_codes__WEBPACK_IMPORTED_MODULE_6__["getBuildingDataFromCode"])(this.code).variant;
    }

    /**
     * Copy the current state to another component
     * @param {Component} otherComponent
     */
    copyAdditionalStateTo(otherComponent) {
        return new StaticMapEntityComponent({
            origin: this.origin.copy(),
            rotation: this.rotation,
            originalRotation: this.originalRotation,
            code: this.code,
        });
    }

    /**
     *
     * @param {object} param0
     * @param {Vector=} param0.origin Origin (Top Left corner) of the entity
     * @param {Vector=} param0.tileSize Size of the entity in tiles
     * @param {number=} param0.rotation Rotation in degrees. Must be multiple of 90
     * @param {number=} param0.originalRotation Original Rotation in degrees. Must be multiple of 90
     * @param {number=} param0.code Building code
     */
    constructor({
        origin = new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](),
        tileSize = new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](1, 1),
        rotation = 0,
        originalRotation = 0,
        code = 0,
    }) {
        super();
        window.assert(
            rotation % 90 === 0,
            "Rotation of static map entity must be multiple of 90 (was " + rotation + ")"
        );

        this.origin = origin;
        this.rotation = rotation;
        this.code = code;
        this.originalRotation = originalRotation;
    }

    /**
     * Returns the effective rectangle of this entity in tile space
     * @returns {Rectangle}
     */
    getTileSpaceBounds() {
        const size = this.getTileSize();
        switch (this.rotation) {
            case 0:
                return new _core_rectangle__WEBPACK_IMPORTED_MODULE_2__["Rectangle"](this.origin.x, this.origin.y, size.x, size.y);
            case 90:
                return new _core_rectangle__WEBPACK_IMPORTED_MODULE_2__["Rectangle"](this.origin.x - size.y + 1, this.origin.y, size.y, size.x);
            case 180:
                return new _core_rectangle__WEBPACK_IMPORTED_MODULE_2__["Rectangle"](this.origin.x - size.x + 1, this.origin.y - size.y + 1, size.x, size.y);
            case 270:
                return new _core_rectangle__WEBPACK_IMPORTED_MODULE_2__["Rectangle"](this.origin.x, this.origin.y - size.x + 1, size.y, size.x);
            default:
                window.assert(false, "Invalid rotation");
        }
    }

    /**
     * Transforms the given vector/rotation from local space to world space
     * @param {Vector} vector
     * @returns {Vector}
     */
    applyRotationToVector(vector) {
        return vector.rotateFastMultipleOf90(this.rotation);
    }

    /**
     * Transforms the given vector/rotation from world space to local space
     * @param {Vector} vector
     * @returns {Vector}
     */
    unapplyRotationToVector(vector) {
        return vector.rotateFastMultipleOf90(360 - this.rotation);
    }

    /**
     * Transforms the given direction from local space
     * @param {enumDirection} direction
     * @returns {enumDirection}
     */
    localDirectionToWorld(direction) {
        return _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"].transformDirectionFromMultipleOf90(direction, this.rotation);
    }

    /**
     * Transforms the given direction from world to local space
     * @param {enumDirection} direction
     * @returns {enumDirection}
     */
    worldDirectionToLocal(direction) {
        return _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"].transformDirectionFromMultipleOf90(direction, 360 - this.rotation);
    }

    /**
     * Transforms from local tile space to global tile space
     * @param {Vector} localTile
     * @returns {Vector}
     */
    localTileToWorld(localTile) {
        const result = localTile.rotateFastMultipleOf90(this.rotation);
        result.x += this.origin.x;
        result.y += this.origin.y;
        return result;
    }

    /**
     * Transforms from world space to local space
     * @param {Vector} worldTile
     */
    worldToLocalTile(worldTile) {
        const localUnrotated = worldTile.sub(this.origin);
        return this.unapplyRotationToVector(localUnrotated);
    }

    /**
     * Returns whether the entity should be drawn for the given parameters
     * @param {DrawParameters} parameters
     */
    shouldBeDrawn(parameters) {
        let x = 0;
        let y = 0;
        let w = 0;
        let h = 0;
        const size = this.getTileSize();

        switch (this.rotation) {
            case 0: {
                x = this.origin.x;
                y = this.origin.y;
                w = size.x;
                h = size.y;
                break;
            }
            case 90: {
                x = this.origin.x - size.y + 1;
                y = this.origin.y;
                w = size.y;
                h = size.x;
                break;
            }
            case 180: {
                x = this.origin.x - size.x + 1;
                y = this.origin.y - size.y + 1;
                w = size.x;
                h = size.y;
                break;
            }
            case 270: {
                x = this.origin.x;
                y = this.origin.y - size.x + 1;
                w = size.y;
                h = size.x;
                break;
            }
            default:
                window.assert(false, "Invalid rotation");
        }

        return parameters.visibleRect.containsRect4Params(
            x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            w * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            h * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize
        );
    }

    /**
     * Draws a sprite over the whole space of the entity
     * @param {DrawParameters} parameters
     * @param {AtlasSprite} sprite
     * @param {number=} extrudePixels How many pixels to extrude the sprite
     * @param {Vector=} overridePosition Whether to drwa the entity at a different location
     */
    drawSpriteOnBoundsClipped(parameters, sprite, extrudePixels = 0, overridePosition = null) {
        if (!this.shouldBeDrawn(parameters) && !overridePosition) {
            return;
        }
        const size = this.getTileSize();
        let worldX = this.origin.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
        let worldY = this.origin.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;

        if (overridePosition) {
            worldX = overridePosition.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
            worldY = overridePosition.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
        }

        if (this.rotation === 0) {
            // Early out, is faster
            sprite.drawCached(
                parameters,
                worldX - extrudePixels * size.x,
                worldY - extrudePixels * size.y,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * size.x + 2 * extrudePixels * size.x,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * size.y + 2 * extrudePixels * size.y
            );
        } else {
            const rotationCenterX = worldX + _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;
            const rotationCenterY = worldY + _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;

            parameters.context.translate(rotationCenterX, rotationCenterY);
            parameters.context.rotate(Math.radians(this.rotation));
            sprite.drawCached(
                parameters,
                -_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize - extrudePixels * size.x,
                -_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize - extrudePixels * size.y,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * size.x + 2 * extrudePixels * size.x,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * size.y + 2 * extrudePixels * size.y,
                false // no clipping possible here
            );
            parameters.context.rotate(-Math.radians(this.rotation));
            parameters.context.translate(-rotationCenterX, -rotationCenterY);
        }
    }
}


/***/ }),

/***/ "./src/js/game/components/storage.js":
/*!*******************************************!*\
  !*** ./src/js/game/components/storage.js ***!
  \*******************************************/
/*! exports provided: StorageComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StorageComponent", function() { return StorageComponent; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../items/shape_item */ "./src/js/game/items/shape_item.js");







class StorageComponent extends _component__WEBPACK_IMPORTED_MODULE_2__["Component"] {
    static getId() {
        return "Storage";
    }

    static getSchema() {
        return {
            storedCount: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].uint,
            storedItem: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].nullable(_item_resolver__WEBPACK_IMPORTED_MODULE_3__["typeItemSingleton"]),
        };
    }

    /**
     * @param {object} param0
     * @param {number=} param0.maximumStorage How much this storage can hold
     */
    constructor({ maximumStorage = 1e20 }) {
        super();
        this.maximumStorage = maximumStorage;

        /**
         * Currently stored item
         * @type {BaseItem}
         */
        this.storedItem = null;

        /**
         * How many of this item we have stored
         */
        this.storedCount = 0;

        /**
         * We compute an opacity to make sure it doesn't flicker
         */
        this.overlayOpacity = 0;
    }

    /**
     * Returns whether this storage can accept the item
     * @param {BaseItem} item
     */
    canAcceptItem(item) {
        if (this.storedCount >= this.maximumStorage) {
            return false;
        }
        if (!this.storedItem || this.storedCount === 0) {
            return true;
        }

        const itemType = item.getItemType();

        // Check type matches
        if (itemType !== this.storedItem.getItemType()) {
            return false;
        }

        if (itemType === "color") {
            return /** @type {ColorItem} */ (this.storedItem).color === /** @type {ColorItem} */ (item).color;
        }

        if (itemType === "shape") {
            return (
                /** @type {ShapeItem} */ (this.storedItem).definition.getHash() ===
                /** @type {ShapeItem} */ (item).definition.getHash()
            );
        }
        return false;
    }

    /**
     * Returns whether the storage is full
     * @returns {boolean}
     */
    getIsFull() {
        return this.storedCount >= this.maximumStorage;
    }

    /**
     * @param {BaseItem} item
     */
    takeItem(item) {
        this.storedItem = item;
        this.storedCount++;
    }
}


/***/ }),

/***/ "./src/js/game/components/underground_belt.js":
/*!****************************************************!*\
  !*** ./src/js/game/components/underground_belt.js ***!
  \****************************************************/
/*! exports provided: enumUndergroundBeltMode, UndergroundBeltComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumUndergroundBeltMode", function() { return enumUndergroundBeltMode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UndergroundBeltComponent", function() { return UndergroundBeltComponent; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");







/** @enum {string} */
const enumUndergroundBeltMode = {
    sender: "sender",
    receiver: "receiver",
};

/**
 * @typedef {{
 *   entity: Entity,
 *   distance: number
 * }} LinkedUndergroundBelt
 */

class UndergroundBeltComponent extends _component__WEBPACK_IMPORTED_MODULE_3__["Component"] {
    static getId() {
        return "UndergroundBelt";
    }

    static getSchema() {
        return {
            pendingItems: _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].array(_savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].pair(_item_resolver__WEBPACK_IMPORTED_MODULE_5__["typeItemSingleton"], _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].float)),
        };
    }

    /**
     *
     * @param {object} param0
     * @param {enumUndergroundBeltMode=} param0.mode As which type of belt the entity acts
     * @param {number=} param0.tier
     */
    constructor({ mode = enumUndergroundBeltMode.sender, tier = 0 }) {
        super();

        this.mode = mode;
        this.tier = tier;

        /** @type {Array<{ item: BaseItem, progress: number }>} */
        this.consumptionAnimations = [];

        /**
         * Used on both receiver and sender.
         * Reciever: Used to store the next item to transfer, and to block input while doing this
         * Sender: Used to store which items are currently "travelling"
         * @type {Array<[BaseItem, number]>} Format is [Item, ingame time to eject the item]
         */
        this.pendingItems = [];

        /**
         * The linked entity, used to speed up performance. This contains either
         * the entrance or exit depending on the tunnel type
         * @type {LinkedUndergroundBelt}
         */
        this.cachedLinkedEntity = null;
    }

    /**
     * Tries to accept an item from an external source like a regular belt or building
     * @param {BaseItem} item
     * @param {number} beltSpeed How fast this item travels
     */
    tryAcceptExternalItem(item, beltSpeed) {
        if (this.mode !== enumUndergroundBeltMode.sender) {
            // Only senders accept external items
            return false;
        }

        if (this.pendingItems.length > 0) {
            // We currently have a pending item
            return false;
        }

        this.pendingItems.push([item, 0]);
        return true;
    }

    /**
     * Tries to accept a tunneled item
     * @param {BaseItem} item
     * @param {number} travelDistance How many tiles this item has to travel
     * @param {number} beltSpeed How fast this item travels
     * @param {number} now Current ingame time
     */
    tryAcceptTunneledItem(item, travelDistance, beltSpeed, now) {
        if (this.mode !== enumUndergroundBeltMode.receiver) {
            // Only receivers can accept tunneled items
            return false;
        }

        // Notice: We assume that for all items the travel distance is the same
        const maxItemsInTunnel = (2 + travelDistance) / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;
        if (this.pendingItems.length >= maxItemsInTunnel) {
            // Simulate a real belt which gets full at some point
            return false;
        }

        // NOTICE:
        // This corresponds to the item ejector - it needs 0.5 additional tiles to eject the item.
        // So instead of adding 1 we add 0.5 only.
        // Additionally it takes 1 tile for the acceptor which we just add on top.
        const travelDuration = (travelDistance + 1.5) / beltSpeed / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;

        this.pendingItems.push([item, now + travelDuration]);
        return true;
    }
}


/***/ }),

/***/ "./src/js/game/components/wire.js":
/*!****************************************!*\
  !*** ./src/js/game/components/wire.js ***!
  \****************************************/
/*! exports provided: enumWireType, enumWireVariant, WireComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumWireType", function() { return enumWireType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumWireVariant", function() { return enumWireVariant; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WireComponent", function() { return WireComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");


/** @enum {string} */
const enumWireType = {
    forward: "forward",
    turn: "turn",
    split: "split",
    cross: "cross",
};

/** @enum {string} */
const enumWireVariant = {
    first: "first",
    second: "second",
};

class WireComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "Wire";
    }

    /**
     * @param {object} param0
     * @param {enumWireType=} param0.type
     * @param {enumWireVariant=} param0.variant
     */
    constructor({ type = enumWireType.forward, variant = enumWireVariant.first }) {
        super();
        this.type = type;

        /**
         * The variant of the wire, different variants do not connect
         * @type {enumWireVariant}
         */
        this.variant = variant;

        /**
         * @type {import("../systems/wire").WireNetwork}
         */
        this.linkedNetwork = null;
    }
}


/***/ }),

/***/ "./src/js/game/components/wire_tunnel.js":
/*!***********************************************!*\
  !*** ./src/js/game/components/wire_tunnel.js ***!
  \***********************************************/
/*! exports provided: WireTunnelComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WireTunnelComponent", function() { return WireTunnelComponent; });
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");


class WireTunnelComponent extends _component__WEBPACK_IMPORTED_MODULE_0__["Component"] {
    static getId() {
        return "WireTunnel";
    }

    constructor() {
        super();

        /**
         * Linked network, only if its not multiple directions
         * @type {Array<import("../systems/wire").WireNetwork>}
         */
        this.linkedNetworks = [];
    }
}


/***/ }),

/***/ "./src/js/game/components/wired_pins.js":
/*!**********************************************!*\
  !*** ./src/js/game/components/wired_pins.js ***!
  \**********************************************/
/*! exports provided: enumPinSlotType, WiredPinsComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumPinSlotType", function() { return enumPinSlotType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WiredPinsComponent", function() { return WiredPinsComponent; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../component */ "./src/js/game/component.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _item_resolver__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../item_resolver */ "./src/js/game/item_resolver.js");






/** @enum {string} */
const enumPinSlotType = {
    logicalEjector: "logicalEjector",
    logicalAcceptor: "logicalAcceptor",
};

/** @typedef {{
 *   pos: Vector,
 *   type: enumPinSlotType,
 *   direction: enumDirection
 * }} WirePinSlotDefinition */

/** @typedef {{
 *   pos: Vector,
 *   type: enumPinSlotType,
 *   direction: enumDirection,
 *   value: BaseItem,
 *   linkedNetwork: import("../systems/wire").WireNetwork
 * }} WirePinSlot */

class WiredPinsComponent extends _component__WEBPACK_IMPORTED_MODULE_2__["Component"] {
    static getId() {
        return "WiredPins";
    }

    static getSchema() {
        return {
            slots: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].fixedSizeArray(
                _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].structured({
                    value: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].nullable(_item_resolver__WEBPACK_IMPORTED_MODULE_4__["typeItemSingleton"]),
                })
            ),
        };
    }

    /**
     *
     * @param {object} param0
     * @param {Array<WirePinSlotDefinition>} param0.slots
     */
    constructor({ slots = [] }) {
        super();
        this.setSlots(slots);
    }

    /**
     * Sets the slots of this building
     * @param {Array<WirePinSlotDefinition>} slots
     */
    setSlots(slots) {
        /** @type {Array<WirePinSlot>} */
        this.slots = [];

        for (let i = 0; i < slots.length; ++i) {
            const slotData = slots[i];
            this.slots.push({
                pos: slotData.pos,
                type: slotData.type,
                direction: slotData.direction,
                value: null,
                linkedNetwork: null,
            });
        }
    }
}


/***/ }),

/***/ "./src/js/game/core.js":
/*!*****************************!*\
  !*** ./src/js/game/core.js ***!
  \*****************************/
/*! exports provided: GameCore */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameCore", function() { return GameCore; });
/* harmony import */ var _core_buffer_maintainer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/buffer_maintainer */ "./src/js/core/buffer_maintainer.js");
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_dpi_manager__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/dpi_manager */ "./src/js/core/dpi_manager.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_savegame__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../savegame/savegame */ "./src/js/savegame/savegame.js");
/* harmony import */ var _savegame_savegame_serializer__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../savegame/savegame_serializer */ "./src/js/savegame/savegame_serializer.js");
/* harmony import */ var _automatic_save__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./automatic_save */ "./src/js/game/automatic_save.js");
/* harmony import */ var _buildings_hub__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./buildings/hub */ "./src/js/game/buildings/hub.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./camera */ "./src/js/game/camera.js");
/* harmony import */ var _dynamic_tickrate__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./dynamic_tickrate */ "./src/js/game/dynamic_tickrate.js");
/* harmony import */ var _entity_manager__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./entity_manager */ "./src/js/game/entity_manager.js");
/* harmony import */ var _game_system_manager__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./game_system_manager */ "./src/js/game/game_system_manager.js");
/* harmony import */ var _hub_goals__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./hub_goals */ "./src/js/game/hub_goals.js");
/* harmony import */ var _hud_hud__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./hud/hud */ "./src/js/game/hud/hud.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _logic__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./logic */ "./src/js/game/logic.js");
/* harmony import */ var _map_view__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./map_view */ "./src/js/game/map_view.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _modes_regular__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./modes/regular */ "./src/js/game/modes/regular.js");
/* harmony import */ var _production_analytics__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./production_analytics */ "./src/js/game/production_analytics.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _shape_definition_manager__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./shape_definition_manager */ "./src/js/game/shape_definition_manager.js");
/* harmony import */ var _sound_proxy__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./sound_proxy */ "./src/js/game/sound_proxy.js");
/* harmony import */ var _time_game_time__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./time/game_time */ "./src/js/game/time/game_time.js");

































const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_6__["createLogger"])("ingame/core");

// Store the canvas so we can reuse it later
/** @type {HTMLCanvasElement} */
let lastCanvas = null;
/** @type {CanvasRenderingContext2D} */
let lastContext = null;

/**
 * The core manages the root and represents the whole game. It wraps the root, since
 * the root class is just a data holder.
 */
class GameCore {
    /** @param {Application} app */
    constructor(app) {
        this.app = app;

        /** @type {GameRoot} */
        this.root = null;

        /**
         * Set to true at the beginning of a logic update and cleared when its finished.
         * This is to prevent doing a recursive logic update which can lead to unexpected
         * behaviour.
         */
        this.duringLogicUpdate = false;

        // Cached
        this.boundInternalTick = this.updateLogic.bind(this);

        /**
         * Opacity of the overview alpha
         * @TODO Doesn't belong here
         */
        this.overlayAlpha = 0;
    }

    /**
     * Initializes the root object which stores all game related data. The state
     * is required as a back reference (used sometimes)
     * @param {import("../states/ingame").InGameState} parentState
     * @param {Savegame} savegame
     */
    initializeRoot(parentState, savegame) {
        // Construct the root element, this is the data representation of the game
        this.root = new _root__WEBPACK_IMPORTED_MODULE_27__["GameRoot"](this.app);
        this.root.gameState = parentState;
        this.root.keyMapper = parentState.keyActionMapper;
        this.root.savegame = savegame;
        this.root.gameWidth = this.app.screenWidth;
        this.root.gameHeight = this.app.screenHeight;

        // Initialize canvas element & context
        this.internalInitCanvas();

        // Members
        const root = this.root;

        // This isn't nice, but we need it right here
        root.keyMapper = new _key_action_mapper__WEBPACK_IMPORTED_MODULE_21__["KeyActionMapper"](root, this.root.gameState.inputReciever);

        // Needs to come first
        root.dynamicTickrate = new _dynamic_tickrate__WEBPACK_IMPORTED_MODULE_16__["DynamicTickrate"](root);

        // Init game mode
        root.gameMode = new _modes_regular__WEBPACK_IMPORTED_MODULE_25__["RegularGameMode"](root);

        // Init classes
        root.camera = new _camera__WEBPACK_IMPORTED_MODULE_15__["Camera"](root);
        root.map = new _map_view__WEBPACK_IMPORTED_MODULE_23__["MapView"](root);
        root.logic = new _logic__WEBPACK_IMPORTED_MODULE_22__["GameLogic"](root);
        root.hud = new _hud_hud__WEBPACK_IMPORTED_MODULE_20__["GameHUD"](root);
        root.time = new _time_game_time__WEBPACK_IMPORTED_MODULE_30__["GameTime"](root);
        root.automaticSave = new _automatic_save__WEBPACK_IMPORTED_MODULE_13__["AutomaticSave"](root);
        root.soundProxy = new _sound_proxy__WEBPACK_IMPORTED_MODULE_29__["SoundProxy"](root);

        // Init managers
        root.entityMgr = new _entity_manager__WEBPACK_IMPORTED_MODULE_17__["EntityManager"](root);
        root.systemMgr = new _game_system_manager__WEBPACK_IMPORTED_MODULE_18__["GameSystemManager"](root);
        root.shapeDefinitionMgr = new _shape_definition_manager__WEBPACK_IMPORTED_MODULE_28__["ShapeDefinitionManager"](root);
        root.hubGoals = new _hub_goals__WEBPACK_IMPORTED_MODULE_19__["HubGoals"](root);
        root.productionAnalytics = new _production_analytics__WEBPACK_IMPORTED_MODULE_26__["ProductionAnalytics"](root);
        root.buffers = new _core_buffer_maintainer__WEBPACK_IMPORTED_MODULE_0__["BufferMaintainer"](root);

        // Initialize the hud once everything is loaded
        this.root.hud.initialize();

        // Initial resize event, it might be possible that the screen
        // resized later during init tho, which is why will emit it later
        // again anyways
        this.resize(this.app.screenWidth, this.app.screenHeight);

        if (false) {}

        // @todo Find better place
        if (false) {}
    }

    /**
     * Initializes a new game, this means creating a new map and centering on the
     * playerbase
     * */
    initNewGame() {
        logger.log("Initializing new game");
        this.root.gameIsFresh = true;
        this.root.map.seed = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["randomInt"])(0, 100000);

        // Place the hub
        const hub = _core_global_registries__WEBPACK_IMPORTED_MODULE_5__["gMetaBuildingRegistry"].findByClass(_buildings_hub__WEBPACK_IMPORTED_MODULE_14__["MetaHubBuilding"]).createEntity({
            root: this.root,
            origin: new _core_vector__WEBPACK_IMPORTED_MODULE_10__["Vector"](-2, -2),
            rotation: 0,
            originalRotation: 0,
            rotationVariant: 0,
            variant: _meta_building__WEBPACK_IMPORTED_MODULE_24__["defaultBuildingVariant"],
        });
        this.root.map.placeStaticEntity(hub);
        this.root.entityMgr.registerEntity(hub);
    }

    /**
     * Inits an existing game by loading the raw savegame data and deserializing it.
     * Also runs basic validity checks.
     */
    initExistingGame() {
        logger.log("Initializing existing game");
        const serializer = new _savegame_savegame_serializer__WEBPACK_IMPORTED_MODULE_12__["SavegameSerializer"]();

        try {
            const status = serializer.deserialize(this.root.savegame.getCurrentDump(), this.root);
            if (!status.isGood()) {
                logger.error("savegame-deserialize-failed:" + status.reason);
                return false;
            }
        } catch (ex) {
            logger.error("Exception during deserialization:", ex);
            return false;
        }
        this.root.gameIsFresh = false;
        return true;
    }

    /**
     * Initializes the render canvas
     */
    internalInitCanvas() {
        let canvas, context;
        if (!lastCanvas) {
            logger.log("Creating new canvas");
            canvas = document.createElement("canvas");
            canvas.id = "ingame_Canvas";
            canvas.setAttribute("opaque", "true");
            canvas.setAttribute("webkitOpaque", "true");
            canvas.setAttribute("mozOpaque", "true");
            this.root.gameState.getDivElement().appendChild(canvas);
            context = canvas.getContext("2d", { alpha: false });

            lastCanvas = canvas;
            lastContext = context;
        } else {
            logger.log("Reusing canvas");
            if (lastCanvas.parentElement) {
                lastCanvas.parentElement.removeChild(lastCanvas);
            }
            this.root.gameState.getDivElement().appendChild(lastCanvas);

            canvas = lastCanvas;
            context = lastContext;

            lastContext.clearRect(0, 0, lastCanvas.width, lastCanvas.height);
        }

        canvas.classList.toggle("smoothed", _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].smoothing.smoothMainCanvas);

        // Oof, use :not() instead
        canvas.classList.toggle("unsmoothed", !_core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].smoothing.smoothMainCanvas);

        if (_core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].smoothing.smoothMainCanvas) {
            Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_1__["enableImageSmoothing"])(context);
        } else {
            Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_1__["disableImageSmoothing"])(context);
        }

        this.root.canvas = canvas;
        this.root.context = context;

        Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_1__["registerCanvas"])(canvas, context);
    }

    /**
     * Destructs the root, freeing all resources
     */
    destruct() {
        if (lastCanvas && lastCanvas.parentElement) {
            lastCanvas.parentElement.removeChild(lastCanvas);
        }

        this.root.destruct();
        delete this.root;
        this.root = null;
        this.app = null;
    }

    tick(deltaMs) {
        const root = this.root;

        // Extract current real time
        root.time.updateRealtimeNow();

        // Camera is always updated, no matter what
        root.camera.update(deltaMs);

        if (true) {
            // Perform logic ticks
            this.root.time.performTicks(deltaMs, this.boundInternalTick);

            // Update analytics
            root.productionAnalytics.update();
        }

        // Update automatic save after everything finished
        root.automaticSave.update();

        return true;
    }

    shouldRender() {
        if (this.root.queue.requireRedraw) {
            return true;
        }
        if (this.root.hud.shouldPauseRendering()) {
            return false;
        }

        // Do not render
        if (!this.app.isRenderable()) {
            return false;
        }

        return true;
    }

    updateLogic() {
        const root = this.root;

        root.dynamicTickrate.beginTick();

        if (false) {}

        this.duringLogicUpdate = true;

        // Update entities, this removes destroyed entities
        root.entityMgr.update();

        // IMPORTANT: At this point, the game might be game over. Stop if this is the case
        if (!this.root) {
            logger.log("Root destructed, returning false");
            root.dynamicTickrate.endTick();

            return false;
        }

        root.systemMgr.update();
        // root.particleMgr.update();

        this.duringLogicUpdate = false;
        root.dynamicTickrate.endTick();
        return true;
    }

    resize(w, h) {
        this.root.gameWidth = w;
        this.root.gameHeight = h;
        Object(_core_dpi_manager__WEBPACK_IMPORTED_MODULE_3__["resizeHighDPICanvas"])(this.root.canvas, w, h, _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].smoothing.smoothMainCanvas);
        this.root.signals.resized.dispatch(w, h);
        this.root.queue.requireRedraw = true;
    }

    postLoadHook() {
        logger.log("Dispatching post load hook");
        this.root.signals.postLoadHook.dispatch();

        if (!this.root.gameIsFresh) {
            // Also dispatch game restored hook on restored savegames
            this.root.signals.gameRestored.dispatch();
        }

        this.root.gameInitialized = true;
    }

    draw() {
        const root = this.root;
        const systems = root.systemMgr.systems;

        this.root.dynamicTickrate.onFrameRendered();

        if (!this.shouldRender()) {
            // Always update hud tho
            root.hud.update();
            return;
        }

        this.root.signals.gameFrameStarted.dispatch();

        root.queue.requireRedraw = false;

        // Gather context and save all state
        const context = root.context;
        context.save();
        if (false) {}

        // Compute optimal zoom level and atlas scale
        const zoomLevel = root.camera.zoomLevel;
        const lowQuality = root.app.settings.getAllSettings().lowQualityTextures;
        const effectiveZoomLevel =
            (zoomLevel / _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].assetsDpi) * Object(_core_dpi_manager__WEBPACK_IMPORTED_MODULE_3__["getDeviceDPI"])() * _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].assetsSharpness;

        let desiredAtlasScale = "0.25";
        if (effectiveZoomLevel > 0.5 && !lowQuality) {
            desiredAtlasScale = _core_sprites__WEBPACK_IMPORTED_MODULE_8__["ORIGINAL_SPRITE_SCALE"];
        } else if (effectiveZoomLevel > 0.35 && !lowQuality) {
            desiredAtlasScale = "0.5";
        }

        // Construct parameters required for drawing
        const params = new _core_draw_parameters__WEBPACK_IMPORTED_MODULE_4__["DrawParameters"]({
            context: context,
            visibleRect: root.camera.getVisibleRect(),
            desiredAtlasScale,
            zoomLevel,
            root: root,
        });

        if (false) {}

        // Transform to world space

        if (false) {}

        root.camera.transform(context);

        window.assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start");

        // Update hud
        root.hud.update();

        // Main rendering order
        // -----

        const desiredOverlayAlpha = this.root.camera.getIsMapOverlayActive() ? 1 : 0;
        this.overlayAlpha = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["lerp"])(this.overlayAlpha, desiredOverlayAlpha, 0.25);

        // On low performance, skip the fade
        if (this.root.entityMgr.entities.length > 5000 || this.root.dynamicTickrate.averageFps < 50) {
            this.overlayAlpha = desiredOverlayAlpha;
        }

        if (this.overlayAlpha < 0.99) {
            // Background (grid, resources, etc)
            root.map.drawBackground(params);

            // Belt items
            systems.belt.drawBeltItems(params);

            // Miner & Static map entities etc.
            root.map.drawForeground(params);

            // HUB Overlay
            systems.hub.draw(params);

            // Green wires overlay
            root.hud.parts.wiresOverlay.draw(params);

            if (this.root.currentLayer === "wires") {
                // Static map entities
                root.map.drawWiresForegroundLayer(params);
            }
        }

        if (this.overlayAlpha > 0.01) {
            // Map overview
            context.globalAlpha = this.overlayAlpha;
            root.map.drawOverlay(params);
            context.globalAlpha = 1;
        }

        if (false) {}

        if (false) {}

        // END OF GAME CONTENT
        // -----

        // Finally, draw the hud. Nothing should come after that
        root.hud.draw(params);

        window.assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame end before restore");

        // Restore to screen space
        context.restore();

        // Restore parameters
        params.zoomLevel = 1;
        params.desiredAtlasScale = _core_sprites__WEBPACK_IMPORTED_MODULE_8__["ORIGINAL_SPRITE_SCALE"];
        params.visibleRect = new _core_rectangle__WEBPACK_IMPORTED_MODULE_7__["Rectangle"](0, 0, this.root.gameWidth, this.root.gameHeight);
        if (false) {}

        // Draw overlays, those are screen space
        root.hud.drawOverlays(params);

        window.assert(context.globalAlpha === 1.0, "context.globalAlpha not 1 on frame end");

        if (false) {}

        if (false) {}

        if (false) {}
    }
}


/***/ }),

/***/ "./src/js/game/dynamic_tickrate.js":
/*!*****************************************!*\
  !*** ./src/js/game/dynamic_tickrate.js ***!
  \*****************************************/
/*! exports provided: DynamicTickrate */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DynamicTickrate", function() { return DynamicTickrate; });
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");




const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("dynamic_tickrate");

const fpsAccumulationTime = 1000;

class DynamicTickrate {
    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;

        this.currentTickStart = null;
        this.capturedTicks = [];
        this.averageTickDuration = 0;

        this.accumulatedFps = 0;
        this.accumulatedFpsLastUpdate = 0;

        this.averageFps = 60;

        this.setTickRate(this.root.app.settings.getDesiredFps());

        if (false) {}
    }

    onFrameRendered() {
        ++this.accumulatedFps;

        const now = performance.now();
        const timeDuration = now - this.accumulatedFpsLastUpdate;
        if (timeDuration > fpsAccumulationTime) {
            const avgFps = (this.accumulatedFps / fpsAccumulationTime) * 1000;
            this.averageFps = avgFps;
            this.accumulatedFps = 0;
            this.accumulatedFpsLastUpdate = now;
        }
    }

    /**
     * Sets the tick rate to N updates per second
     * @param {number} rate
     */
    setTickRate(rate) {
        logger.log("Applying tick-rate of", rate);
        this.currentTickRate = rate;
        this.deltaMs = 1000.0 / this.currentTickRate;
        this.deltaSeconds = 1.0 / this.currentTickRate;
    }

    /**
     * Increases the tick rate marginally
     */
    increaseTickRate() {
        if (false) {}

        const desiredFps = this.root.app.settings.getDesiredFps();
        this.setTickRate(Math.round(Math.min(desiredFps, this.currentTickRate * 1.2)));
    }

    /**
     * Decreases the tick rate marginally
     */
    decreaseTickRate() {
        if (false) {}

        const desiredFps = this.root.app.settings.getDesiredFps();
        this.setTickRate(Math.round(Math.max(desiredFps / 2, this.currentTickRate * 0.8)));
    }

    /**
     * Call whenever a tick began
     */
    beginTick() {
        window.assert(this.currentTickStart === null, "BeginTick called twice");
        this.currentTickStart = performance.now();

        if (this.capturedTicks.length > this.currentTickRate * 2) {
            // Take only a portion of the ticks
            this.capturedTicks.sort();
            this.capturedTicks.splice(0, 10);
            this.capturedTicks.splice(this.capturedTicks.length - 11, 10);

            let average = 0;
            for (let i = 0; i < this.capturedTicks.length; ++i) {
                average += this.capturedTicks[i];
            }
            average /= this.capturedTicks.length;

            this.averageTickDuration = average;

            const desiredFps = this.root.app.settings.getDesiredFps();

            // Disabled for now: Dynamicall adjusting tick rate
            // if (this.averageFps > desiredFps * 0.9) {
            //     // if (average < maxTickDuration) {
            //     this.increaseTickRate();
            // } else if (this.averageFps < desiredFps * 0.7) {
            //     this.decreaseTickRate();
            // }

            this.capturedTicks = [];
        }
    }

    /**
     * Call whenever a tick ended
     */
    endTick() {
        window.assert(this.currentTickStart !== null, "EndTick called without BeginTick");
        const duration = performance.now() - this.currentTickStart;
        this.capturedTicks.push(duration);
        this.currentTickStart = null;
    }
}


/***/ }),

/***/ "./src/js/game/entity.js":
/*!*******************************!*\
  !*** ./src/js/game/entity.js ***!
  \*******************************/
/*! exports provided: Entity */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Entity", function() { return Entity; });
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _entity_components__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./entity_components */ "./src/js/game/entity_components.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../core/draw_utils */ "./src/js/core/draw_utils.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./building_codes */ "./src/js/game/building_codes.js");












class Entity extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["BasicSerializableObject"] {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        super();

        /**
         * Handle to the global game root
         */
        this.root = root;

        /**
         * The components of the entity
         */
        this.components = new _entity_components__WEBPACK_IMPORTED_MODULE_4__["EntityComponentStorage"]();

        /**
         * Whether this entity was registered on the @see EntityManager so far
         */
        this.registered = false;

        /**
         * On which layer this entity is
         * @type {Layer}
         */
        this.layer = "regular";

        /**
         * Internal entity unique id, set by the @see EntityManager
         */
        this.uid = 0;


    }

    static getId() {
        return "Entity";
    }

    /**
     * @see BasicSerializableObject.getSchema
     * @returns {import("../savegame/serialization").Schema}
     */
    static getSchema() {
        return {
            uid: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].uint,
            components: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].keyValueMap(_savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].objData(_core_global_registries__WEBPACK_IMPORTED_MODULE_7__["gComponentRegistry"]), false),
        };
    }

    /**
     * Returns a clone of this entity
     */
    clone() {
        const staticComp = this.components.StaticMapEntity;
        const buildingData = Object(_building_codes__WEBPACK_IMPORTED_MODULE_8__["getBuildingDataFromCode"])(staticComp.code);

        const clone = buildingData.metaInstance.createEntity({
            root: this.root,
            origin: staticComp.origin,
            originalRotation: staticComp.originalRotation,
            rotation: staticComp.rotation,
            rotationVariant: buildingData.rotationVariant,
            variant: buildingData.variant,
        });

        for (const key in this.components) {
            /** @type {Component} */ (this.components[key]).copyAdditionalStateTo(clone.components[key]);
        }

        return clone;
    }

    /**
     * Adds a new component, only possible until the entity is registered on the entity manager,
     * after that use @see EntityManager.addDynamicComponent
     * @param {Component} componentInstance
     * @param {boolean} force Used by the entity manager. Internal parameter, do not change
     */
    addComponent(componentInstance, force = false) {
        if (!force && this.registered) {
            this.root.entityMgr.attachDynamicComponent(this, componentInstance);
            return;
        }
        window.assert(force || !this.registered, "Entity already registered, use EntityManager.addDynamicComponent");
        const id = /** @type {typeof Component} */ (componentInstance.constructor).getId();
        window.assert(!this.components[id], "Component already present");
        this.components[id] = componentInstance;
    }

    /**
     * Removes a given component, only possible until the entity is registered on the entity manager,
     * after that use @see EntityManager.removeDynamicComponent
     * @param {typeof Component} componentClass
     * @param {boolean} force
     */
    removeComponent(componentClass, force = false) {
        if (!force && this.registered) {
            this.root.entityMgr.removeDynamicComponent(this, componentClass);
            return;
        }
        window.assert(
            force || !this.registered,
            "Entity already registered, use EntityManager.removeDynamicComponent"
        );
        const id = componentClass.getId();
        window.assert(this.components[id], "Component does not exist on entity");
        delete this.components[id];
    }

    /**
     * Draws the entity, to override use @see Entity.drawImpl
     * @param {DrawParameters} parameters
     */
    drawDebugOverlays(parameters) {
        const context = parameters.context;
        const staticComp = this.components.StaticMapEntity;

        if (false) {}

        if (false) {}
        // this.drawImpl(parameters);
    }

    ///// Helper interfaces

    ///// Interface to override by subclasses

    /**
     * override, should draw the entity
     * @param {DrawParameters} parameters
     */
    drawImpl(parameters) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }
}


/***/ }),

/***/ "./src/js/game/entity_components.js":
/*!******************************************!*\
  !*** ./src/js/game/entity_components.js ***!
  \******************************************/
/*! exports provided: EntityComponentStorage */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EntityComponentStorage", function() { return EntityComponentStorage; });


/**
 * Typedefs for all entity components. These are not actually present on the entity,
 * thus they are undefined by default
 */
class EntityComponentStorage {
    constructor() {

    }
}


/***/ }),

/***/ "./src/js/game/entity_manager.js":
/*!***************************************!*\
  !*** ./src/js/game/entity_manager.js ***!
  \***************************************/
/*! exports provided: EntityManager */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EntityManager", function() { return EntityManager; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _component__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./component */ "./src/js/game/component.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");








const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_5__["createLogger"])("entity_manager");

// Manages all entities

// NOTICE: We use arrayDeleteValue instead of fastArrayDeleteValue since that does not preserve the order
// This is slower but we need it for the street path generation

class EntityManager extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_4__["BasicSerializableObject"] {
    constructor(root) {
        super();

        /** @type {GameRoot} */
        this.root = root;

        /** @type {Array<Entity>} */
        this.entities = [];

        // We store a separate list with entities to destroy, since we don't destroy
        // them instantly
        /** @type {Array<Entity>} */
        this.destroyList = [];

        // Store a map from componentid to entities - This is used by the game system
        // for faster processing
        /** @type {Object.<string, Array<Entity>>} */
        this.componentToEntity = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["newEmptyMap"])();

        // Store the next uid to use
        this.nextUid = 10000;
    }

    static getId() {
        return "EntityManager";
    }

    static getSchema() {
        return {
            nextUid: _savegame_serialization__WEBPACK_IMPORTED_MODULE_4__["types"].uint,
        };
    }

    getStatsText() {
        return this.entities.length + " entities [" + this.destroyList.length + " to kill]";
    }

    // Main update
    update() {
        this.processDestroyList();
    }

    /**
     * Registers a new entity
     * @param {Entity} entity
     * @param {number=} uid Optional predefined uid
     */
    registerEntity(entity, uid = null) {
        if (false) {}
        window.assert(!entity.destroyed, `Attempting to register destroyed entity ${entity}`);

        if (false) {}

        this.entities.push(entity);

        // Register into the componentToEntity map
        for (const componentId in entity.components) {
            if (entity.components[componentId]) {
                if (this.componentToEntity[componentId]) {
                    this.componentToEntity[componentId].push(entity);
                } else {
                    this.componentToEntity[componentId] = [entity];
                }
            }
        }

        // Give each entity a unique id
        entity.uid = uid ? uid : this.generateUid();
        entity.registered = true;

        this.root.signals.entityAdded.dispatch(entity);
    }

    /**
     * Generates a new uid
     * @returns {number}
     */
    generateUid() {
        return this.nextUid++;
    }

    /**
     * Call to attach a new component after the creation of the entity
     * @param {Entity} entity
     * @param {Component} component
     */
    attachDynamicComponent(entity, component) {
        entity.addComponent(component, true);
        const componentId = /** @type {typeof Component} */ (component.constructor).getId();
        if (this.componentToEntity[componentId]) {
            this.componentToEntity[componentId].push(entity);
        } else {
            this.componentToEntity[componentId] = [entity];
        }
        this.root.signals.entityGotNewComponent.dispatch(entity);
    }

    /**
     * Call to remove a component after the creation of the entity
     * @param {Entity} entity
     * @param {typeof Component} component
     */
    removeDynamicComponent(entity, component) {
        entity.removeComponent(component, true);
        const componentId = /** @type {typeof Component} */ (component.constructor).getId();

        Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["fastArrayDeleteValue"])(this.componentToEntity[componentId], entity);
        this.root.signals.entityComponentRemoved.dispatch(entity);
    }

    /**
     * Finds an entity buy its uid, kinda slow since it loops over all entities
     * @param {number} uid
     * @param {boolean=} errorWhenNotFound
     * @returns {Entity}
     */
    findByUid(uid, errorWhenNotFound = true) {
        const arr = this.entities;
        for (let i = 0, len = arr.length; i < len; ++i) {
            const entity = arr[i];
            if (entity.uid === uid) {
                if (entity.queuedForDestroy || entity.destroyed) {
                    if (errorWhenNotFound) {
                        logger.warn("Entity with UID", uid, "not found (destroyed)");
                    }
                    return null;
                }
                return entity;
            }
        }
        if (errorWhenNotFound) {
            logger.warn("Entity with UID", uid, "not found");
        }
        return null;
    }

    /**
     * Returns a map which gives a mapping from UID to Entity.
     * This map is not updated.
     *
     * @returns {Map<number, Entity>}
     */
    getFrozenUidSearchMap() {
        const result = new Map();
        const array = this.entities;
        for (let i = 0, len = array.length; i < len; ++i) {
            const entity = array[i];
            if (!entity.queuedForDestroy && !entity.destroyed) {
                result.set(entity.uid, entity);
            }
        }
        return result;
    }

    /**
     * Returns all entities having the given component
     * @param {typeof Component} componentHandle
     * @returns {Array<Entity>} entities
     */
    getAllWithComponent(componentHandle) {
        return this.componentToEntity[componentHandle.getId()] || [];
    }

    /**
     * Unregisters all components of an entity from the component to entity mapping
     * @param {Entity} entity
     */
    unregisterEntityComponents(entity) {
        for (const componentId in entity.components) {
            if (entity.components[componentId]) {
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["arrayDeleteValue"])(this.componentToEntity[componentId], entity);
            }
        }
    }

    // Processes the entities to destroy and actually destroys them
    /* eslint-disable max-statements */
    processDestroyList() {
        for (let i = 0; i < this.destroyList.length; ++i) {
            const entity = this.destroyList[i];

            // Remove from entities list
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["arrayDeleteValue"])(this.entities, entity);

            // Remove from componentToEntity list
            this.unregisterEntityComponents(entity);

            entity.registered = false;
            entity.destroyed = true;

            this.root.signals.entityDestroyed.dispatch(entity);
        }

        this.destroyList = [];
    }

    /**
     * Queues an entity for destruction
     * @param {Entity} entity
     */
    destroyEntity(entity) {
        if (entity.destroyed) {
            logger.error("Tried to destroy already destroyed entity:", entity.uid);
            return;
        }

        if (entity.queuedForDestroy) {
            logger.error("Trying to destroy entity which is already queued for destroy!", entity.uid);
            return;
        }

        if (this.destroyList.indexOf(entity) < 0) {
            this.destroyList.push(entity);
            entity.queuedForDestroy = true;
            this.root.signals.entityQueuedForDestroy.dispatch(entity);
        } else {
            window.assert(false, "Trying to destroy entity twice");
        }
    }
}


/***/ }),

/***/ "./src/js/game/game_loading_overlay.js":
/*!*********************************************!*\
  !*** ./src/js/game/game_loading_overlay.js ***!
  \*********************************************/
/*! exports provided: GameLoadingOverlay */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameLoadingOverlay", function() { return GameLoadingOverlay; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");




class GameLoadingOverlay {
    /**
     *
     * @param {Application} app
     * @param {HTMLElement} parent
     */
    constructor(app, parent) {
        this.app = app;
        this.parent = parent;

        /** @type {HTMLElement} */
        this.element = null;
    }

    /**
     * Removes the overlay if its currently visible
     */
    removeIfAttached() {
        if (this.element) {
            this.element.remove();
            this.element = null;
        }
    }

    /**
     * Returns if the loading overlay is attached
     */
    isAttached() {
        return this.element;
    }

    /**
     * Shows a super basic overlay
     */
    showBasic() {
        window.assert(!this.element, "Loading overlay already visible, cant show again");
        this.element = document.createElement("div");
        this.element.classList.add("gameLoadingOverlay");
        this.parent.appendChild(this.element);
        this.internalAddSpinnerAndText(this.element);
        this.internalAddHint(this.element);
    }

    /**
     * Adds a text with 'loading' and a spinner
     * @param {HTMLElement} element
     */
    internalAddSpinnerAndText(element) {
        const inner = document.createElement("span");
        inner.classList.add("prefab_LoadingTextWithAnim");
        element.appendChild(inner);
    }

    /**
     * Adds a random hint
     * @param {HTMLElement} element
     */
    internalAddHint(element) {
        const hint = document.createElement("span");
        hint.innerHTML = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["randomChoice"])(_translations__WEBPACK_IMPORTED_MODULE_1__["T"].tips);
        hint.classList.add("prefab_GameHint");
        element.appendChild(hint);
    }
}


/***/ }),

/***/ "./src/js/game/game_mode.js":
/*!**********************************!*\
  !*** ./src/js/game/game_mode.js ***!
  \**********************************/
/*! exports provided: GameMode */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameMode", function() { return GameMode; });
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");




/** @typedef {{
 *   shape: string,
 *   amount: number
 * }} UpgradeRequirement */

/** @typedef {{
 *   required: Array<UpgradeRequirement>
 *   improvement?: number,
 *   excludePrevious?: boolean
 * }} TierRequirement */

/** @typedef {Array<TierRequirement>} UpgradeTiers */

/** @typedef {{
 *   shape: string,
 *   required: number,
 *   reward: enumHubGoalRewards,
 *   throughputOnly?: boolean
 * }} LevelDefinition */

class GameMode {
    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;
    }

    /**
     * Should return all available upgrades
     * @returns {Object<string, UpgradeTiers>}
     */
    getUpgrades() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return null;
    }

    /**
     * Returns the blueprint shape key
     * @returns {string}
     */
    getBlueprintShapeKey() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return null;
    }

    /**
     * Returns the goals for all levels including their reward
     * @returns {Array<LevelDefinition>}
     */
    getLevelDefinitions() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return null;
    }

    /**
     * Should return whether free play is available or if the game stops
     * after the predefined levels
     * @returns {boolean}
     */
    getIsFreeplayAvailable() {
        return true;
    }
}


/***/ }),

/***/ "./src/js/game/game_speed_registry.js":
/*!********************************************!*\
  !*** ./src/js/game/game_speed_registry.js ***!
  \********************************************/
/*! exports provided: initGameSpeedRegistry */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initGameSpeedRegistry", function() { return initGameSpeedRegistry; });
/* harmony import */ var _time_regular_game_speed__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./time/regular_game_speed */ "./src/js/game/time/regular_game_speed.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");



function initGameSpeedRegistry() {
    _core_global_registries__WEBPACK_IMPORTED_MODULE_1__["gGameSpeedRegistry"].register(_time_regular_game_speed__WEBPACK_IMPORTED_MODULE_0__["RegularGameSpeed"]);

    // Others are disabled for now
}


/***/ }),

/***/ "./src/js/game/game_system.js":
/*!************************************!*\
  !*** ./src/js/game/game_system.js ***!
  \************************************/
/*! exports provided: GameSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameSystem", function() { return GameSystem; });

/**
 * A game system processes all entities which match a given schema, usually a list of
 * required components. This is the core of the game logic.
 */
class GameSystem {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;
    }

    ///// PUBLIC API /////

    /**
     * Updates the game system, override to perform logic
     */
    update() {}

    /**
     * Override, do not call this directly, use startDraw()
     * @param {DrawParameters} parameters
     */
    draw(parameters) {}

    /**
     * Should refresh all caches
     */
    refreshCaches() {}

    /**
     * @see GameSystem.draw Wrapper arround the draw method
     * @param {DrawParameters} parameters
     */
    startDraw(parameters) {
        this.draw(parameters);
    }
}


/***/ }),

/***/ "./src/js/game/game_system_manager.js":
/*!********************************************!*\
  !*** ./src/js/game/game_system_manager.js ***!
  \********************************************/
/*! exports provided: GameSystemManager */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameSystemManager", function() { return GameSystemManager; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _systems_belt__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./systems/belt */ "./src/js/game/systems/belt.js");
/* harmony import */ var _systems_item_ejector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./systems/item_ejector */ "./src/js/game/systems/item_ejector.js");
/* harmony import */ var _systems_map_resources__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./systems/map_resources */ "./src/js/game/systems/map_resources.js");
/* harmony import */ var _systems_miner__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./systems/miner */ "./src/js/game/systems/miner.js");
/* harmony import */ var _systems_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./systems/item_processor */ "./src/js/game/systems/item_processor.js");
/* harmony import */ var _systems_underground_belt__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./systems/underground_belt */ "./src/js/game/systems/underground_belt.js");
/* harmony import */ var _systems_hub__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./systems/hub */ "./src/js/game/systems/hub.js");
/* harmony import */ var _systems_static_map_entity__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./systems/static_map_entity */ "./src/js/game/systems/static_map_entity.js");
/* harmony import */ var _systems_item_acceptor__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./systems/item_acceptor */ "./src/js/game/systems/item_acceptor.js");
/* harmony import */ var _systems_storage__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./systems/storage */ "./src/js/game/systems/storage.js");
/* harmony import */ var _systems_wired_pins__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./systems/wired_pins */ "./src/js/game/systems/wired_pins.js");
/* harmony import */ var _systems_belt_underlays__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./systems/belt_underlays */ "./src/js/game/systems/belt_underlays.js");
/* harmony import */ var _systems_wire__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./systems/wire */ "./src/js/game/systems/wire.js");
/* harmony import */ var _systems_constant_signal__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./systems/constant_signal */ "./src/js/game/systems/constant_signal.js");
/* harmony import */ var _systems_logic_gate__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./systems/logic_gate */ "./src/js/game/systems/logic_gate.js");
/* harmony import */ var _systems_lever__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./systems/lever */ "./src/js/game/systems/lever.js");
/* harmony import */ var _systems_display__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./systems/display */ "./src/js/game/systems/display.js");
/* harmony import */ var _systems_item_processor_overlays__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./systems/item_processor_overlays */ "./src/js/game/systems/item_processor_overlays.js");
/* harmony import */ var _systems_belt_reader__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./systems/belt_reader */ "./src/js/game/systems/belt_reader.js");
/* harmony import */ var _systems_filter__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./systems/filter */ "./src/js/game/systems/filter.js");
/* harmony import */ var _systems_item_producer__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./systems/item_producer */ "./src/js/game/systems/item_producer.js");

























const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("game_system_manager");

class GameSystemManager {
    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;

        this.systems = {

        };
        this.systemUpdateOrder = [];

        this.internalInitSystems();
    }

    /**
     * Initializes all systems
     */
    internalInitSystems() {
        const add = (id, systemClass) => {
            this.systems[id] = new systemClass(this.root);
            this.systemUpdateOrder.push(id);
        };

        // Order is important!

        // IMPORTANT: Item acceptor must be before the belt, because it may not tick after the belt
        // has put in the item into the acceptor animation, otherwise its off
        add("itemAcceptor", _systems_item_acceptor__WEBPACK_IMPORTED_MODULE_9__["ItemAcceptorSystem"]);

        add("belt", _systems_belt__WEBPACK_IMPORTED_MODULE_1__["BeltSystem"]);

        add("undergroundBelt", _systems_underground_belt__WEBPACK_IMPORTED_MODULE_6__["UndergroundBeltSystem"]);

        add("miner", _systems_miner__WEBPACK_IMPORTED_MODULE_4__["MinerSystem"]);

        add("storage", _systems_storage__WEBPACK_IMPORTED_MODULE_10__["StorageSystem"]);

        add("itemProcessor", _systems_item_processor__WEBPACK_IMPORTED_MODULE_5__["ItemProcessorSystem"]);

        add("filter", _systems_filter__WEBPACK_IMPORTED_MODULE_20__["FilterSystem"]);

        add("itemProducer", _systems_item_producer__WEBPACK_IMPORTED_MODULE_21__["ItemProducerSystem"]);

        add("itemEjector", _systems_item_ejector__WEBPACK_IMPORTED_MODULE_2__["ItemEjectorSystem"]);

        add("mapResources", _systems_map_resources__WEBPACK_IMPORTED_MODULE_3__["MapResourcesSystem"]);

        add("hub", _systems_hub__WEBPACK_IMPORTED_MODULE_7__["HubSystem"]);

        add("staticMapEntities", _systems_static_map_entity__WEBPACK_IMPORTED_MODULE_8__["StaticMapEntitySystem"]);

        add("wiredPins", _systems_wired_pins__WEBPACK_IMPORTED_MODULE_11__["WiredPinsSystem"]);

        add("beltUnderlays", _systems_belt_underlays__WEBPACK_IMPORTED_MODULE_12__["BeltUnderlaysSystem"]);

        add("constantSignal", _systems_constant_signal__WEBPACK_IMPORTED_MODULE_14__["ConstantSignalSystem"]);

        // WIRES section
        add("lever", _systems_lever__WEBPACK_IMPORTED_MODULE_16__["LeverSystem"]);

        // Wires must be before all gate, signal etc logic!
        add("wire", _systems_wire__WEBPACK_IMPORTED_MODULE_13__["WireSystem"]);

        // IMPORTANT: We have 2 phases: In phase 1 we compute the output values of all gates,
        // processors etc. In phase 2 we propagate it through the wires network
        add("logicGate", _systems_logic_gate__WEBPACK_IMPORTED_MODULE_15__["LogicGateSystem"]);
        add("beltReader", _systems_belt_reader__WEBPACK_IMPORTED_MODULE_19__["BeltReaderSystem"]);

        add("display", _systems_display__WEBPACK_IMPORTED_MODULE_17__["DisplaySystem"]);

        add("itemProcessorOverlays", _systems_item_processor_overlays__WEBPACK_IMPORTED_MODULE_18__["ItemProcessorOverlaysSystem"]);

        logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
    }

    /**
     * Updates all systems
     */
    update() {
        for (let i = 0; i < this.systemUpdateOrder.length; ++i) {
            const system = this.systems[this.systemUpdateOrder[i]];
            system.update();
        }
    }

    refreshCaches() {
        for (let i = 0; i < this.systemUpdateOrder.length; ++i) {
            const system = this.systems[this.systemUpdateOrder[i]];
            system.refreshCaches();
        }
    }
}


/***/ }),

/***/ "./src/js/game/game_system_with_filter.js":
/*!************************************************!*\
  !*** ./src/js/game/game_system_with_filter.js ***!
  \************************************************/
/*! exports provided: GameSystemWithFilter */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameSystemWithFilter", function() { return GameSystemWithFilter; });
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _game_system__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./game_system */ "./src/js/game/game_system.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");







class GameSystemWithFilter extends _game_system__WEBPACK_IMPORTED_MODULE_1__["GameSystem"] {
    /**
     * Constructs a new game system with the given component filter. It will process
     * all entities which have *all* of the passed components
     * @param {GameRoot} root
     * @param {Array<typeof Component>} requiredComponents
     */
    constructor(root, requiredComponents) {
        super(root);
        this.requiredComponents = requiredComponents;
        this.requiredComponentIds = requiredComponents.map(component => component.getId());

        /**
         * All entities which match the current components
         * @type {Array<Entity>}
         */
        this.allEntities = [];

        this.root.signals.entityAdded.add(this.internalPushEntityIfMatching, this);
        this.root.signals.entityGotNewComponent.add(this.internalReconsiderEntityToAdd, this);
        this.root.signals.entityComponentRemoved.add(this.internalCheckEntityAfterComponentRemoval, this);
        this.root.signals.entityQueuedForDestroy.add(this.internalPopEntityIfMatching, this);

        this.root.signals.postLoadHook.add(this.internalPostLoadHook, this);
        this.root.signals.bulkOperationFinished.add(this.refreshCaches, this);
    }

    /**
     * @param {Entity} entity
     */
    internalPushEntityIfMatching(entity) {
        for (let i = 0; i < this.requiredComponentIds.length; ++i) {
            if (!entity.components[this.requiredComponentIds[i]]) {
                return;
            }
        }

        // This is slow!
        if (false) {}

        this.internalRegisterEntity(entity);
    }

    /**
     *
     * @param {Entity} entity
     */
    internalCheckEntityAfterComponentRemoval(entity) {
        if (this.allEntities.indexOf(entity) < 0) {
            // Entity wasn't interesting anyways
            return;
        }

        for (let i = 0; i < this.requiredComponentIds.length; ++i) {
            if (!entity.components[this.requiredComponentIds[i]]) {
                // Entity is not interesting anymore
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["arrayDeleteValue"])(this.allEntities, entity);
            }
        }
    }

    /**
     *
     * @param {Entity} entity
     */
    internalReconsiderEntityToAdd(entity) {
        for (let i = 0; i < this.requiredComponentIds.length; ++i) {
            if (!entity.components[this.requiredComponentIds[i]]) {
                return;
            }
        }
        if (this.allEntities.indexOf(entity) >= 0) {
            return;
        }
        this.internalRegisterEntity(entity);
    }

    refreshCaches() {
        // Remove all entities which are queued for destroy
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            if (entity.queuedForDestroy || entity.destroyed) {
                this.allEntities.splice(i, 1);
                i -= 1;
            }
        }

        this.allEntities.sort((a, b) => a.uid - b.uid);
    }

    /**
     * Recomputes all target entities after the game has loaded
     */
    internalPostLoadHook() {
        this.refreshCaches();
    }

    /**
     *
     * @param {Entity} entity
     */
    internalRegisterEntity(entity) {
        this.allEntities.push(entity);

        if (this.root.gameInitialized && !this.root.bulkOperationRunning) {
            // Sort entities by uid so behaviour is predictable
            this.allEntities.sort((a, b) => a.uid - b.uid);
        }
    }

    /**
     *
     * @param {Entity} entity
     */
    internalPopEntityIfMatching(entity) {
        if (this.root.bulkOperationRunning) {
            // We do this in refreshCaches afterwards
            return;
        }
        const index = this.allEntities.indexOf(entity);
        if (index >= 0) {
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["arrayDelete"])(this.allEntities, index);
        }
    }
}


/***/ }),

/***/ "./src/js/game/hints.js":
/*!******************************!*\
  !*** ./src/js/game/hints.js ***!
  \******************************/
/*! exports provided: getRandomHint */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getRandomHint", function() { return getRandomHint; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");



const hintsShown = [];

/**
 * Finds a new hint to show about the game which the user hasn't seen within this session
 */
function getRandomHint() {
    let maxTries = 100 * _translations__WEBPACK_IMPORTED_MODULE_1__["T"].tips.length;

    while (maxTries-- > 0) {
        const hint = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["randomChoice"])(_translations__WEBPACK_IMPORTED_MODULE_1__["T"].tips);
        if (!hintsShown.includes(hint)) {
            hintsShown.push(hint);
            return hint;
        }
    }

    // All tips shown so far
    return Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["randomChoice"])(_translations__WEBPACK_IMPORTED_MODULE_1__["T"].tips);
}


/***/ }),

/***/ "./src/js/game/hub_goals.js":
/*!**********************************!*\
  !*** ./src/js/game/hub_goals.js ***!
  \**********************************/
/*! exports provided: HubGoals */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HubGoals", function() { return HubGoals; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_rng__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/rng */ "./src/js/core/rng.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./colors */ "./src/js/game/colors.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _production_analytics__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./production_analytics */ "./src/js/game/production_analytics.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./tutorial_goals */ "./src/js/game/tutorial_goals.js");











class HubGoals extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["BasicSerializableObject"] {
    static getId() {
        return "HubGoals";
    }

    static getSchema() {
        return {
            level: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].uint,
            storedShapes: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].keyValueMap(_savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].uint),
            upgradeLevels: _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].keyValueMap(_savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].uint),
        };
    }

    /**
     *
     * @param {*} data
     * @param {GameRoot} root
     */
    deserialize(data, root) {
        const errorCode = super.deserialize(data);
        if (errorCode) {
            return errorCode;
        }

        const levels = root.gameMode.getLevelDefinitions();

        // If freeplay is not available, clamp the level
        if (!root.gameMode.getIsFreeplayAvailable()) {
            this.level = Math.min(this.level, levels.length);
        }

        // Compute gained rewards
        for (let i = 0; i < this.level - 1; ++i) {
            if (i < levels.length) {
                const reward = levels[i].reward;
                this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
            }
        }

        // Compute upgrade improvements
        const upgrades = this.root.gameMode.getUpgrades();
        for (const upgradeId in upgrades) {
            const tiers = upgrades[upgradeId];
            const level = this.upgradeLevels[upgradeId] || 0;
            let totalImprovement = 1;
            for (let i = 0; i < level; ++i) {
                totalImprovement += tiers[i].improvement;
            }
            this.upgradeImprovements[upgradeId] = totalImprovement;
        }

        // Compute current goal
        this.computeNextGoal();
    }

    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        super();

        this.root = root;

        this.level = 1;

        /**
         * Which story rewards we already gained
         * @type {Object.<string, number>}
         */
        this.gainedRewards = {};

        /**
         * Mapping from shape hash -> amount
         * @type {Object<string, number>}
         */
        this.storedShapes = {};

        /**
         * Stores the levels for all upgrades
         * @type {Object<string, number>}
         */
        this.upgradeLevels = {};

        /**
         * Stores the improvements for all upgrades
         * @type {Object<string, number>}
         */
        this.upgradeImprovements = {};

        // Reset levels first
        const upgrades = this.root.gameMode.getUpgrades();
        for (const key in upgrades) {
            this.upgradeLevels[key] = 0;
            this.upgradeImprovements[key] = 1;
        }

        this.computeNextGoal();

        // Allow quickly switching goals in dev mode
        if (false) {}
    }

    /**
     * Returns whether the end of the demo is reached
     * @returns {boolean}
     */
    isEndOfDemoReached() {
        return (
            !this.root.gameMode.getIsFreeplayAvailable() &&
            this.level >= this.root.gameMode.getLevelDefinitions().length
        );
    }

    /**
     * Returns how much of the current shape is stored
     * @param {ShapeDefinition} definition
     * @returns {number}
     */
    getShapesStored(definition) {
        return this.storedShapes[definition.getHash()] || 0;
    }

    /**
     * @param {string} key
     * @param {number} amount
     */
    takeShapeByKey(key, amount) {
        window.assert(this.getShapesStoredByKey(key) >= amount, "Can not afford: " + key + " x " + amount);
        window.assert(amount >= 0, "Amount < 0 for " + key);
        window.assert(Number.isInteger(amount), "Invalid amount: " + amount);
        this.storedShapes[key] = (this.storedShapes[key] || 0) - amount;
        return;
    }

    /**
     * Returns how much of the current shape is stored
     * @param {string} key
     * @returns {number}
     */
    getShapesStoredByKey(key) {
        return this.storedShapes[key] || 0;
    }

    /**
     * Returns how much of the current goal was already delivered
     */
    getCurrentGoalDelivered() {
        if (this.currentGoal.throughputOnly) {
            return (
                this.root.productionAnalytics.getCurrentShapeRate(
                    _production_analytics__WEBPACK_IMPORTED_MODULE_6__["enumAnalyticsDataSource"].delivered,
                    this.currentGoal.definition
                ) / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].analyticsSliceDurationSeconds
            );
        }

        return this.getShapesStored(this.currentGoal.definition);
    }

    /**
     * Returns the current level of a given upgrade
     * @param {string} upgradeId
     */
    getUpgradeLevel(upgradeId) {
        return this.upgradeLevels[upgradeId] || 0;
    }

    /**
     * Returns whether the given reward is already unlocked
     * @param {enumHubGoalRewards} reward
     */
    isRewardUnlocked(reward) {
        if (false) {}
        return !!this.gainedRewards[reward];
    }

    /**
     * Handles the given definition, by either accounting it towards the
     * goal or otherwise granting some points
     * @param {ShapeDefinition} definition
     */
    handleDefinitionDelivered(definition) {
        const hash = definition.getHash();
        this.storedShapes[hash] = (this.storedShapes[hash] || 0) + 1;

        this.root.signals.shapeDelivered.dispatch(definition);

        // Check if we have enough for the next level
        if (
            this.getCurrentGoalDelivered() >= this.currentGoal.required ||
            ( false && false)
        ) {
            if (!this.isEndOfDemoReached()) {
                this.onGoalCompleted();
            }
        }
    }

    /**
     * Creates the next goal
     */
    computeNextGoal() {
        const storyIndex = this.level - 1;
        const levels = this.root.gameMode.getLevelDefinitions();
        if (storyIndex < levels.length) {
            const { shape, required, reward, throughputOnly } = levels[storyIndex];
            this.currentGoal = {
                /** @type {ShapeDefinition} */
                definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
                required,
                reward,
                throughputOnly,
            };
            return;
        }

        //Floor Required amount to remove confusion
        const required = Math.min(200, Math.floor(4 + (this.level - 27) * 0.25));
        this.currentGoal = {
            definition: this.computeFreeplayShape(this.level),
            required,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_9__["enumHubGoalRewards"].no_reward_freeplay,
            throughputOnly: true,
        };
    }

    /**
     * Called when the level was completed
     */
    onGoalCompleted() {
        const reward = this.currentGoal.reward;
        this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;

        this.root.app.gameAnalytics.handleLevelCompleted(this.level);
        ++this.level;
        this.computeNextGoal();

        this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
    }

    /**
     * Returns whether we are playing in free-play
     */
    isFreePlay() {
        return this.level >= this.root.gameMode.getLevelDefinitions().length;
    }

    /**
     * Returns whether a given upgrade can be unlocked
     * @param {string} upgradeId
     */
    canUnlockUpgrade(upgradeId) {
        const tiers = this.root.gameMode.getUpgrades()[upgradeId];
        const currentLevel = this.getUpgradeLevel(upgradeId);

        if (currentLevel >= tiers.length) {
            // Max level
            return false;
        }

        if (false) {}

        const tierData = tiers[currentLevel];

        for (let i = 0; i < tierData.required.length; ++i) {
            const requirement = tierData.required[i];
            if ((this.storedShapes[requirement.shape] || 0) < requirement.amount) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns the number of available upgrades
     * @returns {number}
     */
    getAvailableUpgradeCount() {
        let count = 0;
        for (const upgradeId in this.root.gameMode.getUpgrades()) {
            if (this.canUnlockUpgrade(upgradeId)) {
                ++count;
            }
        }
        return count;
    }

    /**
     * Tries to unlock the given upgrade
     * @param {string} upgradeId
     * @returns {boolean}
     */
    tryUnlockUpgrade(upgradeId) {
        if (!this.canUnlockUpgrade(upgradeId)) {
            return false;
        }

        const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
        const currentLevel = this.getUpgradeLevel(upgradeId);

        const tierData = upgradeTiers[currentLevel];
        if (!tierData) {
            return false;
        }

        if (false) {} else {
            for (let i = 0; i < tierData.required.length; ++i) {
                const requirement = tierData.required[i];

                // Notice: Don't have to check for hash here
                this.storedShapes[requirement.shape] -= requirement.amount;
            }
        }

        this.upgradeLevels[upgradeId] = (this.upgradeLevels[upgradeId] || 0) + 1;
        this.upgradeImprovements[upgradeId] += tierData.improvement;

        this.root.signals.upgradePurchased.dispatch(upgradeId);

        this.root.app.gameAnalytics.handleUpgradeUnlocked(upgradeId, currentLevel);

        return true;
    }

    /**
     * Picks random colors which are close to each other
     * @param {RandomNumberGenerator} rng
     */
    generateRandomColorSet(rng, allowUncolored = false) {
        const colorWheel = [
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].red,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].yellow,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].green,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].cyan,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].blue,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].purple,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].red,
            _colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].yellow,
        ];

        const universalColors = [_colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].white];
        if (allowUncolored) {
            universalColors.push(_colors__WEBPACK_IMPORTED_MODULE_4__["enumColors"].uncolored);
        }
        const index = rng.nextIntRange(0, colorWheel.length - 2);
        const pickedColors = colorWheel.slice(index, index + 3);
        pickedColors.push(rng.choice(universalColors));
        return pickedColors;
    }

    /**
     * Creates a (seeded) random shape
     * @param {number} level
     * @returns {ShapeDefinition}
     */
    computeFreeplayShape(level) {
        const layerCount = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["clamp"])(this.level / 25, 2, 4);

        /** @type {Array<import("./shape_definition").ShapeLayer>} */
        let layers = [];

        const rng = new _core_rng__WEBPACK_IMPORTED_MODULE_1__["RandomNumberGenerator"](this.root.map.seed + "/" + level);

        const colors = this.generateRandomColorSet(rng, level > 35);

        let pickedSymmetry = null; // pairs of quadrants that must be the same
        let availableShapes = [_shape_definition__WEBPACK_IMPORTED_MODULE_8__["enumSubShape"].rect, _shape_definition__WEBPACK_IMPORTED_MODULE_8__["enumSubShape"].circle, _shape_definition__WEBPACK_IMPORTED_MODULE_8__["enumSubShape"].star];
        if (rng.next() < 0.5) {
            pickedSymmetry = [
                // radial symmetry
                [0, 2],
                [1, 3],
            ];
            availableShapes.push(_shape_definition__WEBPACK_IMPORTED_MODULE_8__["enumSubShape"].windmill); // windmill looks good only in radial symmetry
        } else {
            const symmetries = [
                [
                    // horizontal axis
                    [0, 3],
                    [1, 2],
                ],
                [
                    // vertical axis
                    [0, 1],
                    [2, 3],
                ],
                [
                    // diagonal axis
                    [0, 2],
                    [1],
                    [3],
                ],
                [
                    // other diagonal axis
                    [1, 3],
                    [0],
                    [2],
                ],
            ];
            pickedSymmetry = rng.choice(symmetries);
        }

        const randomColor = () => rng.choice(colors);
        const randomShape = () => rng.choice(Object.values(_shape_definition__WEBPACK_IMPORTED_MODULE_8__["enumSubShape"]));

        let anyIsMissingTwo = false;

        for (let i = 0; i < layerCount; ++i) {
            /** @type {import("./shape_definition").ShapeLayer} */
            const layer = [null, null, null, null];

            for (let j = 0; j < pickedSymmetry.length; ++j) {
                const group = pickedSymmetry[j];
                const shape = randomShape();
                const color = randomColor();
                for (let k = 0; k < group.length; ++k) {
                    const quad = group[k];
                    layer[quad] = {
                        subShape: shape,
                        color,
                    };
                }
            }

            // Sometimes they actually are missing *two* ones!
            // Make sure at max only one layer is missing it though, otherwise we could
            // create an uncreateable shape
            if (level > 75 && rng.next() > 0.95 && !anyIsMissingTwo) {
                layer[rng.nextIntRange(0, 4)] = null;
                anyIsMissingTwo = true;
            }

            layers.push(layer);
        }

        const definition = new _shape_definition__WEBPACK_IMPORTED_MODULE_8__["ShapeDefinition"]({ layers });
        return this.root.shapeDefinitionMgr.registerOrReturnHandle(definition);
    }

    ////////////// HELPERS

    /**
     * Belt speed
     * @returns {number} items / sec
     */
    getBeltBaseSpeed() {
        return _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
    }

    /**
     * Underground belt speed
     * @returns {number} items / sec
     */
    getUndergroundBeltBaseSpeed() {
        return _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
    }

    /**
     * Miner speed
     * @returns {number} items / sec
     */
    getMinerBaseSpeed() {
        return _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].minerSpeedItemsPerSecond * this.upgradeImprovements.miner;
    }

    /**
     * Processor speed
     * @param {enumItemProcessorTypes} processorType
     * @returns {number} items / sec
     */
    getProcessorBaseSpeed(processorType) {
        switch (processorType) {
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].trash:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].hub:
                return 1e30;
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].balancer:
                return _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2;
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].reader:
                return _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond * this.upgradeImprovements.belt;

            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].mixer:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painter:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painterDouble:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].painterQuad: {
                window.assert(
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].buildingSpeeds[processorType],
                    "Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType
                );
                return (
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond *
                    this.upgradeImprovements.painting *
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].buildingSpeeds[processorType]
                );
            }

            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutter:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].cutterQuad:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotaterCCW:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].rotater180:
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_5__["enumItemProcessorTypes"].stacker: {
                window.assert(
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].buildingSpeeds[processorType],
                    "Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType
                );
                return (
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond *
                    this.upgradeImprovements.processors *
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].buildingSpeeds[processorType]
                );
            }
            default:
                window.assert(false, "invalid processor type: " + processorType);
        }

        return 1 / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].beltSpeedItemsPerSecond;
    }
}


/***/ }),

/***/ "./src/js/game/hud/base_hud_part.js":
/*!******************************************!*\
  !*** ./src/js/game/hud/base_hud_part.js ***!
  \******************************************/
/*! exports provided: BaseHUDPart */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseHUDPart", function() { return BaseHUDPart; });
/* harmony import */ var _core_click_detector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../key_action_mapper */ "./src/js/game/key_action_mapper.js");




class BaseHUDPart {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;

        /** @type {Array<ClickDetector>} */
        this.clickDetectors = [];
    }

    /**
     * Should create all require elements
     * @param {HTMLElement} parent
     */
    createElements(parent) {}

    /**
     * Should initialize the element, called *after* the elements have been created
     */
    initialize() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Should update any required logic
     */
    update() {}

    /**
     * Should draw the hud
     * @param {DrawParameters} parameters
     */
    draw(parameters) {}

    /**
     * Should draw any overlays (screen space)
     * @param {DrawParameters} parameters
     */
    drawOverlays(parameters) {}

    /**
     * Should return true if the widget has a modal dialog opened and thus
     * the game does not need to update / redraw
     * @returns {boolean}
     */
    shouldPauseRendering() {
        return false;
    }

    /**
     * Should return false if the game should be paused
     * @returns {boolean}
     */
    shouldPauseGame() {
        return false;
    }

    /**
     * Should return true if this overlay is open and currently blocking any user interaction
     */
    isBlockingOverlay() {
        return false;
    }

    /**
     * Cleans up the hud element, if overridden make sure to call super.cleanup
     */
    cleanup() {
        this.cleanupClickDetectors();
    }

    /**
     * Cleans up all click detectors
     */
    cleanupClickDetectors() {
        if (this.clickDetectors) {
            for (let i = 0; i < this.clickDetectors.length; ++i) {
                this.clickDetectors[i].cleanup();
            }
            this.clickDetectors = [];
        }
    }

    /**
     * Should close the element, in case its supported
     */
    close() {}

    // Helpers

    /**
     * Helper method to construct a new click detector
     * @param {Element} element The element to listen on
     * @param {function} handler The handler to call on this object
     * @param {import("../../core/click_detector").ClickDetectorConstructorArgs=} args Click detector arguments
     *
     */
    trackClicks(element, handler, args = {}) {
        const detector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](element, args);
        detector.click.add(handler, this);
        this.registerClickDetector(detector);
    }

    /**
     * Registers a new click detector
     * @param {ClickDetector} detector
     */
    registerClickDetector(detector) {
        this.clickDetectors.push(detector);
        if (false) {}
    }

    /**
     * Closes this element when its background is clicked
     * @param {HTMLElement} element
     * @param {function} closeMethod
     */
    closeOnBackgroundClick(element, closeMethod = null) {
        const bgClickDetector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](element, {
            preventDefault: true,
            targetOnly: true,
            applyCssClass: null,
            consumeEvents: true,
            clickSound: null,
        });

        // If the state defines a close method, use that as fallback
        // @ts-ignore
        bgClickDetector.touchend.add(closeMethod || this.close, this);
        this.registerClickDetector(bgClickDetector);
    }

    /**
     * Forwards the game speed keybindings so you can toggle pause / Fastforward
     * in the building tooltip and such
     * @param {KeyActionMapper} sourceMapper
     */
    forwardGameSpeedKeybindings(sourceMapper) {
        sourceMapper.forward(this.root.keyMapper, ["gamespeed_pause", "gamespeed_fastforward"]);
    }

    /**
     * Forwards the map movement keybindings so you can move the map with the
     * arrow keys
     * @param {KeyActionMapper} sourceMapper
     */
    forwardMapMovementKeybindings(sourceMapper) {
        sourceMapper.forward(this.root.keyMapper, [
            "mapMoveUp",
            "mapMoveRight",
            "mapMoveDown",
            "mapMoveLeft",
        ]);
    }
}


/***/ }),

/***/ "./src/js/game/hud/dynamic_dom_attach.js":
/*!***********************************************!*\
  !*** ./src/js/game/hud/dynamic_dom_attach.js ***!
  \***********************************************/
/*! exports provided: DynamicDomAttach */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DynamicDomAttach", function() { return DynamicDomAttach; });
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");



// Automatically attaches and detaches elements from the dom
// Also supports detaching elements after a given time, useful if there is a
// hide animation like for the tooltips

// Also attaches a class name if desired

class DynamicDomAttach {
    /**
     *
     * @param {GameRoot} root
     * @param {HTMLElement} element
     * @param {object} param2
     * @param {number=} param2.timeToKeepSeconds How long to keep the element visible (in ms) after it should be hidden.
     *                                           Useful for fade-out effects
     * @param {string=} param2.attachClass If set, attaches a class while the element is visible
     * @param {boolean=} param2.trackHover If set, attaches the 'hovered' class if the cursor is above the element. Useful
     *                                     for fading out the element if its below the cursor for example.
     */
    constructor(root, element, { timeToKeepSeconds = 0, attachClass = null, trackHover = false } = {}) {
        /** @type {GameRoot} */
        this.root = root;

        /** @type {HTMLElement} */
        this.element = element;
        this.parent = this.element.parentElement;
        window.assert(this.parent, "Dom attach created without parent");

        this.attachClass = attachClass;
        this.trackHover = trackHover;

        this.timeToKeepSeconds = timeToKeepSeconds;
        this.lastVisibleTime = 0;

        // We start attached, so detach the node first
        this.attached = true;
        this.internalDetach();

        this.internalIsClassAttached = false;
        this.classAttachTimeout = null;

        // Store the last bounds we computed
        /** @type {DOMRect} */
        this.lastComputedBounds = null;
        this.lastComputedBoundsTime = -1;

        // Track the 'hovered' class
        this.trackedIsHovered = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_0__["TrackedState"](this.setIsHoveredClass, this);
    }

    /**
     * Internal method to attach the element
     */
    internalAttach() {
        if (!this.attached) {
            this.parent.appendChild(this.element);
            window.assert(this.element.parentElement === this.parent, "Invalid parent #1");
            this.attached = true;
        }
    }

    /**
     * Internal method to detach the element
     */
    internalDetach() {
        if (this.attached) {
            window.assert(this.element.parentElement === this.parent, "Invalid parent #2");
            this.element.parentElement.removeChild(this.element);
            this.attached = false;
        }
    }

    /**
     * Returns whether the element is currently attached
     */
    isAttached() {
        return this.attached;
    }

    /**
     * Actually sets the 'hovered' class
     * @param {boolean} isHovered
     */
    setIsHoveredClass(isHovered) {
        this.element.classList.toggle("hovered", isHovered);
    }

    /**
     * Call this every frame, and the dom attach class will take care of
     * everything else
     * @param {boolean} isVisible Whether the element should currently be visible or not
     */
    update(isVisible) {
        if (isVisible) {
            this.lastVisibleTime = this.root ? this.root.time.realtimeNow() : 0;
            this.internalAttach();

            if (this.trackHover && this.root) {
                let bounds = this.lastComputedBounds;

                // Recompute bounds only once in a while
                if (!bounds || this.root.time.realtimeNow() - this.lastComputedBoundsTime > 1.0) {
                    bounds = this.lastComputedBounds = this.element.getBoundingClientRect();
                    this.lastComputedBoundsTime = this.root.time.realtimeNow();
                }

                const mousePos = this.root.app.mousePosition;
                if (mousePos) {
                    this.trackedIsHovered.set(
                        mousePos.x > bounds.left &&
                            mousePos.x < bounds.right &&
                            mousePos.y > bounds.top &&
                            mousePos.y < bounds.bottom
                    );
                }
            }
        } else {
            if (!this.root || this.root.time.realtimeNow() - this.lastVisibleTime >= this.timeToKeepSeconds) {
                this.internalDetach();
            }
        }

        if (this.attachClass && isVisible !== this.internalIsClassAttached) {
            // State changed
            this.internalIsClassAttached = isVisible;

            if (this.classAttachTimeout) {
                clearTimeout(this.classAttachTimeout);
                this.classAttachTimeout = null;
            }

            if (isVisible) {
                this.classAttachTimeout = setTimeout(() => {
                    this.element.classList.add(this.attachClass);
                }, 15);
            } else {
                this.element.classList.remove(this.attachClass);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/hud.js":
/*!********************************!*\
  !*** ./src/js/game/hud/hud.js ***!
  \********************************/
/*! exports provided: GameHUD */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameHUD", function() { return GameHUD; });
/* harmony import */ var _trailer_maker__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./trailer_maker */ "./src/js/game/hud/trailer_maker.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _parts_buildings_toolbar__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./parts/buildings_toolbar */ "./src/js/game/hud/parts/buildings_toolbar.js");
/* harmony import */ var _parts_building_placer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./parts/building_placer */ "./src/js/game/hud/parts/building_placer.js");
/* harmony import */ var _parts_blueprint_placer__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./parts/blueprint_placer */ "./src/js/game/hud/parts/blueprint_placer.js");
/* harmony import */ var _parts_keybinding_overlay__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./parts/keybinding_overlay */ "./src/js/game/hud/parts/keybinding_overlay.js");
/* harmony import */ var _parts_unlock_notification__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./parts/unlock_notification */ "./src/js/game/hud/parts/unlock_notification.js");
/* harmony import */ var _parts_game_menu__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./parts/game_menu */ "./src/js/game/hud/parts/game_menu.js");
/* harmony import */ var _parts_shop__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./parts/shop */ "./src/js/game/hud/parts/shop.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _parts_mass_selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./parts/mass_selector */ "./src/js/game/hud/parts/mass_selector.js");
/* harmony import */ var _parts_vignette_overlay__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./parts/vignette_overlay */ "./src/js/game/hud/parts/vignette_overlay.js");
/* harmony import */ var _parts_statistics__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./parts/statistics */ "./src/js/game/hud/parts/statistics.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _parts_pinned_shapes__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./parts/pinned_shapes */ "./src/js/game/hud/parts/pinned_shapes.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _parts_notifications__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./parts/notifications */ "./src/js/game/hud/parts/notifications.js");
/* harmony import */ var _parts_settings_menu__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./parts/settings_menu */ "./src/js/game/hud/parts/settings_menu.js");
/* harmony import */ var _parts_debug_info__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./parts/debug_info */ "./src/js/game/hud/parts/debug_info.js");
/* harmony import */ var _parts_entity_debugger__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./parts/entity_debugger */ "./src/js/game/hud/parts/entity_debugger.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _parts_watermark__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./parts/watermark */ "./src/js/game/hud/parts/watermark.js");
/* harmony import */ var _parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./parts/modal_dialogs */ "./src/js/game/hud/parts/modal_dialogs.js");
/* harmony import */ var _parts_tutorial_hints__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./parts/tutorial_hints */ "./src/js/game/hud/parts/tutorial_hints.js");
/* harmony import */ var _parts_waypoints__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./parts/waypoints */ "./src/js/game/hud/parts/waypoints.js");
/* harmony import */ var _parts_interactive_tutorial__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./parts/interactive_tutorial */ "./src/js/game/hud/parts/interactive_tutorial.js");
/* harmony import */ var _parts_screenshot_exporter__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./parts/screenshot_exporter */ "./src/js/game/hud/parts/screenshot_exporter.js");
/* harmony import */ var _parts_color_blind_helper__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./parts/color_blind_helper */ "./src/js/game/hud/parts/color_blind_helper.js");
/* harmony import */ var _parts_shape_viewer__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./parts/shape_viewer */ "./src/js/game/hud/parts/shape_viewer.js");
/* harmony import */ var _parts_wires_overlay__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./parts/wires_overlay */ "./src/js/game/hud/parts/wires_overlay.js");
/* harmony import */ var _parts_debug_changes__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./parts/debug_changes */ "./src/js/game/hud/parts/debug_changes.js");
/* harmony import */ var _core_query_parameters__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ../../core/query_parameters */ "./src/js/core/query_parameters.js");
/* harmony import */ var _parts_sandbox_controller__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./parts/sandbox_controller */ "./src/js/game/hud/parts/sandbox_controller.js");
/* harmony import */ var _parts_wires_toolbar__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! ./parts/wires_toolbar */ "./src/js/game/hud/parts/wires_toolbar.js");
/* harmony import */ var _parts_wire_info__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! ./parts/wire_info */ "./src/js/game/hud/parts/wire_info.js");
/* harmony import */ var _parts_lever_toggle__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! ./parts/lever_toggle */ "./src/js/game/hud/parts/lever_toggle.js");
/* harmony import */ var _parts_layer_preview__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! ./parts/layer_preview */ "./src/js/game/hud/parts/layer_preview.js");
/* harmony import */ var _parts_miner_highlight__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! ./parts/miner_highlight */ "./src/js/game/hud/parts/miner_highlight.js");
/* harmony import */ var _parts_beta_overlay__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! ./parts/beta_overlay */ "./src/js/game/hud/parts/beta_overlay.js");
/* harmony import */ var _parts_standalone_advantages__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! ./parts/standalone_advantages */ "./src/js/game/hud/parts/standalone_advantages.js");
/* harmony import */ var _parts_cat_memes__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! ./parts/cat_memes */ "./src/js/game/hud/parts/cat_memes.js");
/* harmony import */ var _parts_tutorial_video_offer__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! ./parts/tutorial_video_offer */ "./src/js/game/hud/parts/tutorial_video_offer.js");
/* harmony import */ var _parts_constant_signal_edit__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! ./parts/constant_signal_edit */ "./src/js/game/hud/parts/constant_signal_edit.js");


/* dev:start */

/* dev:end */













































class GameHUD {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;
    }

    /**
     * Initializes the hud parts
     */
    initialize() {
        this.signals = {
            buildingSelectedForPlacement: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            buildingsSelectedForCopy: /** @type {TypedSignal<[Array<number>]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
            unlockNotificationFinished: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_1__["Signal"]()),
        };

        this.parts = {
            buildingsToolbar: new _parts_buildings_toolbar__WEBPACK_IMPORTED_MODULE_3__["HUDBuildingsToolbar"](this.root),
            wiresToolbar: new _parts_wires_toolbar__WEBPACK_IMPORTED_MODULE_34__["HUDWiresToolbar"](this.root),
            blueprintPlacer: new _parts_blueprint_placer__WEBPACK_IMPORTED_MODULE_5__["HUDBlueprintPlacer"](this.root),
            buildingPlacer: new _parts_building_placer__WEBPACK_IMPORTED_MODULE_4__["HUDBuildingPlacer"](this.root),
            unlockNotification: new _parts_unlock_notification__WEBPACK_IMPORTED_MODULE_7__["HUDUnlockNotification"](this.root),
            gameMenu: new _parts_game_menu__WEBPACK_IMPORTED_MODULE_8__["HUDGameMenu"](this.root),
            massSelector: new _parts_mass_selector__WEBPACK_IMPORTED_MODULE_11__["HUDMassSelector"](this.root),
            shop: new _parts_shop__WEBPACK_IMPORTED_MODULE_9__["HUDShop"](this.root),
            statistics: new _parts_statistics__WEBPACK_IMPORTED_MODULE_13__["HUDStatistics"](this.root),
            waypoints: new _parts_waypoints__WEBPACK_IMPORTED_MODULE_25__["HUDWaypoints"](this.root),
            wireInfo: new _parts_wire_info__WEBPACK_IMPORTED_MODULE_35__["HUDWireInfo"](this.root),
            leverToggle: new _parts_lever_toggle__WEBPACK_IMPORTED_MODULE_36__["HUDLeverToggle"](this.root),
            constantSignalEdit: new _parts_constant_signal_edit__WEBPACK_IMPORTED_MODULE_43__["HUDConstantSignalEdit"](this.root),

            // Must always exist
            pinnedShapes: new _parts_pinned_shapes__WEBPACK_IMPORTED_MODULE_15__["HUDPinnedShapes"](this.root),
            notifications: new _parts_notifications__WEBPACK_IMPORTED_MODULE_17__["HUDNotifications"](this.root),
            settingsMenu: new _parts_settings_menu__WEBPACK_IMPORTED_MODULE_18__["HUDSettingsMenu"](this.root),
            debugInfo: new _parts_debug_info__WEBPACK_IMPORTED_MODULE_19__["HUDDebugInfo"](this.root),
            dialogs: new _parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_23__["HUDModalDialogs"](this.root),
            screenshotExporter: new _parts_screenshot_exporter__WEBPACK_IMPORTED_MODULE_27__["HUDScreenshotExporter"](this.root),
            shapeViewer: new _parts_shape_viewer__WEBPACK_IMPORTED_MODULE_29__["HUDShapeViewer"](this.root),

            wiresOverlay: new _parts_wires_overlay__WEBPACK_IMPORTED_MODULE_30__["HUDWiresOverlay"](this.root),
            layerPreview: new _parts_layer_preview__WEBPACK_IMPORTED_MODULE_37__["HUDLayerPreview"](this.root),

            minerHighlight: new _parts_miner_highlight__WEBPACK_IMPORTED_MODULE_38__["HUDMinerHighlight"](this.root),
            tutorialVideoOffer: new _parts_tutorial_video_offer__WEBPACK_IMPORTED_MODULE_42__["HUDTutorialVideoOffer"](this.root),

            // Typing hints

        };

        if (!_core_config__WEBPACK_IMPORTED_MODULE_10__["IS_MOBILE"]) {
            this.parts.keybindingOverlay = new _parts_keybinding_overlay__WEBPACK_IMPORTED_MODULE_6__["HUDKeybindingOverlay"](this.root);
        }

        if (false) {}

        if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
            this.parts.watermark = new _parts_watermark__WEBPACK_IMPORTED_MODULE_22__["HUDWatermark"](this.root);
            this.parts.standaloneAdvantages = new _parts_standalone_advantages__WEBPACK_IMPORTED_MODULE_40__["HUDStandaloneAdvantages"](this.root);
            this.parts.catMemes = new _parts_cat_memes__WEBPACK_IMPORTED_MODULE_41__["HUDCatMemes"](this.root);
        }

        if (false) {}

        if (this.root.app.settings.getAllSettings().offerHints) {
            this.parts.tutorialHints = new _parts_tutorial_hints__WEBPACK_IMPORTED_MODULE_24__["HUDPartTutorialHints"](this.root);
            this.parts.interactiveTutorial = new _parts_interactive_tutorial__WEBPACK_IMPORTED_MODULE_26__["HUDInteractiveTutorial"](this.root);
        }

        if (this.root.app.settings.getAllSettings().vignette) {
            this.parts.vignetteOverlay = new _parts_vignette_overlay__WEBPACK_IMPORTED_MODULE_12__["HUDVignetteOverlay"](this.root);
        }

        if (this.root.app.settings.getAllSettings().enableColorBlindHelper) {
            this.parts.colorBlindHelper = new _parts_color_blind_helper__WEBPACK_IMPORTED_MODULE_28__["HUDColorBlindHelper"](this.root);
        }

        if (_core_query_parameters__WEBPACK_IMPORTED_MODULE_32__["queryParamOptions"].sandboxMode || false) {
            this.parts.sandboxController = new _parts_sandbox_controller__WEBPACK_IMPORTED_MODULE_33__["HUDSandboxController"](this.root);
        }

        if (false) {}

        const frag = document.createDocumentFragment();
        for (const key in this.parts) {
            this.parts[key].createElements(frag);
        }

        document.body.appendChild(frag);

        for (const key in this.parts) {
            this.parts[key].initialize();
        }

        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_21__["KEYMAPPINGS"].ingame.toggleHud).add(this.toggleUi, this);

        /* dev:start */
        if (false) {}
        /* dev:end*/
    }

    /**
     * Attempts to close all overlays
     */
    closeAllOverlays() {
        for (const key in this.parts) {
            this.parts[key].close();
        }
    }

    /**
     * Returns true if the game logic should be paused
     */
    shouldPauseGame() {
        for (const key in this.parts) {
            if (this.parts[key].shouldPauseGame()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns true if the rendering can be paused
     */
    shouldPauseRendering() {
        for (const key in this.parts) {
            if (this.parts[key].shouldPauseRendering()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns true if the rendering can be paused
     */
    hasBlockingOverlayOpen() {
        for (const key in this.parts) {
            if (this.parts[key].isBlockingOverlay()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Toggles the ui
     */
    toggleUi() {
        document.body.classList.toggle("uiHidden");
    }

    /**
     * Updates all parts
     */
    update() {
        if (!this.root.gameInitialized) {
            return;
        }

        for (const key in this.parts) {
            this.parts[key].update();
        }

        /* dev:start */
        if (this.trailerMaker) {
            this.trailerMaker.update();
        }
        /* dev:end*/
    }

    /**
     * Draws all parts
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        const partsOrder = [
            "massSelector",
            "buildingPlacer",
            "blueprintPlacer",
            "colorBlindHelper",
            "changesDebugger",
            "minerHighlight",
        ];

        for (let i = 0; i < partsOrder.length; ++i) {
            if (this.parts[partsOrder[i]]) {
                this.parts[partsOrder[i]].draw(parameters);
            }
        }
    }

    /**
     * Draws all part overlays
     * @param {DrawParameters} parameters
     */
    drawOverlays(parameters) {
        const partsOrder = ["waypoints", "watermark", "wireInfo"];

        for (let i = 0; i < partsOrder.length; ++i) {
            if (this.parts[partsOrder[i]]) {
                this.parts[partsOrder[i]].drawOverlays(parameters);
            }
        }
    }

    /**
     * Cleans up everything
     */
    cleanup() {
        for (const key in this.parts) {
            this.parts[key].cleanup();
        }

        for (const key in this.signals) {
            this.signals[key].removeAll();
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/base_toolbar.js":
/*!***********************************************!*\
  !*** ./src/js/game/hud/parts/base_toolbar.js ***!
  \***********************************************/
/*! exports provided: HUDBaseToolbar */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDBaseToolbar", function() { return HUDBaseToolbar; });
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../root */ "./src/js/game/root.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");









class HUDBaseToolbar extends _base_hud_part__WEBPACK_IMPORTED_MODULE_6__["BaseHUDPart"] {
    /**
     * @param {GameRoot} root
     * @param {object} param0
     * @param {Array<typeof MetaBuilding>} param0.primaryBuildings
     * @param {Array<typeof MetaBuilding>=} param0.secondaryBuildings
     * @param {function} param0.visibilityCondition
     * @param {string} param0.htmlElementId
     * @param {Layer=} param0.layer
     */
    constructor(
        root,
        { primaryBuildings, secondaryBuildings = [], visibilityCondition, htmlElementId, layer = "regular" }
    ) {
        super(root);

        this.primaryBuildings = primaryBuildings;
        this.secondaryBuildings = secondaryBuildings;
        this.visibilityCondition = visibilityCondition;
        this.htmlElementId = htmlElementId;
        this.layer = layer;

        /** @type {Object.<string, {
         * metaBuilding: MetaBuilding,
         * unlocked: boolean,
         * selected: boolean,
         * element: HTMLElement,
         * index: number
         * }>} */
        this.buildingHandles = {};
    }

    /**
     * Should create all require elements
     * @param {HTMLElement} parent
     */
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(parent, this.htmlElementId, ["ingame_buildingsToolbar"], "");
    }

    /**
     * Returns all buildings
     * @returns {Array<typeof MetaBuilding>}
     */
    get allBuildings() {
        return [...this.primaryBuildings, ...this.secondaryBuildings];
    }

    initialize() {
        const actionMapper = this.root.keyMapper;
        let rowSecondary;
        if (this.secondaryBuildings.length > 0) {
            rowSecondary = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.element, null, ["buildings", "secondary"]);

            this.secondaryDomAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_7__["DynamicDomAttach"](this.root, rowSecondary, {
                attachClass: "visible",
            });
        }

        const rowPrimary = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.element, null, ["buildings", "primary"]);

        const allBuildings = this.allBuildings;

        for (let i = 0; i < allBuildings.length; ++i) {
            const metaBuilding = _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].findByClass(allBuildings[i]);

            let rawBinding = _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].buildings[metaBuilding.getId() + "_" + this.layer];
            if (!rawBinding) {
                rawBinding = _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].buildings[metaBuilding.getId()];
            }

            const binding = actionMapper.getBinding(rawBinding);

            const itemContainer = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(
                this.primaryBuildings.includes(allBuildings[i]) ? rowPrimary : rowSecondary,
                null,
                ["building"]
            );
            itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png");
            itemContainer.setAttribute("data-id", metaBuilding.getId());

            binding.add(() => this.selectBuildingForPlacement(metaBuilding));

            this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), {
                clickSound: null,
            });

            this.buildingHandles[metaBuilding.id] = {
                metaBuilding,
                element: itemContainer,
                unlocked: false,
                selected: false,
                index: i,
            };
        }

        this.root.hud.signals.selectedPlacementBuildingChanged.add(
            this.onSelectedPlacementBuildingChanged,
            this
        );

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_7__["DynamicDomAttach"](this.root, this.element, {
            timeToKeepSeconds: 0.12,
            attachClass: "visible",
        });
        this.lastSelectedIndex = 0;
        actionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].placement.cycleBuildings).add(this.cycleBuildings, this);
    }

    /**
     * Updates the toolbar
     */
    update() {
        const visible = this.visibilityCondition();
        this.domAttach.update(visible);

        if (visible) {
            let recomputeSecondaryToolbarVisibility = false;
            for (const buildingId in this.buildingHandles) {
                const handle = this.buildingHandles[buildingId];
                const newStatus = handle.metaBuilding.getIsUnlocked(this.root);
                if (handle.unlocked !== newStatus) {
                    handle.unlocked = newStatus;
                    handle.element.classList.toggle("unlocked", newStatus);
                    recomputeSecondaryToolbarVisibility = true;
                }
            }

            if (recomputeSecondaryToolbarVisibility && this.secondaryDomAttach) {
                let anyUnlocked = false;
                for (let i = 0; i < this.secondaryBuildings.length; ++i) {
                    const metaClass = _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].findByClass(this.secondaryBuildings[i]);
                    if (metaClass.getIsUnlocked(this.root)) {
                        anyUnlocked = true;
                        break;
                    }
                }

                this.secondaryDomAttach.update(anyUnlocked);
            }
        }
    }

    /**
     * Cycles through all buildings
     */
    cycleBuildings() {
        const visible = this.visibilityCondition();
        if (!visible) {
            return;
        }

        let newBuildingFound = false;
        let newIndex = this.lastSelectedIndex;
        const direction = this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].placement.rotateInverseModifier).pressed
            ? -1
            : 1;

        for (let i = 0; i <= this.primaryBuildings.length; ++i) {
            newIndex = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["safeModulo"])(newIndex + direction, this.primaryBuildings.length);
            const metaBuilding = _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].findByClass(this.primaryBuildings[newIndex]);
            const handle = this.buildingHandles[metaBuilding.id];
            if (!handle.selected && handle.unlocked) {
                newBuildingFound = true;
                break;
            }
        }
        if (!newBuildingFound) {
            return;
        }
        const metaBuildingClass = this.primaryBuildings[newIndex];
        const metaBuilding = _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].findByClass(metaBuildingClass);
        this.selectBuildingForPlacement(metaBuilding);
    }

    /**
     * Called when the selected building got changed
     * @param {MetaBuilding} metaBuilding
     */
    onSelectedPlacementBuildingChanged(metaBuilding) {
        for (const buildingId in this.buildingHandles) {
            const handle = this.buildingHandles[buildingId];
            const newStatus = handle.metaBuilding === metaBuilding;
            if (handle.selected !== newStatus) {
                handle.selected = newStatus;
                handle.element.classList.toggle("selected", newStatus);
            }
            if (handle.selected) {
                this.lastSelectedIndex = handle.index;
            }
        }

        this.element.classList.toggle("buildingSelected", !!metaBuilding);
    }

    /**
     * @param {MetaBuilding} metaBuilding
     */
    selectBuildingForPlacement(metaBuilding) {
        if (!this.visibilityCondition()) {
            // Not active
            return;
        }

        if (!metaBuilding.getIsUnlocked(this.root)) {
            this.root.soundProxy.playUiError();
            return _core_signal__WEBPACK_IMPORTED_MODULE_1__["STOP_PROPAGATION"];
        }

        // Allow clicking an item again to deselect it
        for (const buildingId in this.buildingHandles) {
            const handle = this.buildingHandles[buildingId];
            if (handle.selected && handle.metaBuilding === metaBuilding) {
                metaBuilding = null;
                break;
            }
        }

        this.root.soundProxy.playUiClick();
        this.root.hud.signals.buildingSelectedForPlacement.dispatch(metaBuilding);
        this.onSelectedPlacementBuildingChanged(metaBuilding);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/beta_overlay.js":
/*!***********************************************!*\
  !*** ./src/js/game/hud/parts/beta_overlay.js ***!
  \***********************************************/
/*! exports provided: HUDBetaOverlay */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDBetaOverlay", function() { return HUDBetaOverlay; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");



class HUDBetaOverlay extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
            parent,
            "ingame_HUD_BetaOverlay",
            [],
            "<h2>UNSTABLE BETA VERSION</h2><span>Unfinalized & potential buggy content!</span>"
        );
    }

    initialize() {}
}


/***/ }),

/***/ "./src/js/game/hud/parts/blueprint_placer.js":
/*!***************************************************!*\
  !*** ./src/js/game/hud/parts/blueprint_placer.js ***!
  \***************************************************/
/*! exports provided: HUDBlueprintPlacer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDBlueprintPlacer", function() { return HUDBlueprintPlacer; });
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _blueprint__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../blueprint */ "./src/js/game/blueprint.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../camera */ "./src/js/game/camera.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");













class HUDBlueprintPlacer extends _base_hud_part__WEBPACK_IMPORTED_MODULE_10__["BaseHUDPart"] {
    createElements(parent) {
        const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(
            this.root.gameMode.getBlueprintShapeKey()
        );
        const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);

        this.costDisplayParent = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(parent, "ingame_HUD_BlueprintPlacer", [], ``);

        Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(this.costDisplayParent, null, ["label"], _translations__WEBPACK_IMPORTED_MODULE_6__["T"].ingame.blueprintPlacer.cost);
        const costContainer = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(this.costDisplayParent, null, ["costContainer"], "");
        this.costDisplayText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(costContainer, null, ["costText"], "");
        costContainer.appendChild(blueprintCostShapeCanvas);
    }

    initialize() {
        this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);

        /** @type {TypedTrackedState<Blueprint?>} */
        this.currentBlueprint = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_2__["TrackedState"](this.onBlueprintChanged, this);
        /** @type {Blueprint?} */
        this.lastBlueprintUsed = null;

        const keyActionMapper = this.root.keyMapper;
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_9__["KEYMAPPINGS"].general.back).add(this.abortPlacement, this);
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_9__["KEYMAPPINGS"].placement.pipette).add(this.abortPlacement, this);
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_9__["KEYMAPPINGS"].placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_9__["KEYMAPPINGS"].massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);

        this.root.camera.downPreHandler.add(this.onMouseDown, this);
        this.root.camera.movePreHandler.add(this.onMouseMove, this);

        this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
        this.root.signals.editModeChanged.add(this.onEditModeChanged, this);

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_11__["DynamicDomAttach"](this.root, this.costDisplayParent);
        this.trackedCanAfford = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_2__["TrackedState"](this.onCanAffordChanged, this);
    }

    abortPlacement() {
        if (this.currentBlueprint.get()) {
            this.currentBlueprint.set(null);

            return _core_signal__WEBPACK_IMPORTED_MODULE_1__["STOP_PROPAGATION"];
        }
    }

    /**
     * Called when the layer was changed
     * @param {Layer} layer
     */
    onEditModeChanged(layer) {
        // Check if the layer of the blueprint differs and thus we have to deselect it
        const blueprint = this.currentBlueprint.get();
        if (blueprint) {
            if (blueprint.layer !== layer) {
                this.currentBlueprint.set(null);
            }
        }
    }

    /**
     * Called when the blueprint is now affordable or not
     * @param {boolean} canAfford
     */
    onCanAffordChanged(canAfford) {
        this.costDisplayParent.classList.toggle("canAfford", canAfford);
    }

    update() {
        const currentBlueprint = this.currentBlueprint.get();
        this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
        this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
    }

    /**
     * Called when the blueprint was changed
     * @param {Blueprint} blueprint
     */
    onBlueprintChanged(blueprint) {
        if (blueprint) {
            this.lastBlueprintUsed = blueprint;
            this.costDisplayText.innerText = "" + blueprint.getCost();
        }
    }

    /**
     * mouse down pre handler
     * @param {Vector} pos
     * @param {enumMouseButton} button
     */
    onMouseDown(pos, button) {
        if (button === _camera__WEBPACK_IMPORTED_MODULE_8__["enumMouseButton"].right) {
            if (this.currentBlueprint.get()) {
                this.abortPlacement();
                return _core_signal__WEBPACK_IMPORTED_MODULE_1__["STOP_PROPAGATION"];
            }
        } else if (button === _camera__WEBPACK_IMPORTED_MODULE_8__["enumMouseButton"].left) {
            const blueprint = this.currentBlueprint.get();
            if (!blueprint) {
                return;
            }

            if (!blueprint.canAfford(this.root)) {
                this.root.soundProxy.playUiError();
                return;
            }

            const worldPos = this.root.camera.screenToWorld(pos);
            const tile = worldPos.toTileSpace();
            if (blueprint.tryPlace(this.root, tile)) {
                const cost = blueprint.getCost();
                this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
                this.root.soundProxy.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_5__["SOUNDS"].placeBuilding);
            }
        }
    }

    /**
     * Mose move handler
     */
    onMouseMove() {
        // Prevent movement while blueprint is selected
        if (this.currentBlueprint.get()) {
            return _core_signal__WEBPACK_IMPORTED_MODULE_1__["STOP_PROPAGATION"];
        }
    }

    /**
     * Called when an array of bulidings was selected
     * @param {Array<number>} uids
     */
    createBlueprintFromBuildings(uids) {
        if (uids.length === 0) {
            return;
        }
        this.currentBlueprint.set(_blueprint__WEBPACK_IMPORTED_MODULE_7__["Blueprint"].fromUids(this.root, uids));
    }

    /**
     * Attempts to rotate the current blueprint
     */
    rotateBlueprint() {
        if (this.currentBlueprint.get()) {
            if (this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_9__["KEYMAPPINGS"].placement.rotateInverseModifier).pressed) {
                this.currentBlueprint.get().rotateCcw();
            } else {
                this.currentBlueprint.get().rotateCw();
            }
        }
    }

    /**
     * Attempts to paste the last blueprint
     */
    pasteBlueprint() {
        if (this.lastBlueprintUsed !== null) {
            if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
                // Not compatible
                this.root.soundProxy.playUiError();
                return;
            }

            this.root.hud.signals.pasteBlueprintRequested.dispatch();
            this.currentBlueprint.set(this.lastBlueprintUsed);
        } else {
            this.root.soundProxy.playUiError();
        }
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        const blueprint = this.currentBlueprint.get();
        if (!blueprint) {
            return;
        }
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return;
        }

        const worldPos = this.root.camera.screenToWorld(mousePosition);
        const tile = worldPos.toTileSpace();
        blueprint.draw(parameters, tile);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/building_placer.js":
/*!**************************************************!*\
  !*** ./src/js/game/hud/parts/building_placer.js ***!
  \**************************************************/
/*! exports provided: HUDBuildingPlacer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDBuildingPlacer", function() { return HUDBuildingPlacer; });
/* harmony import */ var _core_click_detector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/draw_utils */ "./src/js/core/draw_utils.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../theme */ "./src/js/game/theme.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _building_placer_logic__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./building_placer_logic */ "./src/js/game/hud/parts/building_placer_logic.js");
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../../root */ "./src/js/game/root.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../../building_codes */ "./src/js/game/building_codes.js");

















class HUDBuildingPlacer extends _building_placer_logic__WEBPACK_IMPORTED_MODULE_12__["HUDBuildingPlacerLogic"] {
    /**
     * @param {HTMLElement} parent
     */
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(parent, "ingame_HUD_PlacementHints", [], ``);

        this.buildingInfoElements = {};
        this.buildingInfoElements.label = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(this.element, null, ["buildingLabel"], "Extract");
        this.buildingInfoElements.desc = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(this.element, null, ["description"], "");
        this.buildingInfoElements.descText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(this.buildingInfoElements.desc, null, ["text"], "");
        this.buildingInfoElements.additionalInfo = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(
            this.buildingInfoElements.desc,
            null,
            ["additionalInfo"],
            ""
        );
        this.buildingInfoElements.hotkey = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(this.buildingInfoElements.desc, null, ["hotkey"], "");
        this.buildingInfoElements.tutorialImage = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(this.element, null, ["buildingImage"]);

        this.variantsElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(parent, "ingame_HUD_PlacerVariants");

        const compact = this.root.app.settings.getAllSettings().compactBuildingInfo;
        this.element.classList.toggle("compact", compact);
        this.variantsElement.classList.toggle("compact", compact);
    }

    initialize() {
        super.initialize();

        // Bind to signals
        this.signals.variantChanged.add(this.rerenderVariants, this);
        this.root.hud.signals.buildingSelectedForPlacement.add(this.startSelection, this);

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_11__["DynamicDomAttach"](this.root, this.element, { trackHover: true });
        this.variantsAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_11__["DynamicDomAttach"](this.root, this.variantsElement, {});

        this.currentInterpolatedCornerTile = new _core_vector__WEBPACK_IMPORTED_MODULE_6__["Vector"]();

        this.lockIndicatorSprites = {};
        _root__WEBPACK_IMPORTED_MODULE_14__["layers"].forEach(layer => {
            this.lockIndicatorSprites[layer] = this.makeLockIndicatorSprite(layer);
        });

        //

        /**
         * Stores the click detectors for the variants so we can clean them up later
         * @type {Array<ClickDetector>}
         */
        this.variantClickDetectors = [];
    }

    /**
     * Makes the lock indicator sprite for the given layer
     * @param {Layer} layer
     */
    makeLockIndicatorSprite(layer) {
        const dims = 48;
        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_13__["makeOffscreenBuffer"])(dims, dims, {
            smooth: true,
            reusable: false,
            label: "lock-direction-indicator",
        });

        context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_10__["THEME"].map.directionLock[layer].color;
        context.strokeStyle = _theme__WEBPACK_IMPORTED_MODULE_10__["THEME"].map.directionLock[layer].color;
        context.lineWidth = 2;

        const padding = 5;
        const height = dims * 0.5;
        const bottom = (dims + height) / 2;

        context.moveTo(padding, bottom);
        context.lineTo(dims / 2, bottom - height);
        context.lineTo(dims - padding, bottom);
        context.closePath();
        context.stroke();
        context.fill();

        return canvas;
    }

    /**
     * Rerenders the building info dialog
     */
    rerenderInfoDialog() {
        const metaBuilding = this.currentMetaBuilding.get();

        if (!metaBuilding) {
            return;
        }

        const variant = this.currentVariant.get();

        this.buildingInfoElements.label.innerHTML = _translations__WEBPACK_IMPORTED_MODULE_7__["T"].buildings[metaBuilding.id][variant].name;
        this.buildingInfoElements.descText.innerHTML = _translations__WEBPACK_IMPORTED_MODULE_7__["T"].buildings[metaBuilding.id][variant].description;

        const layer = this.root.currentLayer;

        let rawBinding = _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].buildings[metaBuilding.getId() + "_" + layer];
        if (!rawBinding) {
            rawBinding = _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].buildings[metaBuilding.getId()];
        }

        const binding = this.root.keyMapper.getBinding(rawBinding);

        this.buildingInfoElements.hotkey.innerHTML = _translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.buildingPlacement.hotkeyLabel.replace(
            "<key>",
            "<code class='keybinding'>" + binding.getKeyCodeString() + "</code>"
        );

        this.buildingInfoElements.tutorialImage.setAttribute(
            "data-icon",
            "building_tutorials/" +
                metaBuilding.getId() +
                (variant === _meta_building__WEBPACK_IMPORTED_MODULE_9__["defaultBuildingVariant"] ? "" : "-" + variant) +
                ".png"
        );

        Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["removeAllChildren"])(this.buildingInfoElements.additionalInfo);
        const additionalInfo = metaBuilding.getAdditionalStatistics(this.root, this.currentVariant.get());
        for (let i = 0; i < additionalInfo.length; ++i) {
            const [label, contents] = additionalInfo[i];
            this.buildingInfoElements.additionalInfo.innerHTML += `
                <label>${label}:</label>
                <span>${contents}</contents>
            `;
        }
    }

    cleanup() {
        super.cleanup();
        this.cleanupVariantClickDetectors();
    }

    /**
     * Cleans up all variant click detectors
     */
    cleanupVariantClickDetectors() {
        for (let i = 0; i < this.variantClickDetectors.length; ++i) {
            const detector = this.variantClickDetectors[i];
            detector.cleanup();
        }
        this.variantClickDetectors = [];
    }

    /**
     * Rerenders the variants displayed
     */
    rerenderVariants() {
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["removeAllChildren"])(this.variantsElement);
        this.rerenderInfoDialog();

        const metaBuilding = this.currentMetaBuilding.get();

        // First, clear up all click detectors
        this.cleanupVariantClickDetectors();

        if (!metaBuilding) {
            return;
        }
        const availableVariants = metaBuilding.getAvailableVariants(this.root);
        if (availableVariants.length === 1) {
            return;
        }

        Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(
            this.variantsElement,
            null,
            ["explanation"],
            _translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.buildingPlacement.cycleBuildingVariants.replace(
                "<key>",
                "<code class='keybinding'>" +
                    this.root.keyMapper
                        .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.cycleBuildingVariants)
                        .getKeyCodeString() +
                    "</code>"
            )
        );

        const container = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(this.variantsElement, null, ["variants"]);

        for (let i = 0; i < availableVariants.length; ++i) {
            const variant = availableVariants[i];

            const element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(container, null, ["variant"]);
            element.classList.toggle("active", variant === this.currentVariant.get());
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(element, null, ["label"], variant);

            const iconSize = 64;

            const dimensions = metaBuilding.getDimensions(variant);
            const sprite = metaBuilding.getPreviewSprite(0, variant);
            const spriteWrapper = Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["makeDiv"])(element, null, ["iconWrap"]);
            spriteWrapper.setAttribute("data-tile-w", dimensions.x);
            spriteWrapper.setAttribute("data-tile-h", dimensions.y);

            spriteWrapper.innerHTML = sprite.getAsHTML(iconSize * dimensions.x, iconSize * dimensions.y);

            const detector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](element, {
                consumeEvents: true,
                targetOnly: true,
            });
            detector.click.add(() => this.setVariant(variant));
        }
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        if (this.root.camera.zoomLevel < _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkOverviewMinZoom) {
            // Dont allow placing in overview mode
            this.domAttach.update(false);
            this.variantsAttach.update(false);
            return;
        }

        this.domAttach.update(!!this.currentMetaBuilding.get());
        this.variantsAttach.update(!!this.currentMetaBuilding.get());
        const metaBuilding = this.currentMetaBuilding.get();

        if (!metaBuilding) {
            return;
        }

        // Draw direction lock
        if (this.isDirectionLockActive) {
            this.drawDirectionLock(parameters);
        } else {
            this.drawRegularPlacement(parameters);
        }

        if (metaBuilding.getShowWiresLayerPreview()) {
            this.drawLayerPeek(parameters);
        }
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    drawLayerPeek(parameters) {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return;
        }

        const worldPosition = this.root.camera.screenToWorld(mousePosition);

        // Draw peeker
        this.root.hud.parts.layerPreview.renderPreview(
            parameters,
            worldPosition,
            1 / this.root.camera.zoomLevel
        );
    }

    /**
     * @param {DrawParameters} parameters
     */
    drawRegularPlacement(parameters) {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return;
        }

        const metaBuilding = this.currentMetaBuilding.get();

        const worldPos = this.root.camera.screenToWorld(mousePosition);
        const mouseTile = worldPos.toTileSpace();

        // Compute best rotation variant
        const {
            rotation,
            rotationVariant,
            connectedEntities,
        } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({
            root: this.root,
            tile: mouseTile,
            rotation: this.currentBaseRotation,
            variant: this.currentVariant.get(),
            layer: metaBuilding.getLayer(),
        });

        // Check if there are connected entities
        if (connectedEntities) {
            for (let i = 0; i < connectedEntities.length; ++i) {
                const connectedEntity = connectedEntities[i];
                const connectedWsPoint = connectedEntity.components.StaticMapEntity.getTileSpaceBounds()
                    .getCenter()
                    .toWorldSpace();

                const startWsPoint = mouseTile.toWorldSpaceCenterOfTile();

                const startOffset = connectedWsPoint
                    .sub(startWsPoint)
                    .normalize()
                    .multiplyScalar(_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize * 0.3);
                const effectiveStartPoint = startWsPoint.add(startOffset);
                const effectiveEndPoint = connectedWsPoint.sub(startOffset);

                parameters.context.globalAlpha = 0.6;

                // parameters.context.lineCap = "round";
                parameters.context.strokeStyle = "#7f7";
                parameters.context.lineWidth = 10;
                parameters.context.beginPath();
                parameters.context.moveTo(effectiveStartPoint.x, effectiveStartPoint.y);
                parameters.context.lineTo(effectiveEndPoint.x, effectiveEndPoint.y);
                parameters.context.stroke();
                parameters.context.globalAlpha = 1;
                // parameters.context.lineCap = "square";
            }
        }

        // Synchronize rotation and origin
        this.fakeEntity.layer = metaBuilding.getLayer();
        const staticComp = this.fakeEntity.components.StaticMapEntity;
        staticComp.origin = mouseTile;
        staticComp.rotation = rotation;
        metaBuilding.updateVariants(this.fakeEntity, rotationVariant, this.currentVariant.get());
        staticComp.code = Object(_building_codes__WEBPACK_IMPORTED_MODULE_15__["getCodeFromBuildingData"])(
            this.currentMetaBuilding.get(),
            this.currentVariant.get(),
            rotationVariant
        );

        const canBuild = this.root.logic.checkCanPlaceEntity(this.fakeEntity);

        // Fade in / out
        parameters.context.lineWidth = 1;

        // Determine the bounds and visualize them
        const entityBounds = staticComp.getTileSpaceBounds();
        const drawBorder = -3;
        if (canBuild) {
            parameters.context.strokeStyle = "rgba(56, 235, 111, 0.5)";
            parameters.context.fillStyle = "rgba(56, 235, 111, 0.2)";
        } else {
            parameters.context.strokeStyle = "rgba(255, 0, 0, 0.2)";
            parameters.context.fillStyle = "rgba(255, 0, 0, 0.2)";
        }

        parameters.context.beginRoundedRect(
            entityBounds.x * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize - drawBorder,
            entityBounds.y * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize - drawBorder,
            entityBounds.w * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize + 2 * drawBorder,
            entityBounds.h * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize + 2 * drawBorder,
            4
        );
        parameters.context.stroke();
        // parameters.context.fill();
        parameters.context.globalAlpha = 1;

        // HACK to draw the entity sprite
        const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get());
        staticComp.origin = worldPos.divideScalar(_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize).subScalars(0.5, 0.5);
        staticComp.drawSpriteOnBoundsClipped(parameters, previewSprite);
        staticComp.origin = mouseTile;

        // Draw ejectors
        if (canBuild) {
            this.drawMatchingAcceptorsAndEjectors(parameters);
        }
    }

    /**
     * @param {DrawParameters} parameters
     */
    drawDirectionLock(parameters) {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return;
        }

        const mouseWorld = this.root.camera.screenToWorld(mousePosition);
        const mouseTile = mouseWorld.toTileSpace();
        parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_10__["THEME"].map.directionLock[this.root.currentLayer].color;
        parameters.context.strokeStyle = _theme__WEBPACK_IMPORTED_MODULE_10__["THEME"].map.directionLock[this.root.currentLayer].background;
        parameters.context.lineWidth = 10;

        parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4);
        parameters.context.fill();

        if (this.lastDragTile) {
            const startLine = this.lastDragTile.toWorldSpaceCenterOfTile();
            const endLine = mouseTile.toWorldSpaceCenterOfTile();
            const midLine = this.currentDirectionLockCorner.toWorldSpaceCenterOfTile();

            parameters.context.beginCircle(startLine.x, startLine.y, 8);
            parameters.context.fill();

            parameters.context.beginPath();
            parameters.context.moveTo(startLine.x, startLine.y);
            parameters.context.lineTo(midLine.x, midLine.y);
            parameters.context.lineTo(endLine.x, endLine.y);
            parameters.context.stroke();

            parameters.context.beginCircle(endLine.x, endLine.y, 5);
            parameters.context.fill();

            // Draw arrow
            const arrowSprite = this.lockIndicatorSprites[this.root.currentLayer];
            const path = this.computeDirectionLockPath();
            for (let i = 0; i < path.length - 1; i += 1) {
                const { rotation, tile } = path[i];
                const worldPos = tile.toWorldSpaceCenterOfTile();
                const angle = Math.radians(rotation);

                parameters.context.translate(worldPos.x, worldPos.y);
                parameters.context.rotate(angle);
                parameters.context.drawImage(
                    arrowSprite,
                    -6,
                    -_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].halfTileSize -
                        Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["clamp"])((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * 1 * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize +
                        _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].halfTileSize -
                        6,
                    12,
                    12
                );
                parameters.context.rotate(-angle);
                parameters.context.translate(-worldPos.x, -worldPos.y);
            }
        }
    }

    /**
     * @param {DrawParameters} parameters
     */
    drawMatchingAcceptorsAndEjectors(parameters) {
        const acceptorComp = this.fakeEntity.components.ItemAcceptor;
        const ejectorComp = this.fakeEntity.components.ItemEjector;
        const staticComp = this.fakeEntity.components.StaticMapEntity;
        const beltComp = this.fakeEntity.components.Belt;
        const minerComp = this.fakeEntity.components.Miner;

        const goodArrowSprite = _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/misc/slot_good_arrow.png");
        const badArrowSprite = _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/misc/slot_bad_arrow.png");

        // Just ignore the following code please ... thanks!

        const offsetShift = 10;

        let acceptorSlots = [];
        let ejectorSlots = [];

        if (ejectorComp) {
            ejectorSlots = ejectorComp.slots.slice();
        }

        if (acceptorComp) {
            acceptorSlots = acceptorComp.slots.slice();
        }

        if (beltComp) {
            const fakeEjectorSlot = beltComp.getFakeEjectorSlot();
            const fakeAcceptorSlot = beltComp.getFakeAcceptorSlot();
            ejectorSlots.push(fakeEjectorSlot);
            acceptorSlots.push(fakeAcceptorSlot);
        }

        for (let acceptorSlotIndex = 0; acceptorSlotIndex < acceptorSlots.length; ++acceptorSlotIndex) {
            const slot = acceptorSlots[acceptorSlotIndex];

            const acceptorSlotWsTile = staticComp.localTileToWorld(slot.pos);
            const acceptorSlotWsPos = acceptorSlotWsTile.toWorldSpaceCenterOfTile();

            // Go over all slots
            for (
                let acceptorDirectionIndex = 0;
                acceptorDirectionIndex < slot.directions.length;
                ++acceptorDirectionIndex
            ) {
                const direction = slot.directions[acceptorDirectionIndex];
                const worldDirection = staticComp.localDirectionToWorld(direction);

                // Figure out which tile ejects to this slot
                const sourceTile = acceptorSlotWsTile.add(_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToVector"][worldDirection]);

                let isBlocked = false;
                let isConnected = false;

                // Find all entities which are on that tile
                const sourceEntities = this.root.map.getLayersContentsMultipleXY(sourceTile.x, sourceTile.y);

                // Check for every entity:
                for (let i = 0; i < sourceEntities.length; ++i) {
                    const sourceEntity = sourceEntities[i];
                    const sourceEjector = sourceEntity.components.ItemEjector;
                    const sourceBeltComp = sourceEntity.components.Belt;
                    const sourceStaticComp = sourceEntity.components.StaticMapEntity;
                    const ejectorAcceptLocalTile = sourceStaticComp.worldToLocalTile(acceptorSlotWsTile);

                    // If this entity is on the same layer as the slot - if so, it can either be
                    // connected, or it can not be connected and thus block the input
                    if (sourceEjector && sourceEjector.anySlotEjectsToLocalTile(ejectorAcceptLocalTile)) {
                        // This one is connected, all good
                        isConnected = true;
                    } else if (
                        sourceBeltComp &&
                        sourceStaticComp.localDirectionToWorld(sourceBeltComp.direction) ===
                            _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumInvertedDirections"][worldDirection]
                    ) {
                        // Belt connected
                        isConnected = true;
                    } else {
                        // This one is blocked
                        isBlocked = true;
                    }
                }

                const alpha = isConnected || isBlocked ? 1.0 : 0.3;
                const sprite = isBlocked ? badArrowSprite : goodArrowSprite;

                parameters.context.globalAlpha = alpha;
                Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_3__["drawRotatedSprite"])({
                    parameters,
                    sprite,
                    x: acceptorSlotWsPos.x,
                    y: acceptorSlotWsPos.y,
                    angle: Math.radians(_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToAngle"][_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumInvertedDirections"][worldDirection]]),
                    size: 13,
                    offsetY: offsetShift + 13,
                });
                parameters.context.globalAlpha = 1;
            }
        }

        // Go over all slots
        for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorSlots.length; ++ejectorSlotIndex) {
            const slot = ejectorSlots[ejectorSlotIndex];

            const ejectorSlotLocalTile = slot.pos.add(_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToVector"][slot.direction]);
            const ejectorSlotWsTile = staticComp.localTileToWorld(ejectorSlotLocalTile);

            const ejectorSLotWsPos = ejectorSlotWsTile.toWorldSpaceCenterOfTile();
            const ejectorSlotWsDirection = staticComp.localDirectionToWorld(slot.direction);

            let isBlocked = false;
            let isConnected = false;

            // Find all entities which are on that tile
            const destEntities = this.root.map.getLayersContentsMultipleXY(
                ejectorSlotWsTile.x,
                ejectorSlotWsTile.y
            );

            // Check for every entity:
            for (let i = 0; i < destEntities.length; ++i) {
                const destEntity = destEntities[i];
                const destAcceptor = destEntity.components.ItemAcceptor;
                const destStaticComp = destEntity.components.StaticMapEntity;
                const destMiner = destEntity.components.Miner;

                const destLocalTile = destStaticComp.worldToLocalTile(ejectorSlotWsTile);
                const destLocalDir = destStaticComp.worldDirectionToLocal(ejectorSlotWsDirection);
                if (destAcceptor && destAcceptor.findMatchingSlot(destLocalTile, destLocalDir)) {
                    // This one is connected, all good
                    isConnected = true;
                } else if (destEntity.components.Belt && destLocalDir === _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirection"].top) {
                    // Connected to a belt
                    isConnected = true;
                } else if (minerComp && minerComp.chainable && destMiner && destMiner.chainable) {
                    // Chainable miners connected to eachother
                    isConnected = true;
                } else {
                    // This one is blocked
                    isBlocked = true;
                }
            }

            const alpha = isConnected || isBlocked ? 1.0 : 0.3;
            const sprite = isBlocked ? badArrowSprite : goodArrowSprite;

            parameters.context.globalAlpha = alpha;
            Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_3__["drawRotatedSprite"])({
                parameters,
                sprite,
                x: ejectorSLotWsPos.x,
                y: ejectorSLotWsPos.y,
                angle: Math.radians(_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToAngle"][ejectorSlotWsDirection]),
                size: 13,
                offsetY: offsetShift,
            });
            parameters.context.globalAlpha = 1;
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/building_placer_logic.js":
/*!********************************************************!*\
  !*** ./src/js/game/hud/parts/building_placer_logic.js ***!
  \********************************************************/
/*! exports provided: HUDBuildingPlacerLogic */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDBuildingPlacerLogic", function() { return HUDBuildingPlacerLogic; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../camera */ "./src/js/game/camera.js");
/* harmony import */ var _components_static_map_entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../components/static_map_entity */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../entity */ "./src/js/game/entity.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _buildings_miner__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../../buildings/miner */ "./src/js/game/buildings/miner.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../../building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _buildings_hub__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../../buildings/hub */ "./src/js/game/buildings/hub.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");


















/**
 * Contains all logic for the building placer - this doesn't include the rendering
 * of info boxes or drawing.
 */
class HUDBuildingPlacerLogic extends _base_hud_part__WEBPACK_IMPORTED_MODULE_10__["BaseHUDPart"] {
    /**
     * Initializes the logic
     * @see BaseHUDPart.initialize
     */
    initialize() {
        /**
         * We use a fake entity to get information about how a building will look
         * once placed
         * @type {Entity}
         */
        this.fakeEntity = null;

        // Signals
        this.signals = {
            variantChanged: new _core_signal__WEBPACK_IMPORTED_MODULE_2__["Signal"](),
            draggingStarted: new _core_signal__WEBPACK_IMPORTED_MODULE_2__["Signal"](),
        };

        /**
         * The current building
         * @type {TypedTrackedState<MetaBuilding?>}
         */
        this.currentMetaBuilding = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_3__["TrackedState"](this.onSelectedMetaBuildingChanged, this);

        /**
         * The current rotation
         * @type {number}
         */
        this.currentBaseRotationGeneral = 0;

        /**
         * The current rotation preference for each building.
         * @type{Object.<string,number>}
         */
        this.preferredBaseRotations = {};

        /**
         * Whether we are currently dragging
         * @type {boolean}
         */
        this.currentlyDragging = false;

        /**
         * Current building variant
         * @type {TypedTrackedState<string>}
         */
        this.currentVariant = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_3__["TrackedState"](() => this.signals.variantChanged.dispatch());

        /**
         * Whether we are currently drag-deleting
         * @type {boolean}
         */
        this.currentlyDeleting = false;

        /**
         * Stores which variants for each building we prefer, this is based on what
         * the user last selected
         * @type {Object.<string, string>}
         */
        this.preferredVariants = {};

        /**
         * The tile we last dragged from
         * @type {Vector}
         */
        this.lastDragTile = null;

        /**
         * The side for direction lock
         * @type {number} (0|1)
         */
        this.currentDirectionLockSide = 0;

        /**
         * Whether the side for direction lock has not yet been determined.
         * @type {boolean}
         */
        this.currentDirectionLockSideIndeterminate = true;

        this.initializeBindings();
    }

    /**
     * Initializes all bindings
     */
    initializeBindings() {
        // KEYBINDINGS
        const keyActionMapper = this.root.keyMapper;
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.rotateWhilePlacing).add(this.tryRotate, this);
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.cycleBuildingVariants).add(this.cycleVariants, this);
        keyActionMapper
            .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.switchDirectionLockSide)
            .add(this.switchDirectionLockSide, this);
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].general.back).add(this.abortPlacement, this);
        keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.pipette).add(this.startPipette, this);
        this.root.gameState.inputReciever.keyup.add(this.checkForDirectionLockSwitch, this);

        // BINDINGS TO GAME EVENTS
        this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this);
        this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this);
        this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch());
        this.root.signals.storyGoalCompleted.add(() => this.currentMetaBuilding.set(null));
        this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch());
        this.root.signals.editModeChanged.add(this.onEditModeChanged, this);

        // MOUSE BINDINGS
        this.root.camera.downPreHandler.add(this.onMouseDown, this);
        this.root.camera.movePreHandler.add(this.onMouseMove, this);
        this.root.camera.upPostHandler.add(this.onMouseUp, this);
    }

    /**
     * Called when the edit mode got changed
     * @param {Layer} layer
     */
    onEditModeChanged(layer) {
        const metaBuilding = this.currentMetaBuilding.get();
        if (metaBuilding) {
            if (metaBuilding.getLayer() !== layer) {
                // This layer doesn't fit the edit mode anymore
                this.currentMetaBuilding.set(null);
            }
        }
    }

    /**
     * Returns the current base rotation for the current meta-building.
     * @returns {number}
     */
    get currentBaseRotation() {
        if (!this.root.app.settings.getAllSettings().rotationByBuilding) {
            return this.currentBaseRotationGeneral;
        }
        const metaBuilding = this.currentMetaBuilding.get();
        if (metaBuilding && this.preferredBaseRotations.hasOwnProperty(metaBuilding.getId())) {
            return this.preferredBaseRotations[metaBuilding.getId()];
        } else {
            return this.currentBaseRotationGeneral;
        }
    }

    /**
     * Sets the base rotation for the current meta-building.
     * @param {number} rotation The new rotation/angle.
     */
    set currentBaseRotation(rotation) {
        if (!this.root.app.settings.getAllSettings().rotationByBuilding) {
            this.currentBaseRotationGeneral = rotation;
        } else {
            const metaBuilding = this.currentMetaBuilding.get();
            if (metaBuilding) {
                this.preferredBaseRotations[metaBuilding.getId()] = rotation;
            } else {
                this.currentBaseRotationGeneral = rotation;
            }
        }
    }

    /**
     * Returns if the direction lock is currently active
     * @returns {boolean}
     */
    get isDirectionLockActive() {
        const metaBuilding = this.currentMetaBuilding.get();
        return (
            metaBuilding &&
            metaBuilding.getHasDirectionLockAvailable() &&
            this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.lockBeltDirection).pressed
        );
    }

    /**
     * Returns the current direction lock corner, that is, the corner between
     * mouse and original start point
     * @returns {Vector|null}
     */
    get currentDirectionLockCorner() {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return null;
        }

        if (!this.lastDragTile) {
            // Haven't dragged yet
            return null;
        }

        // Figure which points the line visits
        const worldPos = this.root.camera.screenToWorld(mousePosition);
        const mouseTile = worldPos.toTileSpace();

        // Figure initial direction
        const dx = Math.abs(this.lastDragTile.x - mouseTile.x);
        const dy = Math.abs(this.lastDragTile.y - mouseTile.y);
        if (dx === 0 && dy === 0) {
            // Back at the start. Try a new direction.
            this.currentDirectionLockSideIndeterminate = true;
        } else if (this.currentDirectionLockSideIndeterminate) {
            this.currentDirectionLockSideIndeterminate = false;
            this.currentDirectionLockSide = dx <= dy ? 0 : 1;
        }

        if (this.currentDirectionLockSide === 0) {
            return new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](this.lastDragTile.x, mouseTile.y);
        } else {
            return new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](mouseTile.x, this.lastDragTile.y);
        }
    }

    /**
     * Aborts the placement
     */
    abortPlacement() {
        if (this.currentMetaBuilding.get()) {
            this.currentMetaBuilding.set(null);
            return _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"];
        }
    }

    /**
     * Aborts any dragging
     */
    abortDragging() {
        this.currentlyDragging = true;
        this.currentlyDeleting = false;
        this.initialPlacementVector = null;
        this.lastDragTile = null;
    }

    /**
     * @see BaseHUDPart.update
     */
    update() {
        // Abort placement if a dialog was shown in the meantime
        if (this.root.hud.hasBlockingOverlayOpen()) {
            this.abortPlacement();
            return;
        }

        // Always update since the camera might have moved
        const mousePos = this.root.app.mousePosition;
        if (mousePos) {
            this.onMouseMove(mousePos);
        }

        // Make sure we have nothing selected while in overview mode
        if (this.root.camera.getIsMapOverlayActive()) {
            if (this.currentMetaBuilding.get()) {
                this.currentMetaBuilding.set(null);
            }
        }
    }

    /**
     * Tries to rotate the current building
     */
    tryRotate() {
        const selectedBuilding = this.currentMetaBuilding.get();
        if (selectedBuilding) {
            if (this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.rotateInverseModifier).pressed) {
                this.currentBaseRotation = (this.currentBaseRotation + 270) % 360;
            } else {
                this.currentBaseRotation = (this.currentBaseRotation + 90) % 360;
            }
            const staticComp = this.fakeEntity.components.StaticMapEntity;
            staticComp.rotation = this.currentBaseRotation;
        }
    }
    /**
     * Tries to delete the building under the mouse
     */
    deleteBelowCursor() {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return false;
        }

        const worldPos = this.root.camera.screenToWorld(mousePosition);
        const tile = worldPos.toTileSpace();
        const contents = this.root.map.getTileContent(tile, this.root.currentLayer);
        if (contents) {
            if (this.root.logic.tryDeleteBuilding(contents)) {
                this.root.soundProxy.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_11__["SOUNDS"].destroyBuilding);
                return true;
            }
        }
        return false;
    }

    /**
     * Starts the pipette function
     */
    startPipette() {
        // Disable in overview
        if (this.root.camera.getIsMapOverlayActive()) {
            return;
        }

        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return;
        }

        const worldPos = this.root.camera.screenToWorld(mousePosition);
        const tile = worldPos.toTileSpace();

        const contents = this.root.map.getTileContent(tile, this.root.currentLayer);
        if (!contents) {
            const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y);

            // Check if there's a shape or color item below, if so select the miner
            if (
                tileBelow &&
                this.root.app.settings.getAllSettings().pickMinerOnPatch &&
                this.root.currentLayer === "regular"
            ) {
                this.currentMetaBuilding.set(_core_global_registries__WEBPACK_IMPORTED_MODULE_1__["gMetaBuildingRegistry"].findByClass(_buildings_miner__WEBPACK_IMPORTED_MODULE_12__["MetaMinerBuilding"]));

                // Select chained miner if available, since that's always desired once unlocked
                if (this.root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_13__["enumHubGoalRewards"].reward_miner_chainable)) {
                    this.currentVariant.set(_buildings_miner__WEBPACK_IMPORTED_MODULE_12__["enumMinerVariants"].chainable);
                }
            } else {
                this.currentMetaBuilding.set(null);
            }
            return;
        }

        // Try to extract the building
        const buildingCode = contents.components.StaticMapEntity.code;
        const extracted = Object(_building_codes__WEBPACK_IMPORTED_MODULE_14__["getBuildingDataFromCode"])(buildingCode);

        // Disable pipetting the hub
        if (extracted.metaInstance.getId() === _core_global_registries__WEBPACK_IMPORTED_MODULE_1__["gMetaBuildingRegistry"].findByClass(_buildings_hub__WEBPACK_IMPORTED_MODULE_15__["MetaHubBuilding"]).getId()) {
            this.currentMetaBuilding.set(null);
            return;
        }

        // If the building we are picking is the same as the one we have, clear the cursor.
        if (
            this.currentMetaBuilding.get() &&
            extracted.metaInstance.getId() === this.currentMetaBuilding.get().getId() &&
            extracted.variant === this.currentVariant.get()
        ) {
            this.currentMetaBuilding.set(null);
            return;
        }

        this.currentMetaBuilding.set(extracted.metaInstance);
        this.currentVariant.set(extracted.variant);
        this.currentBaseRotation = contents.components.StaticMapEntity.rotation;
    }

    /**
     * Switches the side for the direction lock manually
     */
    switchDirectionLockSide() {
        this.currentDirectionLockSide = 1 - this.currentDirectionLockSide;
    }

    /**
     * Checks if the direction lock key got released and if such, resets the placement
     * @param {any} args
     */
    checkForDirectionLockSwitch({ keyCode }) {
        if (
            keyCode ===
            this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.lockBeltDirection).keyCode
        ) {
            this.abortDragging();
        }
    }

    /**
     * Tries to place the current building at the given tile
     * @param {Vector} tile
     */
    tryPlaceCurrentBuildingAt(tile) {
        if (this.root.camera.zoomLevel < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkOverviewMinZoom) {
            // Dont allow placing in overview mode
            return;
        }

        const metaBuilding = this.currentMetaBuilding.get();
        const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({
            root: this.root,
            tile,
            rotation: this.currentBaseRotation,
            variant: this.currentVariant.get(),
            layer: metaBuilding.getLayer(),
        });

        const entity = this.root.logic.tryPlaceBuilding({
            origin: tile,
            rotation,
            rotationVariant,
            originalRotation: this.currentBaseRotation,
            building: this.currentMetaBuilding.get(),
            variant: this.currentVariant.get(),
        });

        if (entity) {
            // Succesfully placed, find which entity we actually placed
            this.root.signals.entityManuallyPlaced.dispatch(entity);

            // Check if we should flip the orientation (used for tunnels)
            if (
                metaBuilding.getFlipOrientationAfterPlacement() &&
                !this.root.keyMapper.getBinding(
                    _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.placementDisableAutoOrientation
                ).pressed
            ) {
                this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
            }

            // Check if we should stop placement
            if (
                !metaBuilding.getStayInPlacementMode() &&
                !this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.placeMultiple).pressed &&
                !this.root.app.settings.getAllSettings().alwaysMultiplace
            ) {
                // Stop placement
                this.currentMetaBuilding.set(null);
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Cycles through the variants
     */
    cycleVariants() {
        const metaBuilding = this.currentMetaBuilding.get();
        if (!metaBuilding) {
            this.currentVariant.set(_meta_building__WEBPACK_IMPORTED_MODULE_9__["defaultBuildingVariant"]);
        } else {
            const availableVariants = metaBuilding.getAvailableVariants(this.root);
            let index = availableVariants.indexOf(this.currentVariant.get());
            if (index < 0) {
                index = 0;
                console.warn("Invalid variant selected:", this.currentVariant.get());
            }
            const direction = this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placement.rotateInverseModifier)
                .pressed
                ? -1
                : 1;

            const newIndex = Object(_core_utils__WEBPACK_IMPORTED_MODULE_16__["safeModulo"])(index + direction, availableVariants.length);
            const newVariant = availableVariants[newIndex];
            this.setVariant(newVariant);
        }
    }

    /**
     * Sets the current variant to the given variant
     * @param {string} variant
     */
    setVariant(variant) {
        const metaBuilding = this.currentMetaBuilding.get();
        this.currentVariant.set(variant);

        this.preferredVariants[metaBuilding.getId()] = variant;
    }

    /**
     * Performs the direction locked placement between two points after
     * releasing the mouse
     */
    executeDirectionLockedPlacement() {
        const metaBuilding = this.currentMetaBuilding.get();
        if (!metaBuilding) {
            // No active building
            return;
        }

        // Get path to place
        const path = this.computeDirectionLockPath();

        // Store if we placed anything
        let anythingPlaced = false;

        // Perform this in bulk to avoid recalculations
        this.root.logic.performBulkOperation(() => {
            for (let i = 0; i < path.length; ++i) {
                const { rotation, tile } = path[i];
                this.currentBaseRotation = rotation;
                if (this.tryPlaceCurrentBuildingAt(tile)) {
                    anythingPlaced = true;
                }
            }
        });

        if (anythingPlaced) {
            this.root.soundProxy.playUi(metaBuilding.getPlacementSound());
        }
    }

    /**
     * Finds the path which the current direction lock will use
     * @returns {Array<{ tile: Vector, rotation: number }>}
     */
    computeDirectionLockPath() {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return [];
        }

        let result = [];

        // Figure which points the line visits
        const worldPos = this.root.camera.screenToWorld(mousePosition);
        let endTile = worldPos.toTileSpace();
        let startTile = this.lastDragTile;

        // if the alt key is pressed, reverse belt planner direction by switching start and end tile
        if (this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.placeInverse).pressed) {
            let tmp = startTile;
            startTile = endTile;
            endTile = tmp;
        }

        // Place from start to corner
        const pathToCorner = this.currentDirectionLockCorner.sub(startTile);
        const deltaToCorner = pathToCorner.normalize().round();
        const lengthToCorner = Math.round(pathToCorner.length());
        let currentPos = startTile.copy();

        let rotation = (Math.round(Math.degrees(deltaToCorner.angle()) / 90) * 90 + 360) % 360;

        if (lengthToCorner > 0) {
            for (let i = 0; i < lengthToCorner; ++i) {
                result.push({
                    tile: currentPos.copy(),
                    rotation,
                });
                currentPos.addInplace(deltaToCorner);
            }
        }

        // Place from corner to end
        const pathFromCorner = endTile.sub(this.currentDirectionLockCorner);
        const deltaFromCorner = pathFromCorner.normalize().round();
        const lengthFromCorner = Math.round(pathFromCorner.length());

        if (lengthFromCorner > 0) {
            rotation = (Math.round(Math.degrees(deltaFromCorner.angle()) / 90) * 90 + 360) % 360;
            for (let i = 0; i < lengthFromCorner + 1; ++i) {
                result.push({
                    tile: currentPos.copy(),
                    rotation,
                });
                currentPos.addInplace(deltaFromCorner);
            }
        } else {
            // Finish last one
            result.push({
                tile: currentPos.copy(),
                rotation,
            });
        }
        return result;
    }

    /**
     * Selects a given building
     * @param {MetaBuilding} metaBuilding
     */
    startSelection(metaBuilding) {
        this.currentMetaBuilding.set(metaBuilding);
    }

    /**
     * Called when the selected buildings changed
     * @param {MetaBuilding} metaBuilding
     */
    onSelectedMetaBuildingChanged(metaBuilding) {
        this.abortDragging();
        this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding);
        if (metaBuilding) {
            const availableVariants = metaBuilding.getAvailableVariants(this.root);
            const preferredVariant = this.preferredVariants[metaBuilding.getId()];

            // Choose last stored variant if possible, otherwise the default one
            let variant;
            if (!preferredVariant || !availableVariants.includes(preferredVariant)) {
                variant = availableVariants[0];
            } else {
                variant = preferredVariant;
            }

            this.currentVariant.set(variant);

            this.fakeEntity = new _entity__WEBPACK_IMPORTED_MODULE_7__["Entity"](null);
            metaBuilding.setupEntityComponents(this.fakeEntity, null);

            this.fakeEntity.addComponent(
                new _components_static_map_entity__WEBPACK_IMPORTED_MODULE_6__["StaticMapEntityComponent"]({
                    origin: new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](0, 0),
                    rotation: 0,
                    tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(),
                    code: Object(_building_codes__WEBPACK_IMPORTED_MODULE_14__["getCodeFromBuildingData"])(metaBuilding, variant, 0),
                })
            );
            metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get());
        } else {
            this.fakeEntity = null;
        }

        // Since it depends on both, rerender twice
        this.signals.variantChanged.dispatch();
    }

    /**
     * mouse down pre handler
     * @param {Vector} pos
     * @param {enumMouseButton} button
     */
    onMouseDown(pos, button) {
        if (this.root.camera.getIsMapOverlayActive()) {
            // We do not allow dragging if the overlay is active
            return;
        }

        const metaBuilding = this.currentMetaBuilding.get();

        // Placement
        if (button === _camera__WEBPACK_IMPORTED_MODULE_5__["enumMouseButton"].left && metaBuilding) {
            this.currentlyDragging = true;
            this.currentlyDeleting = false;
            this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();

            // Place initial building, but only if direction lock is not active
            if (!this.isDirectionLockActive) {
                if (this.tryPlaceCurrentBuildingAt(this.lastDragTile)) {
                    this.root.soundProxy.playUi(metaBuilding.getPlacementSound());
                }
            }
            return _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"];
        }

        // Deletion
        if (
            button === _camera__WEBPACK_IMPORTED_MODULE_5__["enumMouseButton"].right &&
            (!metaBuilding || !this.root.app.settings.getAllSettings().clearCursorOnDeleteWhilePlacing)
        ) {
            this.currentlyDragging = true;
            this.currentlyDeleting = true;
            this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
            if (this.deleteBelowCursor()) {
                return _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"];
            }
        }

        // Cancel placement
        if (button === _camera__WEBPACK_IMPORTED_MODULE_5__["enumMouseButton"].right && metaBuilding) {
            this.currentMetaBuilding.set(null);
        }
    }

    /**
     * mouse move pre handler
     * @param {Vector} pos
     */
    onMouseMove(pos) {
        if (this.root.camera.getIsMapOverlayActive()) {
            return;
        }

        // Check for direction lock
        if (this.isDirectionLockActive) {
            return;
        }

        const metaBuilding = this.currentMetaBuilding.get();
        if ((metaBuilding || this.currentlyDeleting) && this.lastDragTile) {
            const oldPos = this.lastDragTile;
            let newPos = this.root.camera.screenToWorld(pos).toTileSpace();

            // Check if camera is moving, since then we do nothing
            if (this.root.camera.desiredCenter) {
                this.lastDragTile = newPos;
                return;
            }

            // Check if anything changed
            if (!oldPos.equals(newPos)) {
                // Automatic Direction
                if (
                    metaBuilding &&
                    metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) &&
                    !this.root.keyMapper.getBinding(
                        _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.placementDisableAutoOrientation
                    ).pressed
                ) {
                    const delta = newPos.sub(oldPos);
                    const angleDeg = Math.degrees(delta.angle());
                    this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360;

                    // Holding alt inverts the placement
                    if (this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].placementModifiers.placeInverse).pressed) {
                        this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
                    }
                }

                // bresenham
                let x0 = oldPos.x;
                let y0 = oldPos.y;
                let x1 = newPos.x;
                let y1 = newPos.y;

                var dx = Math.abs(x1 - x0);
                var dy = Math.abs(y1 - y0);
                var sx = x0 < x1 ? 1 : -1;
                var sy = y0 < y1 ? 1 : -1;
                var err = dx - dy;

                let anythingPlaced = false;
                let anythingDeleted = false;

                while (this.currentlyDeleting || this.currentMetaBuilding.get()) {
                    if (this.currentlyDeleting) {
                        // Deletion
                        const contents = this.root.map.getLayerContentXY(x0, y0, this.root.currentLayer);
                        if (contents && !contents.queuedForDestroy && !contents.destroyed) {
                            if (this.root.logic.tryDeleteBuilding(contents)) {
                                anythingDeleted = true;
                            }
                        }
                    } else {
                        // Placement
                        if (this.tryPlaceCurrentBuildingAt(new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](x0, y0))) {
                            anythingPlaced = true;
                        }
                    }

                    if (x0 === x1 && y0 === y1) break;
                    var e2 = 2 * err;
                    if (e2 > -dy) {
                        err -= dy;
                        x0 += sx;
                    }
                    if (e2 < dx) {
                        err += dx;
                        y0 += sy;
                    }
                }

                if (anythingPlaced) {
                    this.root.soundProxy.playUi(metaBuilding.getPlacementSound());
                }
                if (anythingDeleted) {
                    this.root.soundProxy.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_11__["SOUNDS"].destroyBuilding);
                }
            }

            this.lastDragTile = newPos;
            return _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"];
        }
    }

    /**
     * Mouse up handler
     */
    onMouseUp() {
        if (this.root.camera.getIsMapOverlayActive()) {
            return;
        }

        // Check for direction lock
        if (this.lastDragTile && this.currentlyDragging && this.isDirectionLockActive) {
            this.executeDirectionLockedPlacement();
        }

        this.abortDragging();
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/buildings_toolbar.js":
/*!****************************************************!*\
  !*** ./src/js/game/hud/parts/buildings_toolbar.js ***!
  \****************************************************/
/*! exports provided: HUDBuildingsToolbar */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDBuildingsToolbar", function() { return HUDBuildingsToolbar; });
/* harmony import */ var _buildings_belt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../buildings/belt */ "./src/js/game/buildings/belt.js");
/* harmony import */ var _buildings_cutter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../buildings/cutter */ "./src/js/game/buildings/cutter.js");
/* harmony import */ var _buildings_display__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../buildings/display */ "./src/js/game/buildings/display.js");
/* harmony import */ var _buildings_filter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../buildings/filter */ "./src/js/game/buildings/filter.js");
/* harmony import */ var _buildings_lever__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../buildings/lever */ "./src/js/game/buildings/lever.js");
/* harmony import */ var _buildings_miner__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../buildings/miner */ "./src/js/game/buildings/miner.js");
/* harmony import */ var _buildings_mixer__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../buildings/mixer */ "./src/js/game/buildings/mixer.js");
/* harmony import */ var _buildings_painter__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../buildings/painter */ "./src/js/game/buildings/painter.js");
/* harmony import */ var _buildings_reader__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../buildings/reader */ "./src/js/game/buildings/reader.js");
/* harmony import */ var _buildings_rotater__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../buildings/rotater */ "./src/js/game/buildings/rotater.js");
/* harmony import */ var _buildings_balancer__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../buildings/balancer */ "./src/js/game/buildings/balancer.js");
/* harmony import */ var _buildings_stacker__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../buildings/stacker */ "./src/js/game/buildings/stacker.js");
/* harmony import */ var _buildings_trash__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../../buildings/trash */ "./src/js/game/buildings/trash.js");
/* harmony import */ var _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../buildings/underground_belt */ "./src/js/game/buildings/underground_belt.js");
/* harmony import */ var _base_toolbar__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./base_toolbar */ "./src/js/game/hud/parts/base_toolbar.js");
/* harmony import */ var _buildings_storage__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../../buildings/storage */ "./src/js/game/buildings/storage.js");
/* harmony import */ var _buildings_item_producer__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../../buildings/item_producer */ "./src/js/game/buildings/item_producer.js");
/* harmony import */ var _core_query_parameters__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../../../core/query_parameters */ "./src/js/core/query_parameters.js");



















class HUDBuildingsToolbar extends _base_toolbar__WEBPACK_IMPORTED_MODULE_14__["HUDBaseToolbar"] {
    constructor(root) {
        super(root, {
            primaryBuildings: [
                _buildings_belt__WEBPACK_IMPORTED_MODULE_0__["MetaBeltBuilding"],
                _buildings_balancer__WEBPACK_IMPORTED_MODULE_10__["MetaBalancerBuilding"],
                _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_13__["MetaUndergroundBeltBuilding"],
                _buildings_miner__WEBPACK_IMPORTED_MODULE_5__["MetaMinerBuilding"],
                _buildings_cutter__WEBPACK_IMPORTED_MODULE_1__["MetaCutterBuilding"],
                _buildings_rotater__WEBPACK_IMPORTED_MODULE_9__["MetaRotaterBuilding"],
                _buildings_stacker__WEBPACK_IMPORTED_MODULE_11__["MetaStackerBuilding"],
                _buildings_mixer__WEBPACK_IMPORTED_MODULE_6__["MetaMixerBuilding"],
                _buildings_painter__WEBPACK_IMPORTED_MODULE_7__["MetaPainterBuilding"],
                _buildings_trash__WEBPACK_IMPORTED_MODULE_12__["MetaTrashBuilding"],
                ...(_core_query_parameters__WEBPACK_IMPORTED_MODULE_17__["queryParamOptions"].sandboxMode || false ? [_buildings_item_producer__WEBPACK_IMPORTED_MODULE_16__["MetaItemProducerBuilding"]] : []),
            ],
            secondaryBuildings: [
                _buildings_storage__WEBPACK_IMPORTED_MODULE_15__["MetaStorageBuilding"],
                _buildings_reader__WEBPACK_IMPORTED_MODULE_8__["MetaReaderBuilding"],
                _buildings_lever__WEBPACK_IMPORTED_MODULE_4__["MetaLeverBuilding"],
                _buildings_filter__WEBPACK_IMPORTED_MODULE_3__["MetaFilterBuilding"],
                _buildings_display__WEBPACK_IMPORTED_MODULE_2__["MetaDisplayBuilding"],
            ],
            visibilityCondition: () =>
                !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular",
            htmlElementId: "ingame_HUD_BuildingsToolbar",
        });
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/cat_memes.js":
/*!********************************************!*\
  !*** ./src/js/game/hud/parts/cat_memes.js ***!
  \********************************************/
/*! exports provided: HUDCatMemes */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDCatMemes", function() { return HUDCatMemes; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");




const memeShowIntervalSeconds = 70 * 60;
const memeShowDuration = 5;

class HUDCatMemes extends _base_hud_part__WEBPACK_IMPORTED_MODULE_1__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(parent, "ingame_HUD_CatMemes");
    }

    initialize() {
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__["DynamicDomAttach"](this.root, this.element);
    }

    update() {
        const now = this.root.time.realtimeNow();
        this.domAttach.update(now % memeShowIntervalSeconds > memeShowIntervalSeconds - memeShowDuration);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/color_blind_helper.js":
/*!*****************************************************!*\
  !*** ./src/js/game/hud/parts/color_blind_helper.js ***!
  \*****************************************************/
/*! exports provided: HUDColorBlindHelper */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDColorBlindHelper", function() { return HUDColorBlindHelper; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../colors */ "./src/js/game/colors.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../theme */ "./src/js/game/theme.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");










class HUDColorBlindHelper extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.belowTileIndicator = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_HUD_ColorBlindBelowTileHelper", []);
    }

    initialize() {
        this.trackedColorBelowTile = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_2__["TrackedState"](this.onColorBelowTileChanged, this);
    }

    /**
     * Called when the color below the current tile changed
     * @param {enumColors|null} color
     */
    onColorBelowTileChanged(color) {
        this.belowTileIndicator.classList.toggle("visible", !!color);
        if (color) {
            this.belowTileIndicator.innerText = _translations__WEBPACK_IMPORTED_MODULE_8__["T"].ingame.colors[color];
        }
    }

    /**
     * Computes the color below the current tile
     * @returns {enumColors}
     */
    computeColorBelowTile() {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return null;
        }

        if (this.root.currentLayer !== "regular") {
            // Not in regular mode
            return null;
        }

        const worldPos = this.root.camera.screenToWorld(mousePosition);
        const tile = worldPos.toTileSpace();
        const contents = this.root.map.getTileContent(tile, this.root.currentLayer);

        if (contents && !contents.components.Miner) {
            const beltComp = contents.components.Belt;

            // Check if the belt has a color item
            if (beltComp) {
                const item = beltComp.assignedPath.findItemAtTile(tile);
                if (item && item.getItemType() === "color") {
                    return /** @type {ColorItem} */ (item).color;
                }
            }

            // Check if we are ejecting an item, if so use that color
            const ejectorComp = contents.components.ItemEjector;
            if (ejectorComp) {
                for (let i = 0; i < ejectorComp.slots.length; ++i) {
                    const slot = ejectorComp.slots[i];
                    if (slot.item && slot.item.getItemType() === "color") {
                        return /** @type {ColorItem} */ (slot.item).color;
                    }
                }
            }
        } else {
            // We hovered a lower layer, show the color there
            const lowerLayer = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
            if (lowerLayer && lowerLayer.getItemType() === "color") {
                return /** @type {ColorItem} */ (lowerLayer).color;
            }
        }

        return null;
    }

    update() {
        this.trackedColorBelowTile.set(this.computeColorBelowTile());
    }

    /**
     * Draws the currently selected tile
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        const mousePosition = this.root.app.mousePosition;
        if (!mousePosition) {
            // Not on screen
            return null;
        }

        const below = this.computeColorBelowTile();
        if (below) {
            // We have something below our tile
            const worldPos = this.root.camera.screenToWorld(mousePosition);
            const tile = worldPos.toTileSpace().toWorldSpace();

            parameters.context.strokeStyle = _theme__WEBPACK_IMPORTED_MODULE_6__["THEME"].map.colorBlindPickerTile;
            parameters.context.lineWidth = 1;
            parameters.context.beginPath();
            parameters.context.rect(tile.x, tile.y, _core_config__WEBPACK_IMPORTED_MODULE_7__["globalConfig"].tileSize, _core_config__WEBPACK_IMPORTED_MODULE_7__["globalConfig"].tileSize);
            parameters.context.stroke();
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/constant_signal_edit.js":
/*!*******************************************************!*\
  !*** ./src/js/game/hud/parts/constant_signal_edit.js ***!
  \*******************************************************/
/*! exports provided: HUDConstantSignalEdit */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDConstantSignalEdit", function() { return HUDConstantSignalEdit; });
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../camera */ "./src/js/game/camera.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");





class HUDConstantSignalEdit extends _base_hud_part__WEBPACK_IMPORTED_MODULE_3__["BaseHUDPart"] {
    initialize() {
        this.root.camera.downPreHandler.add(this.downPreHandler, this);
    }

    /**
     * @param {Vector} pos
     * @param {enumMouseButton} button
     */
    downPreHandler(pos, button) {
        const tile = this.root.camera.screenToWorld(pos).toTileSpace();
        const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires");
        if (contents) {
            const constantComp = contents.components.ConstantSignal;
            if (constantComp) {
                if (button === _camera__WEBPACK_IMPORTED_MODULE_2__["enumMouseButton"].left) {
                    this.root.systemMgr.systems.constantSignal.editConstantSignal(contents, {
                        deleteOnCancel: false,
                    });
                    return _core_signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
                } else if (button === _camera__WEBPACK_IMPORTED_MODULE_2__["enumMouseButton"].right) {
                    this.root.logic.tryDeleteBuilding(contents);
                    return _core_signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/debug_changes.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/debug_changes.js ***!
  \************************************************/
/*! exports provided: HUDChangesDebugger */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDChangesDebugger", function() { return HUDChangesDebugger; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");





/**
 * @typedef {{
 *    label: string,
 *    area: Rectangle,
 *    hideAt: number,
 *    fillColor: string
 * }} DebugChange
 */

class HUDChangesDebugger extends _base_hud_part__WEBPACK_IMPORTED_MODULE_3__["BaseHUDPart"] {
    createElements(parent) {}

    initialize() {
        /** @type {Array<DebugChange>} */
        this.changes = [];
    }

    /**
     * Renders a new change
     * @param {string} label Text to display
     * @param {Rectangle} area Affected area world space
     * @param {string} fillColor Color to display (Hex)
     * @param {number=} timeToDisplay How long to display the change
     */
    renderChange(label, area, fillColor, timeToDisplay = 0.3) {
        this.changes.push({
            label,
            area: area.clone(),
            fillColor,
            hideAt: this.root.time.realtimeNow() + timeToDisplay,
        });
    }

    update() {
        const now = this.root.time.realtimeNow();
        // Detect outdated changes
        for (let i = 0; i < this.changes.length; ++i) {
            const change = this.changes[i];
            if (change.hideAt <= now) {
                this.changes.splice(i, 1);
                i -= 1;
                continue;
            }
        }
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        for (let i = 0; i < this.changes.length; ++i) {
            const change = this.changes[i];
            parameters.context.fillStyle = change.fillColor;
            parameters.context.globalAlpha = 0.2;
            parameters.context.fillRect(
                change.area.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                change.area.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                change.area.w * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                change.area.h * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize
            );

            parameters.context.fillStyle = "#222";
            parameters.context.globalAlpha = 1;
            parameters.context.font = "bold 8px GameFont";
            parameters.context.fillText(
                change.label,
                change.area.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + 2,
                change.area.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + 12
            );
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/debug_info.js":
/*!*********************************************!*\
  !*** ./src/js/game/hud/parts/debug_info.js ***!
  \*********************************************/
/*! exports provided: HUDDebugInfo */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDDebugInfo", function() { return HUDDebugInfo; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");







/** @enum {string} */
const enumDebugOverlayMode = { disabled: "disabled", regular: "regular", detailed: "detailed" };

/**
 * Specifies which mode follows after which mode
 * @enum {enumDebugOverlayMode}
 */
const enumDebugOverlayModeNext = {
    [enumDebugOverlayMode.disabled]: enumDebugOverlayMode.regular,
    [enumDebugOverlayMode.regular]: enumDebugOverlayMode.detailed,
    [enumDebugOverlayMode.detailed]: enumDebugOverlayMode.disabled,
};

const UPDATE_INTERVAL_SECONDS = 0.25;

class HUDDebugInfo extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_HUD_DebugInfo", []);

        const tickRateElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["tickRate"]);
        this.trackedTickRate = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](str => (tickRateElement.innerText = str));

        const tickDurationElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["tickDuration"]);
        this.trackedTickDuration = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](str => (tickDurationElement.innerText = str));

        const fpsElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["fps"]);
        this.trackedFPS = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](str => (fpsElement.innerText = str));

        const mousePositionElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["mousePosition"]);
        this.trackedMousePosition = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](str => (mousePositionElement.innerHTML = str));

        const cameraPositionElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["cameraPosition"]);
        this.trackedCameraPosition = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](str => (cameraPositionElement.innerHTML = str));

        this.versionElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["version"], "version unknown");
    }

    initialize() {
        this.lastTick = 0;

        this.trackedMode = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](this.onModeChanged, this);
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__["DynamicDomAttach"](this.root, this.element);

        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].ingame.toggleFPSInfo).add(() => this.cycleModes());

        // Set initial mode
        this.trackedMode.set(enumDebugOverlayMode.disabled);
    }

    /**
     * Called when the mode changed
     * @param {enumDebugOverlayMode} mode
     */
    onModeChanged(mode) {
        this.element.setAttribute("data-mode", mode);
        this.versionElement.innerText = `${"1.2.2"} @ ${"dev"} @ ${"b5a96a91"}`;
    }

    /**
     * Updates the labels
     */
    updateLabels() {
        this.trackedTickRate.set("Tickrate: " + this.root.dynamicTickrate.currentTickRate);
        this.trackedFPS.set(
            "FPS: " +
                Math.round(this.root.dynamicTickrate.averageFps) +
                " (" +
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(1000 / this.root.dynamicTickrate.averageFps) +
                " ms)"
        );
        this.trackedTickDuration.set(
            "Tick: " + Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round3Digits"])(this.root.dynamicTickrate.averageTickDuration) + "ms"
        );
    }

    /**
     * Updates the detailed information
     */
    updateDetailedInformation() {
        const mousePos = this.root.app.mousePosition || new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](0, 0);
        const mouseTile = this.root.camera.screenToWorld(mousePos).toTileSpace();
        const cameraTile = this.root.camera.center.toTileSpace();

        this.trackedMousePosition.set(`Mouse: <code>${mouseTile.x}</code> / <code>${mouseTile.y}</code>`);
        this.trackedCameraPosition.set(`Camera: <code>${cameraTile.x}</code> / <code>${cameraTile.y}</code>`);
    }

    /**
     * Cycles through the different modes
     */
    cycleModes() {
        this.trackedMode.set(enumDebugOverlayModeNext[this.trackedMode.get()]);
    }

    update() {
        const visible = this.trackedMode.get() !== enumDebugOverlayMode.disabled;
        this.domAttach.update(visible);

        if (!visible) {
            return;
        }

        // Periodically update the text
        const now = this.root.time.realtimeNow();
        if (now - this.lastTick > UPDATE_INTERVAL_SECONDS) {
            this.lastTick = now;
            this.updateLabels();
        }

        // Also update detailed information if required
        if (this.trackedMode.get() === enumDebugOverlayMode.detailed) {
            this.updateDetailedInformation();
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/entity_debugger.js":
/*!**************************************************!*\
  !*** ./src/js/game/hud/parts/entity_debugger.js ***!
  \**************************************************/
/*! exports provided: HUDEntityDebugger */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDEntityDebugger", function() { return HUDEntityDebugger; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../entity */ "./src/js/game/entity.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* dev:start */






/**
 * Allows to inspect entities by pressing F8 while hovering them
 */
class HUDEntityDebugger extends _base_hud_part__WEBPACK_IMPORTED_MODULE_3__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(
            parent,
            "ingame_HUD_EntityDebugger",
            [],
            `
            <label>Entity Debugger</label>
            <span class="hint">Use F8 to toggle this overlay</span>

            <div class="propertyTable">
                <div class="entityComponents"></div>
            </div>
        `
        );
        this.componentsElem = this.element.querySelector(".entityComponents");
    }

    initialize() {
        this.root.gameState.inputReciever.keydown.add(key => {
            if (key.keyCode === 119) {
                // F8
                this.pickEntity();
            }
        });

        /**
         * The currently selected entity
         * @type {Entity}
         */
        this.selectedEntity = null;

        this.lastUpdate = 0;

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_4__["DynamicDomAttach"](this.root, this.element);
    }

    pickEntity() {
        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            return;
        }
        const worldPos = this.root.camera.screenToWorld(mousePos);
        const worldTile = worldPos.toTileSpace();
        const entity = this.root.map.getTileContent(worldTile, this.root.currentLayer);

        this.selectedEntity = entity;
        if (entity) {
            this.rerenderFull(entity);
        }
    }

    /**
     *
     * @param {string} name
     * @param {any} val
     * @param {number} indent
     * @param {Array} recursion
     */
    propertyToHTML(name, val, indent = 0, recursion = []) {
        if (indent > 20) {
            return;
        }

        if (val !== null && typeof val === "object") {
            // Array is displayed like object, with indexes
            recursion.push(val);

            // Get type class name (like Array, Object, Vector...)
            let typeName = `(${val.constructor ? val.constructor.name : "unknown"})`;

            if (Array.isArray(val)) {
                typeName = `(Array[${val.length}])`;
            }

            if (val instanceof _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"]) {
                typeName = `(Vector[${val.x}, ${val.y}])`;
            }

            const colorStyle = `color: hsl(${30 * indent}, 100%, 80%)`;

            let html = `<details class="object" style="${colorStyle}">
                            <summary>${name} ${typeName}</summary>
                            <div>`;

            for (const property in val) {
                const isRoot = val[property] == this.root;
                const isRecursive = recursion.includes(val[property]);

                let hiddenValue = isRoot ? "<root>" : null;
                if (isRecursive) {
                    // Avoid recursion by not "expanding" object more than once
                    hiddenValue = "<recursion>";
                }

                html += this.propertyToHTML(
                    property,
                    hiddenValue ? hiddenValue : val[property],
                    indent + 1,
                    [...recursion] // still expand same value in other "branches"
                );
            }

            html += "</div></details>";

            return html;
        }

        const displayValue = (val + "")
            .replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;");
        return `<label>${name}</label> <span>${displayValue}</span>`;
    }

    /**
     * Rerenders the whole container
     * @param {Entity} entity
     */
    rerenderFull(entity) {
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["removeAllChildren"])(this.componentsElem);
        let html = "";

        const property = (strings, val) => `<label>${strings[0]}</label> <span>${val}</span>`;

        html += property`registered ${!!entity.registered}`;
        html += property`uid ${entity.uid}`;
        html += property`destroyed ${!!entity.destroyed}`;

        for (const componentId in entity.components) {
            const data = entity.components[componentId];
            html += "<details class='object'>";
            html += "<summary>" + componentId + "</summary><div>";

            for (const property in data) {
                // Put entity into recursion list, so it won't get "expanded"
                html += this.propertyToHTML(property, data[property], 0, [entity]);
            }

            html += "</div></details>";
        }

        this.componentsElem.innerHTML = html;
    }

    update() {
        this.domAttach.update(!!this.selectedEntity);
    }
}

/* dev:end */


/***/ }),

/***/ "./src/js/game/hud/parts/game_menu.js":
/*!********************************************!*\
  !*** ./src/js/game/hud/parts/game_menu.js ***!
  \********************************************/
/*! exports provided: HUDGameMenu */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDGameMenu", function() { return HUDGameMenu; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _notifications__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./notifications */ "./src/js/game/hud/parts/notifications.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");









class HUDGameMenu extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_HUD_GameMenu");

        const buttons = [
            {
                id: "shop",
                label: "Upgrades",
                handler: () => this.root.hud.parts.shop.show(),
                keybinding: _key_action_mapper__WEBPACK_IMPORTED_MODULE_5__["KEYMAPPINGS"].ingame.menuOpenShop,
                badge: () => this.root.hubGoals.getAvailableUpgradeCount(),
                notification: /** @type {[string, enumNotificationType]} */ ([
                    _translations__WEBPACK_IMPORTED_MODULE_4__["T"].ingame.notifications.newUpgrade,
                    _notifications__WEBPACK_IMPORTED_MODULE_3__["enumNotificationType"].upgrade,
                ]),
                visible: () =>
                    !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3,
            },
            {
                id: "stats",
                label: "Stats",
                handler: () => this.root.hud.parts.statistics.show(),
                keybinding: _key_action_mapper__WEBPACK_IMPORTED_MODULE_5__["KEYMAPPINGS"].ingame.menuOpenStats,
                visible: () =>
                    !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3,
            },
        ];

        /** @type {Array<{
         * badge: function,
         * button: HTMLElement,
         * badgeElement: HTMLElement,
         * lastRenderAmount: number,
         * condition?: function,
         * notification: [string, enumNotificationType]
         * }>} */
        this.badgesToUpdate = [];

        /** @type {Array<{
         * button: HTMLElement,
         * condition: function,
         * domAttach: DynamicDomAttach
         * }>} */
        this.visibilityToUpdate = [];

        buttons.forEach(({ id, label, handler, keybinding, badge, notification, visible }) => {
            const button = document.createElement("button");
            button.classList.add(id);
            this.element.appendChild(button);
            this.trackClicks(button, handler);

            if (keybinding) {
                const binding = this.root.keyMapper.getBinding(keybinding);
                binding.add(handler);
            }

            if (visible) {
                this.visibilityToUpdate.push({
                    button,
                    condition: visible,
                    domAttach: new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_6__["DynamicDomAttach"](this.root, button),
                });
            }

            if (badge) {
                const badgeElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(button, null, ["badge"]);
                this.badgesToUpdate.push({
                    badge,
                    lastRenderAmount: 0,
                    button,
                    badgeElement,
                    notification,
                    condition: visible,
                });
            }
        });

        this.saveButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["button", "save", "animEven"]);
        this.settingsButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["button", "settings"]);

        this.trackClicks(this.saveButton, this.startSave);
        this.trackClicks(this.settingsButton, this.openSettings);
    }

    initialize() {
        this.root.signals.gameSaved.add(this.onGameSaved, this);

        this.trackedIsSaving = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_7__["TrackedState"](this.onIsSavingChanged, this);
    }

    update() {
        let playSound = false;
        let notifications = new Set();

        // Check whether we are saving
        this.trackedIsSaving.set(!!this.root.gameState.currentSavePromise);

        // Update visibility of buttons
        for (let i = 0; i < this.visibilityToUpdate.length; ++i) {
            const { condition, domAttach } = this.visibilityToUpdate[i];
            domAttach.update(condition());
        }

        // Check for notifications and badges
        for (let i = 0; i < this.badgesToUpdate.length; ++i) {
            const {
                badge,
                button,
                badgeElement,
                lastRenderAmount,
                notification,
                condition,
            } = this.badgesToUpdate[i];

            if (condition && !condition()) {
                // Do not show notifications for invisible buttons
                continue;
            }

            // Check if the amount shown differs from the one shown last frame
            const amount = badge();
            if (lastRenderAmount !== amount) {
                if (amount > 0) {
                    badgeElement.innerText = amount;
                }
                // Check if the badge increased, if so play a notification
                if (amount > lastRenderAmount) {
                    playSound = true;
                    if (notification) {
                        notifications.add(notification);
                    }
                }

                // Rerender notifications
                this.badgesToUpdate[i].lastRenderAmount = amount;
                button.classList.toggle("hasBadge", amount > 0);
            }
        }

        if (playSound) {
            this.root.soundProxy.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_2__["SOUNDS"].badgeNotification);
        }

        notifications.forEach(([notification, type]) => {
            this.root.hud.signals.notification.dispatch(notification, type);
        });
    }

    onIsSavingChanged(isSaving) {
        this.saveButton.classList.toggle("saving", isSaving);
    }

    onGameSaved() {
        this.saveButton.classList.toggle("animEven");
        this.saveButton.classList.toggle("animOdd");
    }

    startSave() {
        this.root.gameState.doSave();
    }

    openSettings() {
        this.root.hud.parts.settingsMenu.show();
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/interactive_tutorial.js":
/*!*******************************************************!*\
  !*** ./src/js/game/hud/parts/interactive_tutorial.js ***!
  \*******************************************************/
/*! exports provided: HUDInteractiveTutorial */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDInteractiveTutorial", function() { return HUDInteractiveTutorial; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../root */ "./src/js/game/root.js");
/* harmony import */ var _components_miner__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../components/miner */ "./src/js/game/components/miner.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _core_cachebust__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../core/cachebust */ "./src/js/core/cachebust.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../items/shape_item */ "./src/js/game/items/shape_item.js");
/* harmony import */ var _components_wire__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../components/wire */ "./src/js/game/components/wire.js");
/* harmony import */ var _components_lever__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../components/lever */ "./src/js/game/components/lever.js");













// @todo: Make dictionary
const tutorialsByLevel = [
    // Level 1
    [
        // 1.1. place an extractor
        {
            id: "1_1_extractor",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr.getAllWithComponent(_components_miner__WEBPACK_IMPORTED_MODULE_3__["MinerComponent"]).length === 0,
        },
        // 1.2. connect to hub
        {
            id: "1_2_conveyor",
            condition: /** @param {GameRoot} root */ root => root.hubGoals.getCurrentGoalDelivered() === 0,
        },
        // 1.3 wait for completion
        {
            id: "1_3_expand",
            condition: () => true,
        },
    ],
    // Level 2
    [
        // 2.1 place a cutter
        {
            id: "2_1_place_cutter",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr
                    .getAllWithComponent(_components_item_processor__WEBPACK_IMPORTED_MODULE_8__["ItemProcessorComponent"])
                    .filter(e => e.components.ItemProcessor.type === _components_item_processor__WEBPACK_IMPORTED_MODULE_8__["enumItemProcessorTypes"].cutter).length ===
                0,
        },
        // 2.2 place trash
        {
            id: "2_2_place_trash",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr
                    .getAllWithComponent(_components_item_processor__WEBPACK_IMPORTED_MODULE_8__["ItemProcessorComponent"])
                    .filter(e => e.components.ItemProcessor.type === _components_item_processor__WEBPACK_IMPORTED_MODULE_8__["enumItemProcessorTypes"].trash).length ===
                0,
        },
        // 2.3 place more cutters
        {
            id: "2_3_more_cutters",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr
                    .getAllWithComponent(_components_item_processor__WEBPACK_IMPORTED_MODULE_8__["ItemProcessorComponent"])
                    .filter(e => e.components.ItemProcessor.type === _components_item_processor__WEBPACK_IMPORTED_MODULE_8__["enumItemProcessorTypes"].cutter).length <
                3,
        },
    ],

    // Level 3
    [
        // 3.1. rectangles
        {
            id: "3_1_rectangles",
            condition: /** @param {GameRoot} root */ root =>
                // 4 miners placed above rectangles and 10 delivered
                root.hubGoals.getCurrentGoalDelivered() < 10 ||
                root.entityMgr.getAllWithComponent(_components_miner__WEBPACK_IMPORTED_MODULE_3__["MinerComponent"]).filter(entity => {
                    const tile = entity.components.StaticMapEntity.origin;
                    const below = root.map.getLowerLayerContentXY(tile.x, tile.y);
                    if (below && below.getItemType() === "shape") {
                        const shape = /** @type {ShapeItem} */ (below).definition.getHash();
                        return shape === "RuRuRuRu";
                    }
                    return false;
                }).length < 4,
        },
    ],

    [], // Level 4
    [], // Level 5
    [], // Level 6
    [], // Level 7
    [], // Level 8
    [], // Level 9
    [], // Level 10
    [], // Level 11
    [], // Level 12
    [], // Level 13
    [], // Level 14
    [], // Level 15
    [], // Level 16
    [], // Level 17
    [], // Level 18
    [], // Level 19
    [], // Level 20

    // Level 21
    [
        // 21.1 place quad painter
        {
            id: "21_1_place_quad_painter",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr
                    .getAllWithComponent(_components_item_processor__WEBPACK_IMPORTED_MODULE_8__["ItemProcessorComponent"])
                    .filter(e => e.components.ItemProcessor.type === _components_item_processor__WEBPACK_IMPORTED_MODULE_8__["enumItemProcessorTypes"].painterQuad)
                    .length === 0,
        },

        // 21.2 switch to wires layer
        {
            id: "21_2_switch_to_wires",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr.getAllWithComponent(_components_wire__WEBPACK_IMPORTED_MODULE_10__["WireComponent"]).length < 5,
        },

        // 21.3 place button
        {
            id: "21_3_place_button",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr.getAllWithComponent(_components_lever__WEBPACK_IMPORTED_MODULE_11__["LeverComponent"]).length === 0,
        },

        // 21.4 activate button
        {
            id: "21_4_press_button",
            condition: /** @param {GameRoot} root */ root =>
                root.entityMgr.getAllWithComponent(_components_lever__WEBPACK_IMPORTED_MODULE_11__["LeverComponent"]).some(e => !e.components.Lever.toggled),
        },
    ],
];

class HUDInteractiveTutorial extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
            parent,
            "ingame_HUD_InteractiveTutorial",
            ["animEven"],
            `
            <strong class="title">${_translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.interactiveTutorial.title}</strong>
            `
        );

        this.elementDescription = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["desc"]);
        this.elementGif = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.element, null, ["helperGif"]);
    }

    initialize() {
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_4__["DynamicDomAttach"](this.root, this.element, { trackHover: true });
        this.currentHintId = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_5__["TrackedState"](this.onHintChanged, this);
    }

    onHintChanged(hintId) {
        this.elementDescription.innerHTML = _translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.interactiveTutorial.hints[hintId];
        this.elementGif.style.backgroundImage =
            "url('" + Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_6__["cachebust"])("res/ui/interactive_tutorial.noinline/" + hintId + ".gif") + "')";
        this.element.classList.toggle("animEven");
        this.element.classList.toggle("animOdd");
    }

    update() {
        // Compute current hint
        const thisLevelHints = tutorialsByLevel[this.root.hubGoals.level - 1];
        let targetHintId = null;

        if (thisLevelHints) {
            for (let i = 0; i < thisLevelHints.length; ++i) {
                const hint = thisLevelHints[i];
                if (hint.condition(this.root)) {
                    targetHintId = hint.id;
                    break;
                }
            }
        }

        this.currentHintId.set(targetHintId);
        this.domAttach.update(!!targetHintId);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/keybinding_overlay.js":
/*!*****************************************************!*\
  !*** ./src/js/game/hud/parts/keybinding_overlay.js ***!
  \*****************************************************/
/*! exports provided: HUDKeybindingOverlay */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDKeybindingOverlay", function() { return HUDKeybindingOverlay; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");







const DIVIDER_TOKEN = "/";
const ADDER_TOKEN = "+";

/**
 * @typedef {{ keyCode: number }} KeyCode
 */

/**
 * @typedef {{
 *   condition: () => boolean,
 *   keys: Array<KeyCode|number|string>,
 *   label: string,
 *   cachedElement?: HTMLElement,
 *   cachedVisibility?: boolean
 * }} KeyBinding
 */

class HUDKeybindingOverlay extends _base_hud_part__WEBPACK_IMPORTED_MODULE_4__["BaseHUDPart"] {
    /**
     * HELPER / Returns if there is a building selected for placement
     * @returns {boolean}
     */
    get buildingPlacementActive() {
        const placer = this.root.hud.parts.buildingPlacer;
        return !this.mapOverviewActive && placer && !!placer.currentMetaBuilding.get();
    }

    /**
     * HELPER / Returns if there is a building selected for placement and
     * it supports the belt planner
     * @returns {boolean}
     */
    get buildingPlacementSupportsBeltPlanner() {
        const placer = this.root.hud.parts.buildingPlacer;
        return (
            !this.mapOverviewActive &&
            placer &&
            placer.currentMetaBuilding.get() &&
            placer.currentMetaBuilding.get().getHasDirectionLockAvailable()
        );
    }

    /**
     * HELPER / Returns if there is a building selected for placement and
     * it has multiplace enabled by default
     * @returns {boolean}
     */
    get buildingPlacementStaysInPlacement() {
        const placer = this.root.hud.parts.buildingPlacer;
        return (
            !this.mapOverviewActive &&
            placer &&
            placer.currentMetaBuilding.get() &&
            placer.currentMetaBuilding.get().getStayInPlacementMode()
        );
    }

    /**
     * HELPER / Returns if there is a blueprint selected for placement
     * @returns {boolean}
     */
    get blueprintPlacementActive() {
        const placer = this.root.hud.parts.blueprintPlacer;
        return placer && !!placer.currentBlueprint.get();
    }

    /**
     * HELPER / Returns if the belt planner is currently active
     * @returns {boolean}
     */
    get beltPlannerActive() {
        const placer = this.root.hud.parts.buildingPlacer;
        return !this.mapOverviewActive && placer && placer.isDirectionLockActive;
    }

    /**
     * HELPER / Returns if there is a last blueprint available
     * @returns {boolean}
     */
    get lastBlueprintAvailable() {
        const placer = this.root.hud.parts.blueprintPlacer;
        return placer && !!placer.lastBlueprintUsed;
    }

    /**
     * HELPER / Returns if there is anything selected on the map
     * @returns {boolean}
     */
    get anythingSelectedOnMap() {
        const selector = this.root.hud.parts.massSelector;
        return selector && selector.selectedUids.size > 0;
    }

    /**
     * HELPER / Returns if there is a building or blueprint selected for placement
     * @returns {boolean}
     */
    get anyPlacementActive() {
        return this.buildingPlacementActive || this.blueprintPlacementActive;
    }

    /**
     * HELPER / Returns if the map overview is active
     * @returns {boolean}
     */
    get mapOverviewActive() {
        return this.root.camera.getIsMapOverlayActive();
    }

    /**
     * Initializes the element
     * @param {HTMLElement} parent
     */
    createElements(parent) {
        const mapper = this.root.keyMapper;
        const k = _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"];

        /** @type {Array<KeyBinding>} */
        this.keybindings = [
            {
                // Move map - Including mouse
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.moveMap,
                keys: [
                    _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_LMB"],
                    DIVIDER_TOKEN,
                    k.navigation.mapMoveUp,
                    k.navigation.mapMoveLeft,
                    k.navigation.mapMoveDown,
                    k.navigation.mapMoveRight,
                ],
                condition: () => !this.anyPlacementActive,
            },

            {
                // Move map - No mouse
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.moveMap,
                keys: [
                    k.navigation.mapMoveUp,
                    k.navigation.mapMoveLeft,
                    k.navigation.mapMoveDown,
                    k.navigation.mapMoveRight,
                ],
                condition: () => this.anyPlacementActive,
            },

            {
                // [OVERVIEW] Create marker with right click
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.createMarker,
                keys: [_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_RMB"]],
                condition: () => this.mapOverviewActive && !this.blueprintPlacementActive,
            },

            {
                // Cancel placement
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.stopPlacement,
                keys: [_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_RMB"]],
                condition: () => this.anyPlacementActive,
            },

            {
                // Delete with right click
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.delete,
                keys: [_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_RMB"]],
                condition: () =>
                    !this.anyPlacementActive && !this.mapOverviewActive && !this.anythingSelectedOnMap,
            },

            {
                // Pipette
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.pipette,
                keys: [k.placement.pipette],
                condition: () => !this.mapOverviewActive && !this.blueprintPlacementActive,
            },

            {
                // Area select
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.selectBuildings,
                keys: [k.massSelect.massSelectStart, ADDER_TOKEN, _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_LMB"]],
                condition: () => !this.anyPlacementActive && !this.anythingSelectedOnMap,
            },

            {
                // Place building
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.placeBuilding,
                keys: [_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_LMB"]],
                condition: () => this.anyPlacementActive,
            },

            {
                // Rotate
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.rotateBuilding,
                keys: [k.placement.rotateWhilePlacing],
                condition: () => this.anyPlacementActive && !this.beltPlannerActive,
            },

            {
                // [BELT PLANNER] Flip Side
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.plannerSwitchSide,
                keys: [k.placement.switchDirectionLockSide],
                condition: () => this.beltPlannerActive,
            },

            {
                // Place last blueprint
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.pasteLastBlueprint,
                keys: [k.massSelect.pasteLastBlueprint],
                condition: () => !this.blueprintPlacementActive && this.lastBlueprintAvailable,
            },

            {
                // Belt planner
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.lockBeltDirection,
                keys: [k.placementModifiers.lockBeltDirection],
                condition: () => this.buildingPlacementSupportsBeltPlanner && !this.beltPlannerActive,
            },

            {
                // [SELECTION] Destroy
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.delete,
                keys: [k.massSelect.confirmMassDelete],
                condition: () => this.anythingSelectedOnMap,
            },

            {
                // [SELECTION] Cancel
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.clearSelection,
                keys: [k.general.back],
                condition: () => this.anythingSelectedOnMap,
            },
            {
                // [SELECTION] Cut
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.cutSelection,
                keys: [k.massSelect.massSelectCut],
                condition: () => this.anythingSelectedOnMap,
            },

            {
                // [SELECTION] Copy
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.copySelection,
                keys: [k.massSelect.massSelectCopy],
                condition: () => this.anythingSelectedOnMap,
            },

            {
                // Switch layers
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.switchLayers,
                keys: [k.ingame.switchLayers],
                condition: () =>
                    this.root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_wires_painter_and_levers),
            },
        ];

        if (!this.root.app.settings.getAllSettings().alwaysMultiplace) {
            this.keybindings.push({
                // Multiplace
                label: _translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.keybindingsOverlay.placeMultiple,
                keys: [k.placementModifiers.placeMultiple],
                condition: () => this.anyPlacementActive && !this.buildingPlacementStaysInPlacement,
            });
        }

        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(parent, "ingame_HUD_KeybindingOverlay", []);

        for (let i = 0; i < this.keybindings.length; ++i) {
            let html = "";
            const handle = this.keybindings[i];

            for (let k = 0; k < handle.keys.length; ++k) {
                const key = handle.keys[k];

                switch (key) {
                    case _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_LMB"]:
                        html += `<code class="keybinding leftMouse"></code>`;
                        break;
                    case _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_RMB"]:
                        html += `<code class="keybinding rightMouse"></code>`;
                        break;
                    case _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYCODE_MMB"]:
                        html += `<code class="keybinding middleMouse"></code>`;
                        break;
                    case DIVIDER_TOKEN:
                        html += `<i></i>`;
                        break;
                    case ADDER_TOKEN:
                        html += `+`;
                        break;
                    default:
                        html += `<code class="keybinding">${Object(_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["getStringForKeyCode"])(
                            mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode
                        )}</code>`;
                }
            }
            html += `<label>${handle.label}</label>`;

            handle.cachedElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(this.element, null, ["binding"], html);
            handle.cachedVisibility = false;
        }
    }

    initialize() {
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__["DynamicDomAttach"](this.root, this.element, {
            trackHover: true,
        });
    }

    update() {
        for (let i = 0; i < this.keybindings.length; ++i) {
            const handle = this.keybindings[i];
            const visibility = handle.condition();
            if (visibility !== handle.cachedVisibility) {
                handle.cachedVisibility = visibility;
                handle.cachedElement.classList.toggle("visible", visibility);
            }
        }

        // Required for hover
        this.domAttach.update(true);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/layer_preview.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/layer_preview.js ***!
  \************************************************/
/*! exports provided: HUDLayerPreview */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDLayerPreview", function() { return HUDLayerPreview; });
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../theme */ "./src/js/game/theme.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");








/**
 * Helper class which allows peaking through to the wires layer
 */
class HUDLayerPreview extends _base_hud_part__WEBPACK_IMPORTED_MODULE_6__["BaseHUDPart"] {
    initialize() {
        this.initializeCanvas();
        this.root.signals.aboutToDestruct.add(() => Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["freeCanvas"])(this.canvas));
        this.root.signals.resized.add(this.initializeCanvas, this);
        this.previewOverlay = _core_loader__WEBPACK_IMPORTED_MODULE_2__["Loader"].getSprite("sprites/wires/wires_preview.png");
    }

    /**
     * (re) initializes the canvas
     */
    initializeCanvas() {
        if (this.canvas) {
            Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["freeCanvas"])(this.canvas);
            delete this.canvas;
            delete this.context;
        }

        // Compute how big the preview should be
        this.previewSize = Math.round(
            Math.min(1024, Math.min(this.root.gameWidth, this.root.gameHeight) * 0.8)
        );

        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(this.previewSize, this.previewSize, {
            smooth: true,
            label: "layerPeeker",
            reusable: true,
        });

        context.clearRect(0, 0, this.previewSize, this.previewSize);
        this.canvas = canvas;
        this.context = context;
    }

    /**
     * Prepares the canvas to render at the given worldPos and the given camera scale
     *
     * @param {Vector} worldPos
     * @param {number} scale 1 / zoomLevel
     */
    prepareCanvasForPreview(worldPos, scale) {
        this.context.clearRect(0, 0, this.previewSize, this.previewSize);
        this.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.wires.previewColor;
        this.context.fillRect(0, 0, this.previewSize, this.previewSize);

        const dimensions = scale * this.previewSize;

        const startWorldX = worldPos.x - dimensions / 2;
        const startWorldY = worldPos.y - dimensions / 2;

        const startTileX = Math.floor(startWorldX / _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize);
        const startTileY = Math.floor(startWorldY / _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize);
        const tileDimensions = Math.ceil(dimensions / _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize);

        this.context.save();
        this.context.scale(1 / scale, 1 / scale);
        this.context.translate(
            startTileX * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize - startWorldX,
            startTileY * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize - startWorldY
        );

        for (let dx = 0; dx < tileDimensions; ++dx) {
            for (let dy = 0; dy < tileDimensions; ++dy) {
                const tileX = dx + startTileX;
                const tileY = dy + startTileY;

                const content = this.root.map.getLayerContentXY(tileX, tileY, "wires");
                if (content) {
                    _map_chunk_view__WEBPACK_IMPORTED_MODULE_4__["MapChunkView"].drawSingleWiresOverviewTile({
                        context: this.context,
                        x: dx * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize,
                        y: dy * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize,
                        entity: content,
                        tileSizePixels: _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize,
                    });
                }
            }
        }

        this.context.restore();
        this.context.globalCompositeOperation = "destination-in";
        this.previewOverlay.draw(this.context, 0, 0, this.previewSize, this.previewSize);
        this.context.globalCompositeOperation = "source-over";

        return this.canvas;
    }

    /**
     * Renders the preview at the given position
     * @param {import("../../../core/draw_utils").DrawParameters} parameters
     * @param {Vector} worldPos
     * @param {number} scale 1 / zoomLevel
     */
    renderPreview(parameters, worldPos, scale) {
        if (this.root.currentLayer !== "regular") {
            // Only supporting wires right now
            return;
        }

        const canvas = this.prepareCanvasForPreview(worldPos, scale);

        parameters.context.globalAlpha = 0.3;
        parameters.context.drawImage(
            canvas,
            worldPos.x - (scale * this.previewSize) / 2,
            worldPos.y - (scale * this.previewSize) / 2,
            scale * this.previewSize,
            scale * this.previewSize
        );
        parameters.context.globalAlpha = 1;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/lever_toggle.js":
/*!***********************************************!*\
  !*** ./src/js/game/hud/parts/lever_toggle.js ***!
  \***********************************************/
/*! exports provided: HUDLeverToggle */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDLeverToggle", function() { return HUDLeverToggle; });
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../camera */ "./src/js/game/camera.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");





class HUDLeverToggle extends _base_hud_part__WEBPACK_IMPORTED_MODULE_3__["BaseHUDPart"] {
    initialize() {
        this.root.camera.downPreHandler.add(this.downPreHandler, this);
    }

    /**
     * @param {Vector} pos
     * @param {enumMouseButton} button
     */
    downPreHandler(pos, button) {
        const tile = this.root.camera.screenToWorld(pos).toTileSpace();
        const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
        if (contents) {
            const leverComp = contents.components.Lever;
            if (leverComp) {
                if (button === _camera__WEBPACK_IMPORTED_MODULE_2__["enumMouseButton"].left) {
                    leverComp.toggled = !leverComp.toggled;
                    return _core_signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
                } else if (button === _camera__WEBPACK_IMPORTED_MODULE_2__["enumMouseButton"].right) {
                    this.root.logic.tryDeleteBuilding(contents);
                    return _core_signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/mass_selector.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/mass_selector.js ***!
  \************************************************/
/*! exports provided: HUDMassSelector */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDMassSelector", function() { return HUDMassSelector; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../entity */ "./src/js/game/entity.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../camera */ "./src/js/game/camera.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../theme */ "./src/js/game/theme.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _blueprint__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../../blueprint */ "./src/js/game/blueprint.js");

















const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_9__["createLogger"])("hud/mass_selector");

class HUDMassSelector extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {}

    initialize() {
        this.currentSelectionStartWorld = null;
        this.currentSelectionEnd = null;
        this.selectedUids = new Set();

        this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this);
        this.root.hud.signals.pasteBlueprintRequested.add(this.clearSelection, this);

        this.root.camera.downPreHandler.add(this.onMouseDown, this);
        this.root.camera.movePreHandler.add(this.onMouseMove, this);
        this.root.camera.upPostHandler.add(this.onMouseUp, this);

        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_12__["KEYMAPPINGS"].general.back).add(this.onBack, this);
        this.root.keyMapper
            .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_12__["KEYMAPPINGS"].massSelect.confirmMassDelete)
            .add(this.confirmDelete, this);
        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_12__["KEYMAPPINGS"].massSelect.massSelectCut).add(this.confirmCut, this);
        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_12__["KEYMAPPINGS"].massSelect.massSelectCopy).add(this.startCopy, this);

        this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this);
        this.root.signals.editModeChanged.add(this.clearSelection, this);
    }

    /**
     * Handles the destroy callback and makes sure we clean our list
     * @param {Entity} entity
     */
    onEntityDestroyed(entity) {
        if (this.root.bulkOperationRunning) {
            return;
        }
        this.selectedUids.delete(entity.uid);
    }

    /**
     *
     */
    onBack() {
        // Clear entities on escape
        if (this.selectedUids.size > 0) {
            this.selectedUids = new Set();
            return _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"];
        }
    }

    /**
     * Clears the entire selection
     */
    clearSelection() {
        this.selectedUids = new Set();
    }

    confirmDelete() {
        if (
            !this.root.app.settings.getAllSettings().disableCutDeleteWarnings &&
            this.selectedUids.size > 100
        ) {
            const { ok } = this.root.hud.parts.dialogs.showWarning(
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.massDeleteConfirm.title,
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.massDeleteConfirm.desc.replace(
                    "<count>",
                    "" + Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["formatBigNumberFull"])(this.selectedUids.size)
                ),
                ["cancel:good:escape", "ok:bad:enter"]
            );
            ok.add(() => this.doDelete());
        } else {
            this.doDelete();
        }
    }

    doDelete() {
        const entityUids = Array.from(this.selectedUids);

        // Build mapping from uid to entity
        /**
         * @type {Map<number, Entity>}
         */
        const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap();

        this.root.logic.performBulkOperation(() => {
            for (let i = 0; i < entityUids.length; ++i) {
                const uid = entityUids[i];
                const entity = mapUidToEntity.get(uid);
                if (!entity) {
                    logger.error("Entity not found by uid:", uid);
                    continue;
                }

                if (!this.root.logic.tryDeleteBuilding(entity)) {
                    logger.error("Error in mass delete, could not remove building");
                }
            }
        });

        // Clear uids later
        this.selectedUids = new Set();
    }

    startCopy() {
        if (this.selectedUids.size > 0) {
            if (!this.root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_14__["enumHubGoalRewards"].reward_blueprints)) {
                this.root.hud.parts.dialogs.showInfo(
                    _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.blueprintsNotUnlocked.title,
                    _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.blueprintsNotUnlocked.desc
                );
                return;
            }
            this.root.hud.signals.buildingsSelectedForCopy.dispatch(Array.from(this.selectedUids));
            this.selectedUids = new Set();
            this.root.soundProxy.playUiClick();
        } else {
            this.root.soundProxy.playUiError();
        }
    }

    confirmCut() {
        if (!this.root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_14__["enumHubGoalRewards"].reward_blueprints)) {
            this.root.hud.parts.dialogs.showInfo(
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.blueprintsNotUnlocked.title,
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.blueprintsNotUnlocked.desc
            );
        } else if (
            !this.root.app.settings.getAllSettings().disableCutDeleteWarnings &&
            this.selectedUids.size > 100
        ) {
            const { ok } = this.root.hud.parts.dialogs.showWarning(
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.massCutConfirm.title,
                _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.massCutConfirm.desc.replace(
                    "<count>",
                    "" + Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["formatBigNumberFull"])(this.selectedUids.size)
                ),
                ["cancel:good:escape", "ok:bad:enter"]
            );
            ok.add(() => this.doCut());
        } else {
            this.doCut();
        }
    }

    doCut() {
        if (this.selectedUids.size > 0) {
            const entityUids = Array.from(this.selectedUids);

            const cutAction = () => {
                // copy code relies on entities still existing, so must copy before deleting.
                this.root.hud.signals.buildingsSelectedForCopy.dispatch(entityUids);

                for (let i = 0; i < entityUids.length; ++i) {
                    const uid = entityUids[i];
                    const entity = this.root.entityMgr.findByUid(uid);
                    if (!this.root.logic.tryDeleteBuilding(entity)) {
                        logger.error("Error in mass cut, could not remove building");
                        this.selectedUids.delete(uid);
                    }
                }
            };

            const blueprint = _blueprint__WEBPACK_IMPORTED_MODULE_15__["Blueprint"].fromUids(this.root, entityUids);
            if (blueprint.canAfford(this.root)) {
                cutAction();
            } else {
                const { cancel, ok } = this.root.hud.parts.dialogs.showWarning(
                    _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.massCutInsufficientConfirm.title,
                    _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.massCutInsufficientConfirm.desc,
                    ["cancel:good:escape", "ok:bad:enter"]
                );
                ok.add(cutAction);
            }

            this.root.soundProxy.playUiClick();
        } else {
            this.root.soundProxy.playUiError();
        }
    }

    /**
     * mouse down pre handler
     * @param {Vector} pos
     * @param {enumMouseButton} mouseButton
     */
    onMouseDown(pos, mouseButton) {
        if (!this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_12__["KEYMAPPINGS"].massSelect.massSelectStart).pressed) {
            return;
        }

        if (mouseButton !== _camera__WEBPACK_IMPORTED_MODULE_10__["enumMouseButton"].left) {
            return;
        }

        if (!this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_12__["KEYMAPPINGS"].massSelect.massSelectSelectMultiple).pressed) {
            // Start new selection
            this.selectedUids = new Set();
        }

        this.currentSelectionStartWorld = this.root.camera.screenToWorld(pos.copy());
        this.currentSelectionEnd = pos.copy();
        return _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"];
    }

    /**
     * mouse move pre handler
     * @param {Vector} pos
     */
    onMouseMove(pos) {
        if (this.currentSelectionStartWorld) {
            this.currentSelectionEnd = pos.copy();
        }
    }

    onMouseUp() {
        if (this.currentSelectionStartWorld) {
            const worldStart = this.currentSelectionStartWorld;
            const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);

            const tileStart = worldStart.toTileSpace();
            const tileEnd = worldEnd.toTileSpace();

            const realTileStart = tileStart.min(tileEnd);
            const realTileEnd = tileStart.max(tileEnd);

            for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
                for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
                    const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
                    if (contents && this.root.logic.canDeleteBuilding(contents)) {
                        this.selectedUids.add(contents.uid);
                    }
                }
            }

            this.currentSelectionStartWorld = null;
            this.currentSelectionEnd = null;
        }
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        const boundsBorder = 2;

        if (this.currentSelectionStartWorld) {
            const worldStart = this.currentSelectionStartWorld;
            const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);

            const realWorldStart = worldStart.min(worldEnd);
            const realWorldEnd = worldStart.max(worldEnd);

            const tileStart = worldStart.toTileSpace();
            const tileEnd = worldEnd.toTileSpace();

            const realTileStart = tileStart.min(tileEnd);
            const realTileEnd = tileStart.max(tileEnd);

            parameters.context.lineWidth = 1;
            parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_13__["THEME"].map.selectionBackground;
            parameters.context.strokeStyle = _theme__WEBPACK_IMPORTED_MODULE_13__["THEME"].map.selectionOutline;
            parameters.context.beginPath();
            parameters.context.rect(
                realWorldStart.x,
                realWorldStart.y,
                realWorldEnd.x - realWorldStart.x,
                realWorldEnd.y - realWorldStart.y
            );
            parameters.context.fill();
            parameters.context.stroke();

            parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_13__["THEME"].map.selectionOverlay;

            const renderedUids = new Set();

            for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
                for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
                    const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
                    if (contents && this.root.logic.canDeleteBuilding(contents)) {
                        // Prevent rendering the overlay twice
                        const uid = contents.uid;
                        if (renderedUids.has(uid)) {
                            continue;
                        }
                        renderedUids.add(uid);

                        const staticComp = contents.components.StaticMapEntity;
                        const bounds = staticComp.getTileSpaceBounds();
                        parameters.context.beginRoundedRect(
                            bounds.x * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize + boundsBorder,
                            bounds.y * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize + boundsBorder,
                            bounds.w * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize - 2 * boundsBorder,
                            bounds.h * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize - 2 * boundsBorder,
                            2
                        );
                        parameters.context.fill();
                    }
                }
            }
        }

        parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_13__["THEME"].map.selectionOverlay;
        this.selectedUids.forEach(uid => {
            const entity = this.root.entityMgr.findByUid(uid);
            const staticComp = entity.components.StaticMapEntity;
            const bounds = staticComp.getTileSpaceBounds();
            parameters.context.beginRoundedRect(
                bounds.x * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize + boundsBorder,
                bounds.y * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize + boundsBorder,
                bounds.w * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize - 2 * boundsBorder,
                bounds.h * _core_config__WEBPACK_IMPORTED_MODULE_6__["globalConfig"].tileSize - 2 * boundsBorder,
                2
            );
            parameters.context.fill();
        });
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/miner_highlight.js":
/*!**************************************************!*\
  !*** ./src/js/game/hud/parts/miner_highlight.js ***!
  \**************************************************/
/*! exports provided: HUDMinerHighlight */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDMinerHighlight", function() { return HUDMinerHighlight; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../entity */ "./src/js/game/entity.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../theme */ "./src/js/game/theme.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");








class HUDMinerHighlight extends _base_hud_part__WEBPACK_IMPORTED_MODULE_6__["BaseHUDPart"] {
    initialize() {}

    /**
     *
     * @param {import("../../../core/draw_utils").DrawParameters} parameters
     */
    draw(parameters) {
        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            // Mouse pos not ready
            return;
        }

        if (this.root.currentLayer !== "regular") {
            // Not within the regular layer
            return;
        }

        if (this.root.camera.getIsMapOverlayActive()) {
            // Not within the map overlay
            return;
        }

        const worldPos = this.root.camera.screenToWorld(mousePos);
        const hoveredTile = worldPos.toTileSpace();

        const contents = this.root.map.getTileContent(hoveredTile, "regular");
        if (!contents) {
            // Empty tile
            return;
        }

        const minerComp = contents.components.Miner;
        if (!minerComp || !minerComp.chainable) {
            // Not a chainable miner
            return;
        }

        const lowerContents = this.root.map.getLowerLayerContentXY(hoveredTile.x, hoveredTile.y);
        if (!lowerContents) {
            // Not connected
            return;
        }

        parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.connectedMiners.overlay;

        const connectedEntities = this.findConnectedMiners(contents);

        for (let i = 0; i < connectedEntities.length; ++i) {
            const entity = connectedEntities[i];
            const staticComp = entity.components.StaticMapEntity;

            parameters.context.beginRoundedRect(
                staticComp.origin.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + 5,
                staticComp.origin.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + 5,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize - 10,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize - 10,
                3
            );
            parameters.context.fill();
        }

        const throughput = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round2Digits"])(connectedEntities.length * this.root.hubGoals.getMinerBaseSpeed());

        const maxThroughput = this.root.hubGoals.getBeltBaseSpeed();

        const tooltipLocation = this.root.camera.screenToWorld(mousePos);

        const scale = (1 / this.root.camera.zoomLevel) * this.root.app.getEffectiveUiScale();

        const isCapped = throughput > maxThroughput;

        // Background
        parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.connectedMiners.background;
        parameters.context.beginRoundedRect(
            tooltipLocation.x + 5 * scale,
            tooltipLocation.y - 3 * scale,
            (isCapped ? 100 : 65) * scale,
            (isCapped ? 45 : 30) * scale,
            2
        );
        parameters.context.fill();

        // Throughput
        parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.connectedMiners.textColor;
        parameters.context.font = "bold " + scale * 10 + "px GameFont";
        parameters.context.fillText(
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["formatItemsPerSecond"])(throughput),
            tooltipLocation.x + 10 * scale,
            tooltipLocation.y + 10 * scale
        );

        // Amount of miners
        parameters.context.globalAlpha = 0.6;
        parameters.context.font = "bold " + scale * 8 + "px GameFont";
        parameters.context.fillText(
            connectedEntities.length === 1
                ? _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.connectedMiners.one_miner
                : _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.connectedMiners.n_miners.replace("<amount>", String(connectedEntities.length)),
            tooltipLocation.x + 10 * scale,
            tooltipLocation.y + 22 * scale
        );

        parameters.context.globalAlpha = 1;

        if (isCapped) {
            parameters.context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.connectedMiners.textColorCapped;
            parameters.context.fillText(
                _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.connectedMiners.limited_items.replace(
                    "<max_throughput>",
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["formatItemsPerSecond"])(maxThroughput)
                ),
                tooltipLocation.x + 10 * scale,
                tooltipLocation.y + 34 * scale
            );
        }
    }

    /**
     * Finds all connected miners to the given entity
     * @param {Entity} entity
     * @param {Set<number>} seenUids Which entities have already been processed
     * @returns {Array<Entity>} The connected miners
     */
    findConnectedMiners(entity, seenUids = new Set()) {
        let results = [];
        const origin = entity.components.StaticMapEntity.origin;

        if (!seenUids.has(entity.uid)) {
            seenUids.add(entity.uid);
            results.push(entity);
        }

        // Check for the miner which we connect to
        const connectedMiner = this.root.systemMgr.systems.miner.findChainedMiner(entity);
        if (connectedMiner && !seenUids.has(connectedMiner.uid)) {
            results.push(connectedMiner);
            seenUids.add(connectedMiner.uid);
            results.push(...this.findConnectedMiners(connectedMiner, seenUids));
        }

        // Search within a 1x1 grid - this assumes miners are always 1x1
        for (let dx = -1; dx <= 1; ++dx) {
            for (let dy = -1; dy <= 1; ++dy) {
                const contents = this.root.map.getTileContent(
                    new _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](origin.x + dx, origin.y + dy),
                    "regular"
                );
                if (contents) {
                    const minerComp = contents.components.Miner;
                    if (minerComp && minerComp.chainable) {
                        // Found a miner connected to this entity
                        if (!seenUids.has(contents.uid)) {
                            if (this.root.systemMgr.systems.miner.findChainedMiner(contents) === entity) {
                                results.push(contents);
                                seenUids.add(contents.uid);
                                results.push(...this.findConnectedMiners(contents, seenUids));
                            }
                        }
                    }
                }
            }
        }

        return results;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/modal_dialogs.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/modal_dialogs.js ***!
  \************************************************/
/*! exports provided: HUDModalDialogs */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDModalDialogs", function() { return HUDModalDialogs; });
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/modal_dialog_elements */ "./src/js/core/modal_dialog_elements.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");










class HUDModalDialogs extends _base_hud_part__WEBPACK_IMPORTED_MODULE_2__["BaseHUDPart"] {
    constructor(root, app) {
        // Important: Root is not always available here! Its also used in the main menu
        super(root);

        /** @type {Application} */
        this.app = root ? root.app : app;

        this.dialogParent = null;
        this.dialogStack = [];
    }

    // For use inside of the game, implementation of base hud part
    initialize() {
        this.dialogParent = document.getElementById("ingame_HUD_ModalDialogs");
        this.domWatcher = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_1__["DynamicDomAttach"](this.root, this.dialogParent);
    }

    shouldPauseRendering() {
        return this.dialogStack.length > 0;
    }

    shouldPauseGame() {
        return this.shouldPauseRendering();
    }

    createElements(parent) {
        return Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["makeDiv"])(parent, "ingame_HUD_ModalDialogs");
    }

    // For use outside of the game
    initializeToElement(element) {
        window.assert(element, "No element for dialogs given");
        this.dialogParent = element;
    }

    isBlockingOverlay() {
        return this.dialogStack.length > 0;
    }

    // Methods

    /**
     * @param {string} title
     * @param {string} text
     * @param {Array<string>} buttons
     */
    showInfo(title, text, buttons = ["ok:good"]) {
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__["Dialog"]({
            app: this.app,
            title: title,
            contentHTML: text,
            buttons: buttons,
            type: "info",
        });
        this.internalShowDialog(dialog);

        if (this.app) {
            this.app.sound.playUiSound(_platform_sound__WEBPACK_IMPORTED_MODULE_0__["SOUNDS"].dialogOk);
        }

        return dialog.buttonSignals;
    }

    /**
     * @param {string} title
     * @param {string} text
     * @param {Array<string>} buttons
     */
    showWarning(title, text, buttons = ["ok:good"]) {
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__["Dialog"]({
            app: this.app,
            title: title,
            contentHTML: text,
            buttons: buttons,
            type: "warning",
        });
        this.internalShowDialog(dialog);

        if (this.app) {
            this.app.sound.playUiSound(_platform_sound__WEBPACK_IMPORTED_MODULE_0__["SOUNDS"].dialogError);
        }

        return dialog.buttonSignals;
    }

    /**
     * @param {string} feature
     * @param {string} textPrefab
     */
    showFeatureRestrictionInfo(feature, textPrefab = _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.featureRestriction.desc) {
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__["Dialog"]({
            app: this.app,
            title: _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.featureRestriction.title,
            contentHTML: textPrefab.replace("<feature>", feature),
            buttons: ["cancel:bad", "getStandalone:good"],
            type: "warning",
        });
        this.internalShowDialog(dialog);

        if (this.app) {
            this.app.sound.playUiSound(_platform_sound__WEBPACK_IMPORTED_MODULE_0__["SOUNDS"].dialogOk);
        }

        this.app.analytics.trackUiClick("demo_dialog_show");

        dialog.buttonSignals.cancel.add(() => {
            this.app.analytics.trackUiClick("demo_dialog_cancel");
        });

        dialog.buttonSignals.getStandalone.add(() => {
            this.app.analytics.trackUiClick("demo_dialog_click");
            window.open(_core_config__WEBPACK_IMPORTED_MODULE_6__["THIRDPARTY_URLS"].standaloneStorePage + "?ref=ddc");
        });

        return dialog.buttonSignals;
    }

    showOptionChooser(title, options) {
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__["DialogOptionChooser"]({
            app: this.app,
            title,
            options,
        });
        this.internalShowDialog(dialog);
        return dialog.buttonSignals;
    }

    // Returns method to be called when laoding finishd
    showLoadingDialog() {
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__["DialogLoading"](this.app);
        this.internalShowDialog(dialog);
        return this.closeDialog.bind(this, dialog);
    }

    internalShowDialog(dialog) {
        const elem = dialog.createElement();
        dialog.setIndex(this.dialogStack.length);

        // Hide last dialog in queue
        if (this.dialogStack.length > 0) {
            this.dialogStack[this.dialogStack.length - 1].hide();
        }

        this.dialogStack.push(dialog);

        // Append dialog
        dialog.show();
        dialog.closeRequested.add(this.closeDialog.bind(this, dialog));

        // Append to HTML
        this.dialogParent.appendChild(elem);

        document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0);

        // IMPORTANT: Attach element directly, otherwise double submit is possible
        this.update();
    }

    update() {
        if (this.domWatcher) {
            this.domWatcher.update(this.dialogStack.length > 0);
        }
    }

    closeDialog(dialog) {
        dialog.destroy();

        let index = -1;
        for (let i = 0; i < this.dialogStack.length; ++i) {
            if (this.dialogStack[i] === dialog) {
                index = i;
                break;
            }
        }
        window.assert(index >= 0, "Dialog not in dialog stack");
        this.dialogStack.splice(index, 1);

        if (this.dialogStack.length > 0) {
            // Show the dialog which was previously open
            this.dialogStack[this.dialogStack.length - 1].show();
        }

        document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0);
    }

    close() {
        for (let i = 0; i < this.dialogStack.length; ++i) {
            const dialog = this.dialogStack[i];
            dialog.destroy();
        }
        this.dialogStack = [];
    }

    cleanup() {
        super.cleanup();
        for (let i = 0; i < this.dialogStack.length; ++i) {
            this.dialogStack[i].destroy();
        }
        this.dialogStack = [];
        this.dialogParent = null;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/notifications.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/notifications.js ***!
  \************************************************/
/*! exports provided: enumNotificationType, HUDNotifications */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumNotificationType", function() { return enumNotificationType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDNotifications", function() { return HUDNotifications; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");




/** @enum {string} */
const enumNotificationType = {
    saved: "saved",
    upgrade: "upgrade",
    success: "success",
};

const notificationDuration = 3;

class HUDNotifications extends _base_hud_part__WEBPACK_IMPORTED_MODULE_2__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(parent, "ingame_HUD_Notifications", [], ``);
    }

    initialize() {
        this.root.hud.signals.notification.add(this.onNotification, this);

        /** @type {Array<{ element: HTMLElement, expireAt: number}>} */
        this.notificationElements = [];

        // Automatic notifications
        this.root.signals.gameSaved.add(() =>
            this.onNotification(_translations__WEBPACK_IMPORTED_MODULE_1__["T"].ingame.notifications.gameSaved, enumNotificationType.saved)
        );
    }

    /**
     * @param {string} message
     * @param {enumNotificationType} type
     */
    onNotification(message, type) {
        const element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(this.element, null, ["notification", "type-" + type], message);
        element.setAttribute("data-icon", "icons/notification_" + type + ".png");

        this.notificationElements.push({
            element,
            expireAt: this.root.time.realtimeNow() + notificationDuration,
        });
    }

    update() {
        const now = this.root.time.realtimeNow();
        for (let i = 0; i < this.notificationElements.length; ++i) {
            const handle = this.notificationElements[i];
            if (handle.expireAt <= now) {
                handle.element.remove();
                this.notificationElements.splice(i, 1);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/pinned_shapes.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/pinned_shapes.js ***!
  \************************************************/
/*! exports provided: HUDPinnedShapes */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDPinnedShapes", function() { return HUDPinnedShapes; });
/* harmony import */ var _core_click_detector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _production_analytics__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../production_analytics */ "./src/js/game/production_analytics.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");









/**
 * Manages the pinned shapes on the left side of the screen
 */
class HUDPinnedShapes extends _base_hud_part__WEBPACK_IMPORTED_MODULE_7__["BaseHUDPart"] {
    constructor(root) {
        super(root);
        /**
         * Store a list of pinned shapes
         * @type {Array<string>}
         */
        this.pinnedShapes = [];

        /**
         * Store handles to the currently rendered elements, so we can update them more
         * convenient. Also allows for cleaning up handles.
         * @type {Array<{
         *  key: string,
         *  definition: ShapeDefinition,
         *  amountLabel: HTMLElement,
         *  lastRenderedValue: string,
         *  element: HTMLElement,
         *  detector?: ClickDetector,
         *  infoDetector?: ClickDetector,
         *  throughputOnly?: boolean
         * }>}
         */
        this.handles = [];
    }

    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(parent, "ingame_HUD_PinnedShapes", []);
    }

    /**
     * Serializes the pinned shapes
     */
    serialize() {
        return {
            shapes: this.pinnedShapes,
        };
    }

    /**
     * Deserializes the pinned shapes
     * @param {{ shapes: Array<string>}} data
     */
    deserialize(data) {
        if (!data || !data.shapes || !Array.isArray(data.shapes)) {
            return "Invalid pinned shapes data";
        }
        this.pinnedShapes = data.shapes;
    }

    /**
     * Initializes the hud component
     */
    initialize() {
        // Connect to any relevant signals
        this.root.signals.storyGoalCompleted.add(this.rerenderFull, this);
        this.root.signals.upgradePurchased.add(this.updateShapesAfterUpgrade, this);
        this.root.signals.postLoadHook.add(this.rerenderFull, this);
        this.root.hud.signals.shapePinRequested.add(this.pinNewShape, this);
        this.root.hud.signals.shapeUnpinRequested.add(this.unpinShape, this);

        // Perform initial render
        this.updateShapesAfterUpgrade();
    }

    /**
     * Updates all shapes after an upgrade has been purchased and removes the unused ones
     */
    updateShapesAfterUpgrade() {
        for (let i = 0; i < this.pinnedShapes.length; ++i) {
            const key = this.pinnedShapes[i];
            if (key === this.root.gameMode.getBlueprintShapeKey()) {
                // Ignore blueprint shapes
                continue;
            }
            let goal = this.findGoalValueForShape(key);
            if (!goal) {
                // Seems no longer relevant
                this.pinnedShapes.splice(i, 1);
                i -= 1;
            }
        }

        this.rerenderFull();
    }

    /**
     * Finds the current goal for the given key. If the key is the story goal, returns
     * the story goal. If its the blueprint shape, no goal is returned. Otherwise
     * it's searched for upgrades.
     * @param {string} key
     */
    findGoalValueForShape(key) {
        if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
            return this.root.hubGoals.currentGoal.required;
        }
        if (key === this.root.gameMode.getBlueprintShapeKey()) {
            return null;
        }

        // Check if this shape is required for any upgrade
        const upgrades = this.root.gameMode.getUpgrades();
        for (const upgradeId in upgrades) {
            const upgradeTiers = upgrades[upgradeId];
            const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
            const tierHandle = upgradeTiers[currentTier];

            if (!tierHandle) {
                // Max level
                continue;
            }

            for (let i = 0; i < tierHandle.required.length; ++i) {
                const { shape, amount } = tierHandle.required[i];
                if (shape === key) {
                    return amount;
                }
            }
        }

        return null;
    }

    /**
     * Returns whether a given shape is currently pinned
     * @param {string} key
     */
    isShapePinned(key) {
        if (
            key === this.root.hubGoals.currentGoal.definition.getHash() ||
            key === this.root.gameMode.getBlueprintShapeKey()
        ) {
            // This is a "special" shape which is always pinned
            return true;
        }

        return this.pinnedShapes.indexOf(key) >= 0;
    }

    /**
     * Rerenders the whole component
     */
    rerenderFull() {
        const currentGoal = this.root.hubGoals.currentGoal;
        const currentKey = currentGoal.definition.getHash();

        // First, remove all old shapes
        for (let i = 0; i < this.handles.length; ++i) {
            this.handles[i].element.remove();
            const detector = this.handles[i].detector;
            if (detector) {
                detector.cleanup();
            }
            const infoDetector = this.handles[i].infoDetector;
            if (infoDetector) {
                infoDetector.cleanup();
            }
        }
        this.handles = [];

        // Pin story goal
        this.internalPinShape({
            key: currentKey,
            canUnpin: false,
            className: "goal",
            throughputOnly: currentGoal.throughputOnly,
        });

        // Pin blueprint shape as well
        if (this.root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_6__["enumHubGoalRewards"].reward_blueprints)) {
            this.internalPinShape({
                key: this.root.gameMode.getBlueprintShapeKey(),
                canUnpin: false,
                className: "blueprint",
            });
        }

        // Pin manually pinned shapes
        for (let i = 0; i < this.pinnedShapes.length; ++i) {
            const key = this.pinnedShapes[i];
            if (key !== currentKey) {
                this.internalPinShape({ key });
            }
        }
    }

    /**
     * Pins a new shape
     * @param {object} param0
     * @param {string} param0.key
     * @param {boolean=} param0.canUnpin
     * @param {string=} param0.className
     * @param {boolean=} param0.throughputOnly
     */
    internalPinShape({ key, canUnpin = true, className = null, throughputOnly = false }) {
        const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);

        const element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.element, null, ["shape"]);
        const canvas = definition.generateAsCanvas(120);
        element.appendChild(canvas);

        if (className) {
            element.classList.add(className);
        }

        let detector = null;
        if (canUnpin) {
            const unpinButton = document.createElement("button");
            unpinButton.classList.add("unpinButton");
            element.appendChild(unpinButton);
            element.classList.add("removable");
            detector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](unpinButton, {
                consumeEvents: true,
                preventDefault: true,
                targetOnly: true,
            });
            detector.click.add(() => this.unpinShape(key));
        } else {
            element.classList.add("marked");
        }

        // Show small info icon
        const infoButton = document.createElement("button");
        infoButton.classList.add("infoButton");
        element.appendChild(infoButton);
        const infoDetector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](infoButton, {
            consumeEvents: true,
            preventDefault: true,
            targetOnly: true,
        });
        infoDetector.click.add(() => this.root.hud.signals.viewShapeDetailsRequested.dispatch(definition));

        const amountLabel = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(element, null, ["amountLabel"], "");

        const goal = this.findGoalValueForShape(key);
        if (goal) {
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(element, null, ["goalLabel"], "/" + Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(goal));
        }

        this.handles.push({
            key,
            definition,
            element,
            amountLabel,
            lastRenderedValue: "",
            detector,
            infoDetector,
            throughputOnly,
        });
    }

    /**
     * Updates all amount labels
     */
    update() {
        for (let i = 0; i < this.handles.length; ++i) {
            const handle = this.handles[i];

            let currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
            let currentValueFormatted = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(currentValue);

            if (handle.throughputOnly) {
                currentValue =
                    this.root.productionAnalytics.getCurrentShapeRate(
                        _production_analytics__WEBPACK_IMPORTED_MODULE_4__["enumAnalyticsDataSource"].delivered,
                        handle.definition
                    ) / _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].analyticsSliceDurationSeconds;
                currentValueFormatted = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.statistics.shapesDisplayUnits.second.replace(
                    "<shapes>",
                    String(currentValue)
                );
            }

            if (currentValueFormatted !== handle.lastRenderedValue) {
                handle.lastRenderedValue = currentValueFormatted;
                handle.amountLabel.innerText = currentValueFormatted;
                const goal = this.findGoalValueForShape(handle.key);
                handle.element.classList.toggle("completed", goal && currentValue > goal);
            }
        }
    }

    /**
     * Unpins a shape
     * @param {string} key
     */
    unpinShape(key) {
        console.log("unpin", key);
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["arrayDeleteValue"])(this.pinnedShapes, key);
        this.rerenderFull();
    }

    /**
     * Requests to pin a new shape
     * @param {ShapeDefinition} definition
     */
    pinNewShape(definition) {
        const key = definition.getHash();
        if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
            // Can not pin current goal
            return;
        }

        if (key === this.root.gameMode.getBlueprintShapeKey()) {
            // Can not pin the blueprint shape
            return;
        }

        // Check if its already pinned
        if (this.pinnedShapes.indexOf(key) >= 0) {
            return;
        }

        this.pinnedShapes.push(key);
        this.rerenderFull();
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/sandbox_controller.js":
/*!*****************************************************!*\
  !*** ./src/js/game/hud/parts/sandbox_controller.js ***!
  \*****************************************************/
/*! exports provided: HUDSandboxController */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDSandboxController", function() { return HUDSandboxController; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _notifications__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./notifications */ "./src/js/game/hud/parts/notifications.js");





class HUDSandboxController extends _base_hud_part__WEBPACK_IMPORTED_MODULE_1__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["makeDiv"])(
            parent,
            "ingame_HUD_SandboxController",
            [],
            `
            <label>Sandbox Options</label>
            <span class="sandboxHint">Use F6 to toggle this overlay</span>

            <div class="buttons">
                <div class="levelToggle plusMinus">
                    <label>Level</label>
                    <button class="styledButton minus">-</button>
                    <button class="styledButton plus">+</button>
                </div>
                
                <div class="upgradesBelt plusMinus">
                    <label>Upgrades &rarr; Belt</label>
                    <button class="styledButton minus">-</button>
                    <button class="styledButton plus">+</button>
                </div>
                
                <div class="upgradesExtraction plusMinus">
                    <label>Upgrades &rarr; Extraction</label>
                    <button class="styledButton minus">-</button>
                    <button class="styledButton plus">+</button>
                </div>
                
                <div class="upgradesProcessing plusMinus">
                    <label>Upgrades &rarr; Processing</label>
                    <button class="styledButton minus">-</button>
                    <button class="styledButton plus">+</button>
                </div>
                
                <div class="upgradesPainting plusMinus">
                    <label>Upgrades &rarr; Painting</label>
                    <button class="styledButton minus">-</button>
                    <button class="styledButton plus">+</button>
                </div>

                <div class="additionalOptions">
                    <button class="styledButton giveBlueprints">Fill blueprint shapes</button>
                    <button class="styledButton maxOutAll">Max out all</button>
                </div>
            </div>
        `
        );

        const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler);

        bind(".giveBlueprints", this.giveBlueprints);
        bind(".maxOutAll", this.maxOutAll);
        bind(".levelToggle .minus", () => this.modifyLevel(-1));
        bind(".levelToggle .plus", () => this.modifyLevel(1));

        bind(".upgradesBelt .minus", () => this.modifyUpgrade("belt", -1));
        bind(".upgradesBelt .plus", () => this.modifyUpgrade("belt", 1));

        bind(".upgradesExtraction .minus", () => this.modifyUpgrade("miner", -1));
        bind(".upgradesExtraction .plus", () => this.modifyUpgrade("miner", 1));

        bind(".upgradesProcessing .minus", () => this.modifyUpgrade("processors", -1));
        bind(".upgradesProcessing .plus", () => this.modifyUpgrade("processors", 1));

        bind(".upgradesPainting .minus", () => this.modifyUpgrade("painting", -1));
        bind(".upgradesPainting .plus", () => this.modifyUpgrade("painting", 1));
    }

    giveBlueprints() {
        const shape = this.root.gameMode.getBlueprintShapeKey();
        if (!this.root.hubGoals.storedShapes[shape]) {
            this.root.hubGoals.storedShapes[shape] = 0;
        }
        this.root.hubGoals.storedShapes[shape] += 1e9;
    }

    maxOutAll() {
        this.modifyUpgrade("belt", 100);
        this.modifyUpgrade("miner", 100);
        this.modifyUpgrade("processors", 100);
        this.modifyUpgrade("painting", 100);
    }

    modifyUpgrade(id, amount) {
        const upgradeTiers = this.root.gameMode.getUpgrades()[id];
        const maxLevel = upgradeTiers.length;

        this.root.hubGoals.upgradeLevels[id] = Math.max(
            0,
            Math.min(maxLevel, (this.root.hubGoals.upgradeLevels[id] || 0) + amount)
        );

        // Compute improvement
        let improvement = 1;
        for (let i = 0; i < this.root.hubGoals.upgradeLevels[id]; ++i) {
            improvement += upgradeTiers[i].improvement;
        }
        this.root.hubGoals.upgradeImprovements[id] = improvement;
        this.root.signals.upgradePurchased.dispatch(id);
        this.root.hud.signals.notification.dispatch(
            "Upgrade '" + id + "' is now at tier " + (this.root.hubGoals.upgradeLevels[id] + 1),
            _notifications__WEBPACK_IMPORTED_MODULE_3__["enumNotificationType"].upgrade
        );
    }

    modifyLevel(amount) {
        const hubGoals = this.root.hubGoals;
        hubGoals.level = Math.max(1, hubGoals.level + amount);
        hubGoals.computeNextGoal();

        // Clear all shapes of this level
        hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;

        this.root.hud.parts.pinnedShapes.rerenderFull();

        // Compute gained rewards
        hubGoals.gainedRewards = {};
        const levels = this.root.gameMode.getLevelDefinitions();
        for (let i = 0; i < hubGoals.level - 1; ++i) {
            if (i < levels.length) {
                const reward = levels[i].reward;
                hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
            }
        }

        this.root.hud.signals.notification.dispatch(
            "Changed level to " + hubGoals.level,
            _notifications__WEBPACK_IMPORTED_MODULE_3__["enumNotificationType"].upgrade
        );
    }

    initialize() {
        // Allow toggling the controller overlay
        this.root.gameState.inputReciever.keydown.add(key => {
            if (key.keyCode === 117) {
                // F6
                this.toggle();
            }
        });

        this.visible = !false;
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__["DynamicDomAttach"](this.root, this.element);
    }

    toggle() {
        this.visible = !this.visible;
    }

    update() {
        this.domAttach.update(this.visible);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/screenshot_exporter.js":
/*!******************************************************!*\
  !*** ./src/js/game/hud/parts/screenshot_exporter.js ***!
  \******************************************************/
/*! exports provided: HUDScreenshotExporter */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDScreenshotExporter", function() { return HUDScreenshotExporter; });
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_static_map_entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../components/static_map_entity */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");











const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_3__["createLogger"])("screenshot_exporter");

class HUDScreenshotExporter extends _base_hud_part__WEBPACK_IMPORTED_MODULE_9__["BaseHUDPart"] {
    createElements() {}

    initialize() {
        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_8__["KEYMAPPINGS"].ingame.exportScreenshot).add(this.startExport, this);
    }

    startExport() {
        if (!this.root.app.restrictionMgr.getIsExportingScreenshotsPossible()) {
            this.root.hud.parts.dialogs.showFeatureRestrictionInfo(_translations__WEBPACK_IMPORTED_MODULE_6__["T"].demo.features.exportingBase);
            return;
        }

        const { ok } = this.root.hud.parts.dialogs.showInfo(
            _translations__WEBPACK_IMPORTED_MODULE_6__["T"].dialogs.exportScreenshotWarning.title,
            _translations__WEBPACK_IMPORTED_MODULE_6__["T"].dialogs.exportScreenshotWarning.desc,
            ["cancel:good", "ok:bad"]
        );
        ok.add(this.doExport, this);
    }

    doExport() {
        logger.log("Starting export ...");

        // Find extends
        const staticEntities = this.root.entityMgr.getAllWithComponent(_components_static_map_entity__WEBPACK_IMPORTED_MODULE_7__["StaticMapEntityComponent"]);

        const minTile = new _core_vector__WEBPACK_IMPORTED_MODULE_5__["Vector"](0, 0);
        const maxTile = new _core_vector__WEBPACK_IMPORTED_MODULE_5__["Vector"](0, 0);
        for (let i = 0; i < staticEntities.length; ++i) {
            const bounds = staticEntities[i].components.StaticMapEntity.getTileSpaceBounds();
            minTile.x = Math.min(minTile.x, bounds.x);
            minTile.y = Math.min(minTile.y, bounds.y);

            maxTile.x = Math.max(maxTile.x, bounds.x + bounds.w);
            maxTile.y = Math.max(maxTile.y, bounds.y + bounds.h);
        }

        const minChunk = minTile.divideScalar(_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkSize).floor();
        const maxChunk = maxTile.divideScalar(_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkSize).ceil();

        const dimensions = maxChunk.sub(minChunk);
        logger.log("Dimensions:", dimensions);

        let chunkSizePixels = 128;
        const maxDimensions = Math.max(dimensions.x, dimensions.y);

        if (maxDimensions > 128) {
            chunkSizePixels = Math.max(1, Math.floor(128 * (128 / maxDimensions)));
        }
        logger.log("ChunkSizePixels:", chunkSizePixels);

        const chunkScale = chunkSizePixels / _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkWorldSize;
        logger.log("Scale:", chunkScale);

        logger.log("Allocating buffer, if the factory grew too big it will crash here");
        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(
            dimensions.x * chunkSizePixels,
            dimensions.y * chunkSizePixels,
            {
                smooth: true,
                reusable: false,
                label: "export-buffer",
            }
        );
        logger.log("Got buffer, rendering now ...");

        const visibleRect = new _core_rectangle__WEBPACK_IMPORTED_MODULE_4__["Rectangle"](
            minChunk.x * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkWorldSize,
            minChunk.y * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkWorldSize,
            dimensions.x * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkWorldSize,
            dimensions.y * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].mapChunkWorldSize
        );
        const parameters = new _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__["DrawParameters"]({
            context,
            visibleRect,
            desiredAtlasScale: 0.25,
            root: this.root,
            zoomLevel: chunkScale,
        });

        context.scale(chunkScale, chunkScale);
        context.translate(-visibleRect.x, -visibleRect.y);

        // Render all relevant chunks
        this.root.map.drawBackground(parameters);
        this.root.map.drawForeground(parameters);

        // Offer export
        logger.log("Rendered buffer, exporting ...");
        const image = canvas.toDataURL("image/png");
        const link = document.createElement("a");
        link.download = "base.png";
        link.href = image;
        link.click();
        logger.log("Done!");
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/settings_menu.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/settings_menu.js ***!
  \************************************************/
/*! exports provided: HUDSettingsMenu */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDSettingsMenu", function() { return HUDSettingsMenu; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_static_map_entity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../components/static_map_entity */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _components_belt__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../components/belt */ "./src/js/game/components/belt.js");









class HUDSettingsMenu extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.background = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_HUD_SettingsMenu", ["ingameDialog"]);

        this.menuElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.background, null, ["menuElement"]);

        this.statsElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
            this.background,
            null,
            ["statsElement"],
            `
            <strong>${_translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.settingsMenu.beltsPlaced}</strong><span class="beltsPlaced"></span>
            <strong>${_translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.settingsMenu.buildingsPlaced}</strong><span class="buildingsPlaced"></span>
            <strong>${_translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.settingsMenu.playtime}</strong><span class="playtime"></span>

            `
        );

        this.buttonContainer = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.menuElement, null, ["buttons"]);

        const buttons = [
            {
                id: "continue",
                action: () => this.close(),
            },
            {
                id: "settings",
                action: () => this.goToSettings(),
            },
            {
                id: "menu",
                action: () => this.returnToMenu(),
            },
        ];

        for (let i = 0; i < buttons.length; ++i) {
            const { action, id } = buttons[i];

            const element = document.createElement("button");
            element.classList.add("styledButton");
            element.classList.add(id);
            this.buttonContainer.appendChild(element);

            this.trackClicks(element, action);
        }
    }

    isBlockingOverlay() {
        return this.visible;
    }

    returnToMenu() {
        this.root.gameState.goBackToMenu();
    }

    goToSettings() {
        this.root.gameState.goToSettings();
    }

    shouldPauseGame() {
        return this.visible;
    }

    shouldPauseRendering() {
        return this.visible;
    }

    initialize() {
        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KEYMAPPINGS"].general.back).add(this.show, this);

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_2__["DynamicDomAttach"](this.root, this.background, {
            attachClass: "visible",
        });

        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_3__["InputReceiver"]("settingsmenu");
        this.keyActionMapper = new _key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KeyActionMapper"](this.root, this.inputReciever);
        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KEYMAPPINGS"].general.back).add(this.close, this);

        this.close();
    }

    show() {
        this.visible = true;
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);

        const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60);

        /** @type {HTMLElement} */
        const playtimeElement = this.statsElement.querySelector(".playtime");
        /** @type {HTMLElement} */
        const buildingsPlacedElement = this.statsElement.querySelector(".buildingsPlaced");
        /** @type {HTMLElement} */
        const beltsPlacedElement = this.statsElement.querySelector(".beltsPlaced");

        playtimeElement.innerText = _translations__WEBPACK_IMPORTED_MODULE_5__["T"].global.time.xMinutes.replace("<x>", `${totalMinutesPlayed}`);

        buildingsPlacedElement.innerText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["formatBigNumberFull"])(
            this.root.entityMgr.getAllWithComponent(_components_static_map_entity__WEBPACK_IMPORTED_MODULE_6__["StaticMapEntityComponent"]).length -
                this.root.entityMgr.getAllWithComponent(_components_belt__WEBPACK_IMPORTED_MODULE_7__["BeltComponent"]).length
        );

        beltsPlacedElement.innerText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["formatBigNumberFull"])(
            this.root.entityMgr.getAllWithComponent(_components_belt__WEBPACK_IMPORTED_MODULE_7__["BeltComponent"]).length
        );
    }

    close() {
        this.visible = false;
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        this.update();
    }

    update() {
        this.domAttach.update(this.visible);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/shape_viewer.js":
/*!***********************************************!*\
  !*** ./src/js/game/hud/parts/shape_viewer.js ***!
  \***********************************************/
/*! exports provided: HUDShapeViewer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDShapeViewer", function() { return HUDShapeViewer; });
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");








const copy = __webpack_require__(/*! clipboard-copy */ "./node_modules/clipboard-copy/index.js");

class HUDShapeViewer extends _base_hud_part__WEBPACK_IMPORTED_MODULE_5__["BaseHUDPart"] {
    createElements(parent) {
        this.background = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_HUD_ShapeViewer", ["ingameDialog"]);

        // DIALOG Inner / Wrapper
        this.dialogInner = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.background, null, ["dialogInner"]);
        this.title = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.dialogInner, null, ["title"], _translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.shapeViewer.title);
        this.closeButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.title, null, ["closeButton"]);
        this.trackClicks(this.closeButton, this.close);
        this.contentDiv = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.dialogInner, null, ["content"]);

        this.renderArea = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.contentDiv, null, ["renderArea"]);
        this.infoArea = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.contentDiv, null, ["infoArea"]);

        // Create button to copy the shape area
        this.copyButton = document.createElement("button");
        this.copyButton.classList.add("styledButton", "copyKey");
        this.copyButton.innerText = _translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.shapeViewer.copyKey;
        this.infoArea.appendChild(this.copyButton);
    }

    initialize() {
        this.root.hud.signals.viewShapeDetailsRequested.add(this.renderForShape, this);

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_6__["DynamicDomAttach"](this.root, this.background, {
            attachClass: "visible",
        });

        this.currentShapeKey = null;

        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_0__["InputReceiver"]("shape_viewer");
        this.keyActionMapper = new _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KeyActionMapper"](this.root, this.inputReciever);

        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].general.back).add(this.close, this);

        this.trackClicks(this.copyButton, this.onCopyKeyRequested);

        this.close();
    }

    isBlockingOverlay() {
        return this.visible;
    }

    /**
     * Called when the copying of a key was requested
     */
    onCopyKeyRequested() {
        if (this.currentShapeKey) {
            copy(this.currentShapeKey);
            this.close();
        }
    }

    /**
     * Closes the dialog
     */
    close() {
        this.visible = false;
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        this.update();
    }

    /**
     * Shows the viewer for a given definition
     * @param {ShapeDefinition} definition
     */
    renderForShape(definition) {
        this.visible = true;
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);

        Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["removeAllChildren"])(this.renderArea);

        this.currentShapeKey = definition.getHash();

        const layers = definition.layers;
        this.contentDiv.setAttribute("data-layers", layers.length);

        for (let i = layers.length - 1; i >= 0; --i) {
            const layerElem = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.renderArea, null, ["layer", "layer-" + i]);

            let fakeLayers = [];
            for (let k = 0; k < i; ++k) {
                fakeLayers.push([null, null, null, null]);
            }
            fakeLayers.push(layers[i]);

            const thisLayerOnly = new _shape_definition__WEBPACK_IMPORTED_MODULE_4__["ShapeDefinition"]({ layers: fakeLayers });
            const thisLayerCanvas = thisLayerOnly.generateAsCanvas(160);
            layerElem.appendChild(thisLayerCanvas);

            for (let quad = 0; quad < 4; ++quad) {
                const quadElem = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(layerElem, null, ["quad", "quad-" + quad]);

                const contents = layers[i][quad];
                if (contents) {
                    const colorLabelElem = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
                        quadElem,
                        null,
                        ["colorLabel"],
                        _translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.colors[contents.color]
                    );
                } else {
                    const emptyLabelElem = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
                        quadElem,
                        null,
                        ["emptyLabel"],
                        _translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.shapeViewer.empty
                    );
                }
            }
        }
    }

    update() {
        this.domAttach.update(this.visible);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/shop.js":
/*!***************************************!*\
  !*** ./src/js/game/hud/parts/shop.js ***!
  \***************************************/
/*! exports provided: HUDShop */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDShop", function() { return HUDShop; });
/* harmony import */ var _core_click_detector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/click_detector */ "./src/js/core/click_detector.js");
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");








class HUDShop extends _base_hud_part__WEBPACK_IMPORTED_MODULE_5__["BaseHUDPart"] {
    createElements(parent) {
        this.background = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(parent, "ingame_HUD_Shop", ["ingameDialog"]);

        // DIALOG Inner / Wrapper
        this.dialogInner = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.background, null, ["dialogInner"]);
        this.title = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.dialogInner, null, ["title"], _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.shop.title);
        this.closeButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.title, null, ["closeButton"]);
        this.trackClicks(this.closeButton, this.close);
        this.contentDiv = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.dialogInner, null, ["content"]);

        this.upgradeToElements = {};

        // Upgrades
        for (const upgradeId in this.root.gameMode.getUpgrades()) {
            const handle = {};
            handle.requireIndexToElement = [];

            // Wrapper
            handle.elem = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.contentDiv, null, ["upgrade"]);
            handle.elem.setAttribute("data-upgrade-id", upgradeId);

            // Title
            const title = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(handle.elem, null, ["title"], _translations__WEBPACK_IMPORTED_MODULE_3__["T"].shopUpgrades[upgradeId].name);

            // Title > Tier
            handle.elemTierLabel = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(title, null, ["tier"]);

            // Icon
            handle.icon = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(handle.elem, null, ["icon"]);
            handle.icon.setAttribute("data-icon", "upgrades/" + upgradeId + ".png");

            // Description
            handle.elemDescription = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(handle.elem, null, ["description"], "??");
            handle.elemRequirements = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(handle.elem, null, ["requirements"]);

            // Buy button
            handle.buyButton = document.createElement("button");
            handle.buyButton.classList.add("buy", "styledButton");
            handle.buyButton.innerText = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.shop.buttonUnlock;
            handle.elem.appendChild(handle.buyButton);

            this.trackClicks(handle.buyButton, () => this.tryUnlockNextTier(upgradeId));

            // Assign handle
            this.upgradeToElements[upgradeId] = handle;
        }
    }

    rerenderFull() {
        for (const upgradeId in this.upgradeToElements) {
            const handle = this.upgradeToElements[upgradeId];
            const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];

            const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
            const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
            const tierHandle = upgradeTiers[currentTier];

            // Set tier
            handle.elemTierLabel.innerText = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.shop.tier.replace(
                "<x>",
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["getRomanNumber"])(currentTier + 1)
            );

            handle.elemTierLabel.setAttribute("data-tier", currentTier);

            // Cleanup detectors
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
                const requiredHandle = handle.requireIndexToElement[i];
                requiredHandle.container.remove();
                requiredHandle.pinDetector.cleanup();
                requiredHandle.infoDetector.cleanup();
            }

            // Cleanup
            handle.requireIndexToElement = [];

            handle.elem.classList.toggle("maxLevel", !tierHandle);

            if (!tierHandle) {
                // Max level
                handle.elemDescription.innerText = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.shop.maximumLevel.replace(
                    "<currentMult>",
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(currentTierMultiplier)
                );
                continue;
            }

            // Set description
            handle.elemDescription.innerText = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].shopUpgrades[upgradeId].description
                .replace("<currentMult>", Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(currentTierMultiplier))
                .replace("<newMult>", Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(currentTierMultiplier + tierHandle.improvement));

            tierHandle.required.forEach(({ shape, amount }) => {
                const container = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(handle.elemRequirements, null, ["requirement"]);

                const shapeDef = this.root.shapeDefinitionMgr.getShapeFromShortKey(shape);
                const shapeCanvas = shapeDef.generateAsCanvas(120);
                shapeCanvas.classList.add();
                container.appendChild(shapeCanvas);

                const progressContainer = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(container, null, ["amount"]);
                const progressBar = document.createElement("label");
                progressBar.classList.add("progressBar");
                progressContainer.appendChild(progressBar);

                const progressLabel = document.createElement("label");
                progressContainer.appendChild(progressLabel);

                const pinButton = document.createElement("button");
                pinButton.classList.add("pin");
                container.appendChild(pinButton);

                const viewInfoButton = document.createElement("button");
                viewInfoButton.classList.add("showInfo");
                container.appendChild(viewInfoButton);

                const currentGoalShape = this.root.hubGoals.currentGoal.definition.getHash();
                if (shape === currentGoalShape) {
                    pinButton.classList.add("isGoal");
                } else if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
                    pinButton.classList.add("alreadyPinned");
                }

                const pinDetector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](pinButton, {
                    consumeEvents: true,
                    preventDefault: true,
                });
                pinDetector.click.add(() => {
                    if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
                        this.root.hud.signals.shapeUnpinRequested.dispatch(shape);
                        pinButton.classList.add("unpinned");
                        pinButton.classList.remove("pinned", "alreadyPinned");
                    } else {
                        this.root.hud.signals.shapePinRequested.dispatch(shapeDef);
                        pinButton.classList.add("pinned");
                        pinButton.classList.remove("unpinned");
                    }
                });

                const infoDetector = new _core_click_detector__WEBPACK_IMPORTED_MODULE_0__["ClickDetector"](viewInfoButton, {
                    consumeEvents: true,
                    preventDefault: true,
                });
                infoDetector.click.add(() =>
                    this.root.hud.signals.viewShapeDetailsRequested.dispatch(shapeDef)
                );

                handle.requireIndexToElement.push({
                    container,
                    progressLabel,
                    progressBar,
                    definition: shapeDef,
                    required: amount,
                    pinDetector,
                    infoDetector,
                });
            });
        }
    }

    renderCountsAndStatus() {
        for (const upgradeId in this.upgradeToElements) {
            const handle = this.upgradeToElements[upgradeId];
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
                const { progressLabel, progressBar, definition, required } = handle.requireIndexToElement[i];

                const haveAmount = this.root.hubGoals.getShapesStored(definition);
                const progress = Math.min(haveAmount / required, 1.0);

                progressLabel.innerText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(haveAmount) + " / " + Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(required);
                progressBar.style.width = progress * 100.0 + "%";
                progressBar.classList.toggle("complete", progress >= 1.0);
            }

            handle.buyButton.classList.toggle("buyable", this.root.hubGoals.canUnlockUpgrade(upgradeId));
        }
    }

    initialize() {
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_6__["DynamicDomAttach"](this.root, this.background, {
            attachClass: "visible",
        });

        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_1__["InputReceiver"]("shop");
        this.keyActionMapper = new _key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KeyActionMapper"](this.root, this.inputReciever);

        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KEYMAPPINGS"].general.back).add(this.close, this);
        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KEYMAPPINGS"].ingame.menuClose).add(this.close, this);
        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_4__["KEYMAPPINGS"].ingame.menuOpenShop).add(this.close, this);

        this.close();

        this.rerenderFull();
        this.root.signals.upgradePurchased.add(this.rerenderFull, this);
    }

    cleanup() {
        // Cleanup detectors
        for (const upgradeId in this.upgradeToElements) {
            const handle = this.upgradeToElements[upgradeId];
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
                const requiredHandle = handle.requireIndexToElement[i];
                requiredHandle.container.remove();
                requiredHandle.pinDetector.cleanup();
                requiredHandle.infoDetector.cleanup();
            }
            handle.requireIndexToElement = [];
        }
    }

    show() {
        this.visible = true;
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
        this.rerenderFull();
    }

    close() {
        this.visible = false;
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        this.update();
    }

    update() {
        this.domAttach.update(this.visible);
        if (this.visible) {
            this.renderCountsAndStatus();
        }
    }

    tryUnlockNextTier(upgradeId) {
        // Nothing
        this.root.hubGoals.tryUnlockUpgrade(upgradeId);
    }

    isBlockingOverlay() {
        return this.visible;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/standalone_advantages.js":
/*!********************************************************!*\
  !*** ./src/js/game/hud/parts/standalone_advantages.js ***!
  \********************************************************/
/*! exports provided: HUDStandaloneAdvantages */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDStandaloneAdvantages", function() { return HUDStandaloneAdvantages; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");







const showIntervalSeconds = 30 * 60;

class HUDStandaloneAdvantages extends _base_hud_part__WEBPACK_IMPORTED_MODULE_4__["BaseHUDPart"] {
    createElements(parent) {
        this.background = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(parent, "ingame_HUD_StandaloneAdvantages", ["ingameDialog"]);

        // DIALOG Inner / Wrapper
        this.dialogInner = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.background, null, ["dialogInner"]);
        this.title = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(this.dialogInner, null, ["title"], _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.standaloneAdvantages.title);
        this.contentDiv = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(
            this.dialogInner,
            null,
            ["content"],
            `
            <div class="points">
                ${Object.entries(_translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.standaloneAdvantages.points)
                    .map(
                        ([key, trans]) => `
                <div class="point ${key}">
                    <strong>${trans.title}</strong>
                    <p>${trans.desc}</p> 
                </div>`
                    )
                    .join("")}

            </div>

            <div class="lowerBar">
            <button class="steamLinkButton ${_core_config__WEBPACK_IMPORTED_MODULE_0__["A_B_TESTING_LINK_TYPE"]}"></button>
            <button class="otherCloseButton">${_translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.standaloneAdvantages.no_thanks}</button>
            </div>
        `
        );

        this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => {
            this.root.app.analytics.trackUiClick("standalone_advantage_visit_steam");
            this.root.app.platformWrapper.openExternalLink(
                _core_config__WEBPACK_IMPORTED_MODULE_0__["THIRDPARTY_URLS"].standaloneStorePage + "?ref=savs&prc=" + _core_config__WEBPACK_IMPORTED_MODULE_0__["A_B_TESTING_LINK_TYPE"]
            );
            this.close();
        });
        this.trackClicks(this.contentDiv.querySelector("button.otherCloseButton"), () => {
            this.root.app.analytics.trackUiClick("standalone_advantage_no_thanks");
            this.close();
        });
    }

    initialize() {
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__["DynamicDomAttach"](this.root, this.background, {
            attachClass: "visible",
        });

        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_1__["InputReceiver"]("standalone-advantages");
        this.close();

        this.lastShown = this.root.gameIsFresh ? this.root.time.now() : 0;
    }

    show() {
        this.lastShown = this.root.time.now();
        this.visible = true;
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
    }

    close() {
        this.visible = false;
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        this.update();
    }

    update() {
        if (!this.visible && this.root.time.now() - this.lastShown > showIntervalSeconds) {
            this.show();
        }

        this.domAttach.update(this.visible);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/statistics.js":
/*!*********************************************!*\
  !*** ./src/js/game/hud/parts/statistics.js ***!
  \*********************************************/
/*! exports provided: HUDStatistics */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDStatistics", function() { return HUDStatistics; });
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _production_analytics__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../production_analytics */ "./src/js/game/production_analytics.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _statistics_handle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./statistics_handle */ "./src/js/game/hud/parts/statistics_handle.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");









/**
 * Capitalizes the first letter
 * @param {string} str
 */
function capitalizeFirstLetter(str) {
    return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
}

class HUDStatistics extends _base_hud_part__WEBPACK_IMPORTED_MODULE_4__["BaseHUDPart"] {
    createElements(parent) {
        this.background = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_HUD_Statistics", ["ingameDialog"]);

        // DIALOG Inner / Wrapper
        this.dialogInner = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.background, null, ["dialogInner"]);
        this.title = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.dialogInner, null, ["title"], _translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.statistics.title);
        this.closeButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.title, null, ["closeButton"]);
        this.trackClicks(this.closeButton, this.close);

        this.filterHeader = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.dialogInner, null, ["filterHeader"]);
        this.sourceExplanation = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.dialogInner, null, ["sourceExplanation"]);

        this.filtersDataSource = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.filterHeader, null, ["filtersDataSource"]);
        this.filtersDisplayMode = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.filterHeader, null, ["filtersDisplayMode"]);

        const dataSources = [
            _production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].produced,
            _production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].delivered,
            _production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].stored,
        ];

        for (let i = 0; i < dataSources.length; ++i) {
            const dataSource = dataSources[i];
            const button = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeButton"])(
                this.filtersDataSource,
                ["mode" + capitalizeFirstLetter(dataSource)],
                _translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.statistics.dataSources[dataSource].title
            );
            this.trackClicks(button, () => this.setDataSource(dataSource));
        }

        const buttonIterateUnit = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeButton"])(this.filtersDisplayMode, ["displayIterateUnit"]);
        const buttonDisplaySorted = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeButton"])(this.filtersDisplayMode, ["displaySorted"]);
        const buttonDisplayDetailed = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeButton"])(this.filtersDisplayMode, ["displayDetailed"]);
        const buttonDisplayIcons = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeButton"])(this.filtersDisplayMode, ["displayIcons"]);

        this.trackClicks(buttonIterateUnit, () => this.iterateUnit());
        this.trackClicks(buttonDisplaySorted, () => this.toggleSorted());
        this.trackClicks(buttonDisplayIcons, () => this.setDisplayMode(_statistics_handle__WEBPACK_IMPORTED_MODULE_6__["enumDisplayMode"].icons));
        this.trackClicks(buttonDisplayDetailed, () => this.setDisplayMode(_statistics_handle__WEBPACK_IMPORTED_MODULE_6__["enumDisplayMode"].detailed));

        this.contentDiv = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(this.dialogInner, null, ["content"]);
    }

    /**
     * @param {enumAnalyticsDataSource} source
     */
    setDataSource(source) {
        this.dataSource = source;
        this.dialogInner.setAttribute("data-datasource", source);

        this.sourceExplanation.innerText = _translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.statistics.dataSources[source].description;
        if (this.visible) {
            this.rerenderFull();
        }
    }

    /**
     * @param {enumDisplayMode} mode
     */
    setDisplayMode(mode) {
        this.displayMode = mode;
        this.dialogInner.setAttribute("data-displaymode", mode);
        if (this.visible) {
            this.rerenderFull();
        }
    }

    /**
     * @param {boolean} sorted
     */
    setSorted(sorted) {
        this.sorted = sorted;
        this.dialogInner.setAttribute("data-sorted", String(sorted));
        if (this.visible) {
            this.rerenderFull();
        }
    }

    toggleSorted() {
        this.setSorted(!this.sorted);
    }

    /**
     * Chooses the next unit
     */
    iterateUnit() {
        const units = Array.from(Object.keys(_statistics_handle__WEBPACK_IMPORTED_MODULE_6__["statisticsUnitsSeconds"]));
        const newIndex = (units.indexOf(this.currentUnit) + 1) % units.length;
        this.currentUnit = units[newIndex];

        this.rerenderPartial();
    }

    initialize() {
        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__["DynamicDomAttach"](this.root, this.background, {
            attachClass: "visible",
        });

        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_0__["InputReceiver"]("statistics");
        this.keyActionMapper = new _key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KeyActionMapper"](this.root, this.inputReciever);

        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"].general.back).add(this.close, this);
        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"].ingame.menuClose).add(this.close, this);
        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"].ingame.menuOpenStats).add(this.close, this);

        /** @type {Object.<string, HUDShapeStatisticsHandle>} */
        this.activeHandles = {};

        this.currentUnit = "second";

        this.setSorted(true);
        this.setDataSource(_production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].produced);
        this.setDisplayMode(_statistics_handle__WEBPACK_IMPORTED_MODULE_6__["enumDisplayMode"].detailed);

        this.intersectionObserver = new IntersectionObserver(this.intersectionCallback.bind(this), {
            root: this.contentDiv,
        });

        this.lastFullRerender = 0;

        this.close();
        this.rerenderFull();
    }

    intersectionCallback(entries) {
        for (let i = 0; i < entries.length; ++i) {
            const entry = entries[i];
            const handle = this.activeHandles[entry.target.getAttribute("data-shape-key")];
            if (handle) {
                handle.setVisible(entry.intersectionRatio > 0);
            }
        }
    }

    isBlockingOverlay() {
        return this.visible;
    }

    show() {
        this.visible = true;
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
        this.rerenderFull();
        this.update();
    }

    close() {
        this.visible = false;
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        this.update();
    }

    update() {
        this.domAttach.update(this.visible);
        if (this.visible) {
            if (this.root.time.now() - this.lastFullRerender > 1) {
                this.lastFullRerender = this.root.time.now();
                this.lastPartialRerender = this.root.time.now();
                this.rerenderFull();
            }
            this.rerenderPartial();
        }
    }

    /**
     * Performs a partial rerender, only updating graphs and counts
     */
    rerenderPartial() {
        for (const key in this.activeHandles) {
            const handle = this.activeHandles[key];
            handle.update(this.displayMode, this.dataSource, this.currentUnit);
        }
    }

    /**
     * Performs a full rerender, regenerating everything
     */
    rerenderFull() {
        for (const key in this.activeHandles) {
            this.activeHandles[key].detach();
        }
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["removeAllChildren"])(this.contentDiv);

        // Now, attach new ones

        let entries = null;
        switch (this.dataSource) {
            case _production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].stored: {
                entries = Object.entries(this.root.hubGoals.storedShapes);
                break;
            }
            case _production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].produced:
            case _production_analytics__WEBPACK_IMPORTED_MODULE_3__["enumAnalyticsDataSource"].delivered: {
                entries = Object.entries(this.root.productionAnalytics.getCurrentShapeRates(this.dataSource));
                break;
            }
        }

        const pinnedShapes = this.root.hud.parts.pinnedShapes;

        entries.sort((a, b) => {
            const aPinned = pinnedShapes.isShapePinned(a[0]);
            const bPinned = pinnedShapes.isShapePinned(b[0]);

            if (aPinned !== bPinned) {
                return aPinned ? -1 : 1;
            }

            // Sort by shape key for some consistency
            if (!this.sorted || b[1] == a[1]) {
                return b[0].localeCompare(a[0]);
            }
            return b[1] - a[1];
        });

        let rendered = new Set();

        for (let i = 0; i < Math.min(entries.length, 200); ++i) {
            const entry = entries[i];
            const shapeKey = entry[0];

            let handle = this.activeHandles[shapeKey];
            if (!handle) {
                const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(shapeKey);
                handle = this.activeHandles[shapeKey] = new _statistics_handle__WEBPACK_IMPORTED_MODULE_6__["HUDShapeStatisticsHandle"](
                    this.root,
                    definition,
                    this.intersectionObserver
                );
            }

            rendered.add(shapeKey);
            handle.attach(this.contentDiv);
        }

        for (const key in this.activeHandles) {
            if (!rendered.has(key)) {
                this.activeHandles[key].destroy();
                delete this.activeHandles[key];
            }
        }

        if (entries.length === 0) {
            this.contentDiv.innerHTML = `
            <strong class="noEntries">${_translations__WEBPACK_IMPORTED_MODULE_7__["T"].ingame.statistics.noShapesProduced}</strong>`;
        }

        this.contentDiv.classList.toggle("hasEntries", entries.length > 0);
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/statistics_handle.js":
/*!****************************************************!*\
  !*** ./src/js/game/hud/parts/statistics_handle.js ***!
  \****************************************************/
/*! exports provided: enumDisplayMode, statisticsUnitsSeconds, HUDShapeStatisticsHandle */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumDisplayMode", function() { return enumDisplayMode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "statisticsUnitsSeconds", function() { return statisticsUnitsSeconds; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDShapeStatisticsHandle", function() { return HUDShapeStatisticsHandle; });
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _production_analytics__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../production_analytics */ "./src/js/game/production_analytics.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../root */ "./src/js/game/root.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../shape_definition */ "./src/js/game/shape_definition.js");








/** @enum {string} */
const enumDisplayMode = {
    icons: "icons",
    detailed: "detailed",
};

/**
 * Stores how many seconds one unit is
 * @type {Object<string, number>}
 */
const statisticsUnitsSeconds = {
    second: 1,
    minute: 60,
    hour: 3600,
};

/**
 * Simple wrapper for a shape definition within the shape statistics
 */
class HUDShapeStatisticsHandle {
    /**
     * @param {GameRoot} root
     * @param {ShapeDefinition} definition
     * @param {IntersectionObserver} intersectionObserver
     */
    constructor(root, definition, intersectionObserver) {
        this.definition = definition;
        this.root = root;
        this.intersectionObserver = intersectionObserver;

        this.visible = false;
    }

    initElement() {
        this.element = document.createElement("div");
        this.element.setAttribute("data-shape-key", this.definition.getHash());

        this.counter = document.createElement("span");
        this.counter.classList.add("counter");
        this.element.appendChild(this.counter);
    }

    /**
     * Sets whether the shape handle is visible currently
     * @param {boolean} visibility
     */
    setVisible(visibility) {
        if (visibility === this.visible) {
            return;
        }
        this.visible = visibility;
        if (visibility) {
            if (!this.shapeCanvas) {
                // Create elements
                this.shapeCanvas = this.definition.generateAsCanvas(100);
                this.shapeCanvas.classList.add("icon");
                this.element.appendChild(this.shapeCanvas);
            }
        } else {
            // Drop elements
            this.cleanupChildElements();
        }
    }

    /**
     *
     * @param {enumDisplayMode} displayMode
     * @param {enumAnalyticsDataSource} dataSource
     * @param {string} unit
     * @param {boolean=} forced
     */
    update(displayMode, dataSource, unit, forced = false) {
        if (!this.element) {
            return;
        }
        if (!this.visible && !forced) {
            return;
        }

        this.element.classList.toggle(
            "pinned",
            this.root.hud.parts.pinnedShapes.isShapePinned(this.definition.getHash())
        );

        switch (dataSource) {
            case _production_analytics__WEBPACK_IMPORTED_MODULE_4__["enumAnalyticsDataSource"].stored: {
                this.counter.innerText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(
                    this.root.hubGoals.storedShapes[this.definition.getHash()] || 0
                );
                break;
            }
            case _production_analytics__WEBPACK_IMPORTED_MODULE_4__["enumAnalyticsDataSource"].delivered:
            case _production_analytics__WEBPACK_IMPORTED_MODULE_4__["enumAnalyticsDataSource"].produced: {
                let rate =
                    this.root.productionAnalytics.getCurrentShapeRate(dataSource, this.definition) /
                    _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].analyticsSliceDurationSeconds;

                this.counter.innerText = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].ingame.statistics.shapesDisplayUnits[unit].replace(
                    "<shapes>",
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["formatBigNumber"])(rate * statisticsUnitsSeconds[unit])
                );
                break;
            }
        }

        if (displayMode === enumDisplayMode.detailed) {
            const graphDpi = _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].statisticsGraphDpi;

            const w = 270;
            const h = 40;

            if (!this.graphCanvas) {
                const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(w * graphDpi, h * graphDpi, {
                    smooth: true,
                    reusable: false,
                    label: "statgraph-" + this.definition.getHash(),
                });
                context.scale(graphDpi, graphDpi);
                canvas.classList.add("graph");
                this.graphCanvas = canvas;
                this.graphContext = context;
                this.element.appendChild(this.graphCanvas);
            }

            this.graphContext.clearRect(0, 0, w, h);

            this.graphContext.fillStyle = "#bee0db";
            this.graphContext.strokeStyle = "#66ccbc";
            this.graphContext.lineWidth = 1.5;

            const sliceWidth = w / (_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].statisticsGraphSlices - 1);

            let values = [];
            let maxValue = 1;

            for (let i = 0; i < _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].statisticsGraphSlices - 2; ++i) {
                const value = this.root.productionAnalytics.getPastShapeRate(
                    dataSource,
                    this.definition,
                    _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].statisticsGraphSlices - i - 2
                );
                if (value > maxValue) {
                    maxValue = value;
                }
                values.push(value);
            }

            this.graphContext.beginPath();
            this.graphContext.moveTo(0.75, h + 5);
            for (let i = 0; i < values.length; ++i) {
                const yValue = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["clamp"])((1 - values[i] / maxValue) * h, 0.75, h - 0.75);
                const x = i * sliceWidth;
                if (i === 0) {
                    this.graphContext.lineTo(0.75, yValue);
                }
                this.graphContext.lineTo(x, yValue);
                if (i === values.length - 1) {
                    this.graphContext.lineTo(w + 100, yValue);
                    this.graphContext.lineTo(w + 100, h + 5);
                }
            }

            this.graphContext.closePath();
            this.graphContext.stroke();
            this.graphContext.fill();
        } else {
            if (this.graphCanvas) {
                this.graphCanvas.remove();
                delete this.graphCanvas;
                delete this.graphContext;
            }
        }
    }

    /**
     * Attaches the handle
     * @param {HTMLElement} parent
     */
    attach(parent) {
        if (!this.element) {
            this.initElement();
        }
        if (this.element.parentElement !== parent) {
            parent.appendChild(this.element);
            this.intersectionObserver.observe(this.element);
        }
    }

    /**
     * Detaches the handle
     */
    detach() {
        if (this.element && this.element.parentElement) {
            this.element.parentElement.removeChild(this.element);
            this.intersectionObserver.unobserve(this.element);
        }
    }

    /**
     * Cleans up all child elements
     */
    cleanupChildElements() {
        if (this.shapeCanvas) {
            this.shapeCanvas.remove();
            delete this.shapeCanvas;
        }

        if (this.graphCanvas) {
            this.graphCanvas.remove();
            delete this.graphCanvas;
            delete this.graphContext;
        }
    }

    /**
     * Destroys the handle
     */
    destroy() {
        this.cleanupChildElements();
        if (this.element) {
            this.intersectionObserver.unobserve(this.element);
            this.element.remove();
            delete this.element;

            // Remove handle
            delete this.counter;
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/tutorial_hints.js":
/*!*************************************************!*\
  !*** ./src/js/game/hud/parts/tutorial_hints.js ***!
  \*************************************************/
/*! exports provided: HUDPartTutorialHints */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDPartTutorialHints", function() { return HUDPartTutorialHints; });
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _core_tracked_state__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/tracked_state */ "./src/js/core/tracked_state.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");








const tutorialVideos = [2, 3, 4, 5, 6, 7, 9, 10, 11];

class HUDPartTutorialHints extends _base_hud_part__WEBPACK_IMPORTED_MODULE_4__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["makeDiv"])(
            parent,
            "ingame_HUD_TutorialHints",
            [],
            `
        <div class="header">
            <span>${_translations__WEBPACK_IMPORTED_MODULE_6__["T"].ingame.tutorialHints.title}</span>
            <button class="styledButton toggleHint">
                <span class="show">${_translations__WEBPACK_IMPORTED_MODULE_6__["T"].ingame.tutorialHints.showHint}</span>
                <span class="hide">${_translations__WEBPACK_IMPORTED_MODULE_6__["T"].ingame.tutorialHints.hideHint}</span>
            </button>
        </div>

        <video autoplay muted loop class="fullscreenBackgroundVideo">
            <source type="video/webm">
        </video>
        `
        );

        this.videoElement = this.element.querySelector("video");
    }

    shouldPauseGame() {
        return this.enlarged;
    }

    initialize() {
        this.trackClicks(this.element.querySelector(".toggleHint"), this.toggleHintEnlarged);

        this.videoAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__["DynamicDomAttach"](this.root, this.videoElement, {
            timeToKeepSeconds: 0.3,
        });

        this.videoAttach.update(false);
        this.enlarged = false;

        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_0__["InputReceiver"]("tutorial_hints");
        this.keyActionMapper = new _key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KeyActionMapper"](this.root, this.inputReciever);
        this.keyActionMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_3__["KEYMAPPINGS"].general.back).add(this.close, this);

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_5__["DynamicDomAttach"](this.root, this.element);

        this.currentShownLevel = new _core_tracked_state__WEBPACK_IMPORTED_MODULE_1__["TrackedState"](this.updateVideoUrl, this);
    }

    updateVideoUrl(level) {
        if (tutorialVideos.indexOf(level) < 0) {
            this.videoElement.querySelector("source").setAttribute("src", "");
            this.videoElement.pause();
        } else {
            this.videoElement
                .querySelector("source")
                .setAttribute("src", "https://static.shapez.io/tutorial_videos/level_" + level + ".webm");
            this.videoElement.currentTime = 0;
            this.videoElement.load();
        }
    }

    close() {
        this.enlarged = false;
        this.element.classList.remove("enlarged", "noBlur");
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        this.update();
    }

    show() {
        this.root.app.analytics.trackUiClick("tutorial_hint_show");
        this.root.app.analytics.trackUiClick("tutorial_hint_show_lvl_" + this.root.hubGoals.level);
        this.element.classList.add("enlarged", "noBlur");
        this.enlarged = true;
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
        this.update();

        this.videoElement.currentTime = 0;
        this.videoElement.play();
    }

    update() {
        this.videoAttach.update(this.enlarged);

        this.currentShownLevel.set(this.root.hubGoals.level);

        const tutorialVisible = tutorialVideos.indexOf(this.root.hubGoals.level) >= 0;
        this.domAttach.update(tutorialVisible);
    }

    toggleHintEnlarged() {
        if (this.enlarged) {
            this.close();
        } else {
            this.show();
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/tutorial_video_offer.js":
/*!*******************************************************!*\
  !*** ./src/js/game/hud/parts/tutorial_video_offer.js ***!
  \*******************************************************/
/*! exports provided: HUDTutorialVideoOffer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDTutorialVideoOffer", function() { return HUDTutorialVideoOffer; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");




/**
 * Offers to open the tutorial video after completing a level
 */
class HUDTutorialVideoOffer extends _base_hud_part__WEBPACK_IMPORTED_MODULE_2__["BaseHUDPart"] {
    createElements() {}

    initialize() {
        this.root.hud.signals.unlockNotificationFinished.add(() => {
            const level = this.root.hubGoals.level;
            const tutorialVideoLink = _core_config__WEBPACK_IMPORTED_MODULE_0__["THIRDPARTY_URLS"].levelTutorialVideos[level];
            if (tutorialVideoLink) {
                const isForeign = this.root.app.settings.getLanguage() !== "en";
                const dialogData = isForeign
                    ? _translations__WEBPACK_IMPORTED_MODULE_1__["T"].dialogs.tutorialVideoAvailableForeignLanguage
                    : _translations__WEBPACK_IMPORTED_MODULE_1__["T"].dialogs.tutorialVideoAvailable;

                const { ok } = this.root.hud.parts.dialogs.showInfo(dialogData.title, dialogData.desc, [
                    "cancel:bad",
                    "ok:good",
                ]);

                this.root.app.analytics.trackUiClick("ingame_video_link_show_" + level);
                ok.add(() => {
                    this.root.app.platformWrapper.openExternalLink(tutorialVideoLink);
                    this.root.app.analytics.trackUiClick("ingame_video_link_open_" + level);
                });
            }
        });
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/unlock_notification.js":
/*!******************************************************!*\
  !*** ./src/js/game/hud/parts/unlock_notification.js ***!
  \******************************************************/
/*! exports provided: HUDUnlockNotification */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDUnlockNotification", function() { return HUDUnlockNotification; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_input_receiver__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/input_receiver */ "./src/js/core/input_receiver.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _tutorial_goals_mappings__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../tutorial_goals_mappings */ "./src/js/game/tutorial_goals_mappings.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _notifications__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./notifications */ "./src/js/game/hud/parts/notifications.js");













class HUDUnlockNotification extends _base_hud_part__WEBPACK_IMPORTED_MODULE_9__["BaseHUDPart"] {
    initialize() {
        this.visible = false;

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_10__["DynamicDomAttach"](this.root, this.element, {
            timeToKeepSeconds: 0,
        });

        if (true) {
            this.root.signals.storyGoalCompleted.add(this.showForLevel, this);
        }

        this.buttonShowTimeout = null;
    }

    createElements(parent) {
        this.inputReciever = new _core_input_receiver__WEBPACK_IMPORTED_MODULE_2__["InputReceiver"]("unlock-notification");

        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(parent, "ingame_HUD_UnlockNotification", ["noBlur"]);

        const dialog = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(this.element, null, ["dialog"]);

        this.elemTitle = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(dialog, null, ["title"]);
        this.elemSubTitle = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(dialog, null, ["subTitle"], _translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.levelCompleteNotification.completed);

        this.elemContents = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["makeDiv"])(dialog, null, ["contents"]);

        this.btnClose = document.createElement("button");
        this.btnClose.classList.add("close", "styledButton");
        this.btnClose.innerText = _translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.levelCompleteNotification.buttonNextLevel;
        dialog.appendChild(this.btnClose);

        this.trackClicks(this.btnClose, this.requestClose);
    }

    /**
     * @param {number} level
     * @param {enumHubGoalRewards} reward
     */
    showForLevel(level, reward) {
        this.root.soundProxy.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_4__["SOUNDS"].levelComplete);

        const levels = this.root.gameMode.getLevelDefinitions();
        // Don't use getIsFreeplay() because we want the freeplay level up to show
        if (level > levels.length) {
            this.root.hud.signals.notification.dispatch(
                _translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
                _notifications__WEBPACK_IMPORTED_MODULE_11__["enumNotificationType"].success
            );
            return;
        }

        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
        this.elemTitle.innerText = _translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.levelCompleteNotification.levelTitle.replace(
            "<level>",
            ("" + level).padStart(2, "0")
        );

        const rewardName = _translations__WEBPACK_IMPORTED_MODULE_5__["T"].storyRewards[reward].title;

        let html = `
        <div class="rewardName">
            ${_translations__WEBPACK_IMPORTED_MODULE_5__["T"].ingame.levelCompleteNotification.unlockText.replace("<reward>", rewardName)}
        </div>
        
        <div class="rewardDesc">
            ${_translations__WEBPACK_IMPORTED_MODULE_5__["T"].storyRewards[reward].desc}
        </div>

        `;

        html += "<div class='images'>";
        const gained = _tutorial_goals_mappings__WEBPACK_IMPORTED_MODULE_8__["enumHubGoalRewardsToContentUnlocked"][reward];
        if (gained) {
            gained.forEach(([metaBuildingClass, variant]) => {
                const metaBuilding = _core_global_registries__WEBPACK_IMPORTED_MODULE_1__["gMetaBuildingRegistry"].findByClass(metaBuildingClass);
                html += `<div class="buildingExplanation" data-icon="building_tutorials/${
                    metaBuilding.getId() + (variant === _meta_building__WEBPACK_IMPORTED_MODULE_6__["defaultBuildingVariant"] ? "" : "-" + variant)
                }.png"></div>`;
            });
        }
        html += "</div>";

        this.elemContents.innerHTML = html;
        this.visible = true;

        if (this.buttonShowTimeout) {
            clearTimeout(this.buttonShowTimeout);
        }

        this.element.querySelector("button.close").classList.remove("unlocked");

        if (this.root.app.settings.getAllSettings().offerHints) {
            this.buttonShowTimeout = setTimeout(
                () => this.element.querySelector("button.close").classList.add("unlocked"),
                 false ? undefined : 5000
            );
        } else {
            this.element.querySelector("button.close").classList.add("unlocked");
        }
    }

    cleanup() {
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        if (this.buttonShowTimeout) {
            clearTimeout(this.buttonShowTimeout);
            this.buttonShowTimeout = null;
        }
    }

    isBlockingOverlay() {
        return this.visible;
    }

    requestClose() {
        this.root.app.adProvider.showVideoAd().then(() => {
            this.close();

            this.root.hud.signals.unlockNotificationFinished.dispatch();

            if (!this.root.app.settings.getAllSettings().offerHints) {
                return;
            }

            if (this.root.hubGoals.level === 3) {
                const { showUpgrades } = this.root.hud.parts.dialogs.showInfo(
                    _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.upgradesIntroduction.title,
                    _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.upgradesIntroduction.desc,
                    ["showUpgrades:good:timeout"]
                );
                showUpgrades.add(() => this.root.hud.parts.shop.show());
            }

            if (this.root.hubGoals.level === 5) {
                const { showKeybindings } = this.root.hud.parts.dialogs.showInfo(
                    _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.keybindingsIntroduction.title,
                    _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.keybindingsIntroduction.desc,
                    ["showKeybindings:misc", "ok:good:timeout"]
                );
                showKeybindings.add(() => this.root.gameState.goToKeybindings());
            }
        });
    }

    close() {
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
        if (this.buttonShowTimeout) {
            clearTimeout(this.buttonShowTimeout);
            this.buttonShowTimeout = null;
        }
        this.visible = false;
    }

    update() {
        this.domAttach.update(this.visible);
        if (!this.visible && this.buttonShowTimeout) {
            clearTimeout(this.buttonShowTimeout);
            this.buttonShowTimeout = null;
        }
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/vignette_overlay.js":
/*!***************************************************!*\
  !*** ./src/js/game/hud/parts/vignette_overlay.js ***!
  \***************************************************/
/*! exports provided: HUDVignetteOverlay */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDVignetteOverlay", function() { return HUDVignetteOverlay; });
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");



class HUDVignetteOverlay extends _base_hud_part__WEBPACK_IMPORTED_MODULE_0__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(parent, "ingame_VignetteOverlay");
    }

    initialize() {}
}


/***/ }),

/***/ "./src/js/game/hud/parts/watermark.js":
/*!********************************************!*\
  !*** ./src/js/game/hud/parts/watermark.js ***!
  \********************************************/
/*! exports provided: HUDWatermark */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDWatermark", function() { return HUDWatermark; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");






const watermarkShowIntervalSeconds =  false ? undefined : 7 * 60;
const watermarkShowDuration = 5;

class HUDWatermark extends _base_hud_part__WEBPACK_IMPORTED_MODULE_3__["BaseHUDPart"] {
    createElements(parent) {
        this.element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
            parent,
            "ingame_HUD_Watermark",
            [],
            `
            <strong>${_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.watermark.title}</strong>
            <p>${_translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.watermark.desc}</p>
        `
        );

        this.linkElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["makeDiv"])(
            parent,
            "ingame_HUD_WatermarkClicker",
            [],
            _translations__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.watermark.get_on_steam
        );
        this.trackClicks(this.linkElement, () => {
            this.root.app.analytics.trackUiClick("watermark_click_2_direct");
            this.root.app.platformWrapper.openExternalLink(_core_config__WEBPACK_IMPORTED_MODULE_0__["THIRDPARTY_URLS"].standaloneStorePage + "?ref=wtmd");
        });
    }

    initialize() {
        this.trackClicks(this.element, this.onWatermarkClick);

        this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_4__["DynamicDomAttach"](this.root, this.element, {
            attachClass: "visible",
            timeToKeepSeconds: 0.5,
        });
    }

    update() {
        this.domAttach.update(
            this.root.time.realtimeNow() % watermarkShowIntervalSeconds < watermarkShowDuration
        );
    }

    onWatermarkClick() {
        this.root.app.analytics.trackUiClick("watermark_click_2_new");
        this.root.hud.parts.standaloneAdvantages.show();
    }

    /**
     *
     * @param {import("../../../core/draw_utils").DrawParameters} parameters
     */
    drawOverlays(parameters) {
        const w = this.root.gameWidth;

        parameters.context.fillStyle = "rgba(230, 230, 230, 0.9)";
        parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 40 + "px GameFont";
        parameters.context.textAlign = "center";
        parameters.context.fillText(
            _translations__WEBPACK_IMPORTED_MODULE_2__["T"].demoBanners.title.toUpperCase(),
            w / 2,
            this.root.app.getEffectiveUiScale() * 50
        );

        parameters.context.textAlign = "left";
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/waypoints.js":
/*!********************************************!*\
  !*** ./src/js/game/hud/parts/waypoints.js ***!
  \********************************************/
/*! exports provided: HUDWaypoints */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDWaypoints", function() { return HUDWaypoints; });
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/modal_dialog_elements */ "./src/js/core/modal_dialog_elements.js");
/* harmony import */ var _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../core/modal_dialog_forms */ "./src/js/core/modal_dialog_forms.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../../translations */ "./src/js/translations.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _buildings_hub__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../buildings/hub */ "./src/js/game/buildings/hub.js");
/* harmony import */ var _camera__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../../camera */ "./src/js/game/camera.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../../shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../dynamic_dom_attach */ "./src/js/game/hud/dynamic_dom_attach.js");
/* harmony import */ var _notifications__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./notifications */ "./src/js/game/hud/parts/notifications.js");





















/** @typedef {{
 *   label: string | null,
 *   center: { x: number, y: number },
 *   zoomLevel: number,
 *   layer: Layer,
 * }} Waypoint */

/**
 * Used when a shape icon is rendered instead
 */
const MAX_LABEL_LENGTH = 71;

class HUDWaypoints extends _base_hud_part__WEBPACK_IMPORTED_MODULE_17__["BaseHUDPart"] {
    /**
     * Creates the overview of waypoints
     * @param {HTMLElement} parent
     */
    createElements(parent) {
        // Create the helper box on the lower right when zooming out
        if (this.root.app.settings.getAllSettings().offerHints) {
            this.hintElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["makeDiv"])(
                parent,
                "ingame_HUD_Waypoints_Hint",
                [],
                `
            <strong class='title'>${_translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.waypoints.waypoints}</strong>
            <span class='desc'>${_translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.waypoints.description.replace(
                "<keybinding>",
                `<code class='keybinding'>${this.root.keyMapper
                    .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_15__["KEYMAPPINGS"].navigation.createMarker)
                    .getKeyCodeString()}</code>`
            )}</span>
        `
            );
        }

        // Create the waypoint list on the upper right
        this.waypointsListElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["makeDiv"])(parent, "ingame_HUD_Waypoints", [], "Waypoints");
    }

    /**
     * Serializes the waypoints
     */
    serialize() {
        return {
            waypoints: this.waypoints,
        };
    }

    /**
     * Deserializes the waypoints
     * @param {{waypoints: Array<Waypoint>}} data
     */
    deserialize(data) {
        if (!data || !data.waypoints || !Array.isArray(data.waypoints)) {
            return "Invalid waypoints data";
        }
        this.waypoints = data.waypoints;
        this.rerenderWaypointList();
    }

    /**
     * Initializes everything
     */
    initialize() {
        // Cache the sprite for the waypoints

        this.waypointSprites = {
            regular: _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/misc/waypoint.png"),
            wires: _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/misc/waypoint_wires.png"),
        };

        this.directionIndicatorSprite = _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/misc/hub_direction_indicator.png");

        /** @type {Array<Waypoint>}
         */
        this.waypoints = [
            {
                label: null,
                center: { x: 0, y: 0 },
                zoomLevel: 3,
                layer: _core_global_registries__WEBPACK_IMPORTED_MODULE_3__["gMetaBuildingRegistry"].findByClass(_buildings_hub__WEBPACK_IMPORTED_MODULE_13__["MetaHubBuilding"]).getLayer(),
            },
        ];

        // Create a buffer we can use to measure text
        this.dummyBuffer = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(1, 1, {
            reusable: false,
            label: "waypoints-measure-canvas",
        })[1];

        // Dynamically attach/detach the lower right hint in the map overview
        if (this.hintElement) {
            this.domAttach = new _dynamic_dom_attach__WEBPACK_IMPORTED_MODULE_18__["DynamicDomAttach"](this.root, this.hintElement);
        }

        // Catch mouse and key events
        this.root.camera.downPreHandler.add(this.onMouseDown, this);
        this.root.keyMapper
            .getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_15__["KEYMAPPINGS"].navigation.createMarker)
            .add(() => this.requestSaveMarker({}));

        /**
         * Stores at how much opacity the markers should be rendered on the map.
         * This is interpolated over multiple frames so we have some sort of fade effect
         */
        this.currentMarkerOpacity = 1;
        this.currentCompassOpacity = 0;

        // Create buffer which is used to indicate the hub direction
        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(48, 48, {
            smooth: true,
            reusable: false,
            label: "waypoints-compass",
        });
        this.compassBuffer = { canvas, context };

        /**
         * Stores a cache from a shape short key to its canvas representation
         */
        this.cachedKeyToCanvas = {};

        /**
         * Store cached text widths
         * @type {Object<string, number>}
         */
        this.cachedTextWidths = {};

        // Initial render
        this.rerenderWaypointList();
    }

    /**
     * Returns how long a text will be rendered
     * @param {string} text
     * @returns {number}
     */
    getTextWidth(text) {
        if (this.cachedTextWidths[text]) {
            return this.cachedTextWidths[text];
        }

        this.dummyBuffer.font = "bold " + this.getTextScale() + "px GameFont";
        return (this.cachedTextWidths[text] = this.dummyBuffer.measureText(text).width);
    }

    /**
     * Returns how big the text should be rendered
     */
    getTextScale() {
        return this.getWaypointUiScale() * 12;
    }

    /**
     * Returns the scale for rendering waypoints
     */
    getWaypointUiScale() {
        return this.root.app.getEffectiveUiScale();
    }

    /**
     * Re-renders the waypoint list to account for changes
     */
    rerenderWaypointList() {
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["removeAllChildren"])(this.waypointsListElement);
        this.cleanupClickDetectors();

        for (let i = 0; i < this.waypoints.length; ++i) {
            const waypoint = this.waypoints[i];
            const label = this.getWaypointLabel(waypoint);

            const element = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["makeDiv"])(this.waypointsListElement, null, [
                "waypoint",
                "layer--" + waypoint.layer,
            ]);

            if (_shape_definition__WEBPACK_IMPORTED_MODULE_16__["ShapeDefinition"].isValidShortKey(label)) {
                const canvas = this.getWaypointCanvas(waypoint);
                /**
                 * Create a clone of the cached canvas, as calling appendElement when a canvas is
                 * already in the document will move the existing canvas to the new position.
                 */
                const [newCanvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(48, 48, {
                    smooth: true,
                    label: label + "-waypoint-" + i,
                });
                context.drawImage(canvas, 0, 0);
                element.appendChild(newCanvas);
                element.classList.add("shapeIcon");
            } else {
                element.innerText = label;
            }

            if (this.isWaypointDeletable(waypoint)) {
                const editButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["makeDiv"])(element, null, ["editButton"]);
                this.trackClicks(editButton, () => this.requestSaveMarker({ waypoint }));
            }

            if (!waypoint.label) {
                // This must be the hub label
                element.classList.add("hub");
                element.insertBefore(this.compassBuffer.canvas, element.childNodes[0]);
            }

            this.trackClicks(element, () => this.moveToWaypoint(waypoint), {
                targetOnly: true,
            });
        }
    }

    /**
     * Moves the camera to a given waypoint
     * @param {Waypoint} waypoint
     */
    moveToWaypoint(waypoint) {
        this.root.currentLayer = waypoint.layer;
        this.root.camera.setDesiredCenter(new _core_vector__WEBPACK_IMPORTED_MODULE_10__["Vector"](waypoint.center.x, waypoint.center.y));
        this.root.camera.setDesiredZoom(waypoint.zoomLevel);
    }

    /**
     * Deletes a waypoint from the list
     * @param {Waypoint} waypoint
     */
    deleteWaypoint(waypoint) {
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["arrayDeleteValue"])(this.waypoints, waypoint);
        this.rerenderWaypointList();
    }

    /**
     * Gets the canvas for a given waypoint
     * @param {Waypoint} waypoint
     * @returns {HTMLCanvasElement}
     */
    getWaypointCanvas(waypoint) {
        const key = waypoint.label;
        if (this.cachedKeyToCanvas[key]) {
            return this.cachedKeyToCanvas[key];
        }

        window.assert(_shape_definition__WEBPACK_IMPORTED_MODULE_16__["ShapeDefinition"].isValidShortKey(key), "Invalid short key: " + key);
        const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
        const preRendered = definition.generateAsCanvas(48);
        return (this.cachedKeyToCanvas[key] = preRendered);
    }

    /**
     * Requests to save a marker at the current camera position. If worldPos is set,
     * uses that position instead.
     * @param {object} param0
     * @param {Vector=} param0.worldPos Override the world pos, otherwise it is the camera position
     * @param {Waypoint=} param0.waypoint Waypoint to be edited. If omitted, create new
     */
    requestSaveMarker({ worldPos = null, waypoint = null }) {
        // Construct dialog with input field
        const markerNameInput = new _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_6__["FormElementInput"]({
            id: "markerName",
            label: null,
            placeholder: "",
            defaultValue: waypoint ? waypoint.label : "",
            validator: val =>
                val.length > 0 && (val.length < MAX_LABEL_LENGTH || _shape_definition__WEBPACK_IMPORTED_MODULE_16__["ShapeDefinition"].isValidShortKey(val)),
        });
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_5__["DialogWithForm"]({
            app: this.root.app,
            title: waypoint ? _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.createMarker.titleEdit : _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.createMarker.title,
            desc: Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["fillInLinkIntoTranslation"])(_translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.createMarker.desc, _core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].shapeViewer),
            formElements: [markerNameInput],
            buttons: waypoint ? ["delete:bad", "cancel", "ok:good"] : ["cancel", "ok:good"],
        });
        this.root.hud.parts.dialogs.internalShowDialog(dialog);

        // Edit marker
        if (waypoint) {
            dialog.buttonSignals.ok.add(() => {
                // Actually rename the waypoint
                this.renameWaypoint(waypoint, markerNameInput.getValue());
            });
            dialog.buttonSignals.delete.add(() => {
                // Actually delete the waypoint
                this.deleteWaypoint(waypoint);
            });
        } else {
            // Compute where to create the marker
            const center = worldPos || this.root.camera.center;

            dialog.buttonSignals.ok.add(() => {
                // Show info that you can have only N markers in the demo,
                // actually show this *after* entering the name so you want the
                // standalone even more (I'm evil :P)
                if (this.waypoints.length > this.root.app.restrictionMgr.getMaximumWaypoints()) {
                    this.root.hud.parts.dialogs.showFeatureRestrictionInfo(
                        "",
                        _translations__WEBPACK_IMPORTED_MODULE_11__["T"].dialogs.markerDemoLimit.desc
                    );
                    return;
                }

                // Actually create the waypoint
                this.addWaypoint(markerNameInput.getValue(), center);
            });
        }
    }

    /**
     * Adds a new waypoint at the given location with the given label
     * @param {string} label
     * @param {Vector} position
     */
    addWaypoint(label, position) {
        this.waypoints.push({
            label,
            center: { x: position.x, y: position.y },
            zoomLevel: this.root.camera.zoomLevel,
            layer: this.root.currentLayer,
        });

        this.sortWaypoints();

        // Show notification about creation
        this.root.hud.signals.notification.dispatch(
            _translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.waypoints.creationSuccessNotification,
            _notifications__WEBPACK_IMPORTED_MODULE_19__["enumNotificationType"].success
        );

        // Re-render the list and thus add it
        this.rerenderWaypointList();
    }

    /**
     * Renames a waypoint with the given label
     * @param {Waypoint} waypoint
     * @param {string} label
     */
    renameWaypoint(waypoint, label) {
        waypoint.label = label;

        this.sortWaypoints();

        // Show notification about renamed
        this.root.hud.signals.notification.dispatch(
            _translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.waypoints.creationSuccessNotification,
            _notifications__WEBPACK_IMPORTED_MODULE_19__["enumNotificationType"].success
        );

        // Re-render the list and thus add it
        this.rerenderWaypointList();
    }

    /**
     * Called every frame to update stuff
     */
    update() {
        if (this.domAttach) {
            this.domAttach.update(this.root.camera.getIsMapOverlayActive());
        }
    }

    /**
     * Sort waypoints by name
     */
    sortWaypoints() {
        this.waypoints.sort((a, b) => {
            if (!a.label) {
                return -1;
            }
            if (!b.label) {
                return 1;
            }
            return this.getWaypointLabel(a)
                .padEnd(MAX_LABEL_LENGTH, "0")
                .localeCompare(this.getWaypointLabel(b).padEnd(MAX_LABEL_LENGTH, "0"));
        });
    }

    /**
     * Returns the label for a given waypoint
     * @param {Waypoint} waypoint
     * @returns {string}
     */
    getWaypointLabel(waypoint) {
        return waypoint.label || _translations__WEBPACK_IMPORTED_MODULE_11__["T"].ingame.waypoints.hub;
    }

    /**
     * Returns if a waypoint is deletable
     * @param {Waypoint} waypoint
     * @returns {boolean}
     */
    isWaypointDeletable(waypoint) {
        return waypoint.label !== null;
    }

    /**
     * Returns the screen space bounds of the given waypoint or null
     * if it couldn't be determined. Also returns wheter its a shape or not
     * @param {Waypoint} waypoint
     * @return {{
     *   screenBounds: Rectangle
     *   item: BaseItem|null,
     *   text: string
     * }}
     */
    getWaypointScreenParams(waypoint) {
        if (!this.root.camera.getIsMapOverlayActive()) {
            return null;
        }

        // Find parameters
        const scale = this.getWaypointUiScale();
        const screenPos = this.root.camera.worldToScreen(new _core_vector__WEBPACK_IMPORTED_MODULE_10__["Vector"](waypoint.center.x, waypoint.center.y));

        // Distinguish between text and item waypoints -> Figure out parameters
        const originalLabel = this.getWaypointLabel(waypoint);
        let text, item, textWidth;

        if (_shape_definition__WEBPACK_IMPORTED_MODULE_16__["ShapeDefinition"].isValidShortKey(originalLabel)) {
            // If the label is actually a key, render the shape icon
            item = this.root.shapeDefinitionMgr.getShapeItemFromShortKey(originalLabel);
            textWidth = 40;
        } else {
            // Otherwise render a regular waypoint
            text = originalLabel;
            textWidth = this.getTextWidth(text);
        }

        return {
            screenBounds: new _core_rectangle__WEBPACK_IMPORTED_MODULE_7__["Rectangle"](
                screenPos.x - 7 * scale,
                screenPos.y - 12 * scale,
                15 * scale + textWidth,
                15 * scale
            ),
            item,
            text,
        };
    }

    /**
     * Finds the currently intersected waypoint on the map overview under
     * the cursor.
     *
     * @returns {Waypoint | null}
     */
    findCurrentIntersectedWaypoint() {
        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            return;
        }

        for (let i = 0; i < this.waypoints.length; ++i) {
            const waypoint = this.waypoints[i];
            const params = this.getWaypointScreenParams(waypoint);
            if (params && params.screenBounds.containsPoint(mousePos.x, mousePos.y)) {
                return waypoint;
            }
        }
    }

    /**
     * Mouse-Down handler
     * @param {Vector} pos
     * @param {enumMouseButton} button
     */
    onMouseDown(pos, button) {
        const waypoint = this.findCurrentIntersectedWaypoint();
        if (waypoint) {
            if (button === _camera__WEBPACK_IMPORTED_MODULE_14__["enumMouseButton"].left) {
                this.root.soundProxy.playUiClick();
                this.moveToWaypoint(waypoint);
            } else if (button === _camera__WEBPACK_IMPORTED_MODULE_14__["enumMouseButton"].right) {
                if (this.isWaypointDeletable(waypoint)) {
                    this.root.soundProxy.playUiClick();
                    this.requestSaveMarker({ waypoint });
                } else {
                    this.root.soundProxy.playUiError();
                }
            }

            return _core_signal__WEBPACK_IMPORTED_MODULE_8__["STOP_PROPAGATION"];
        } else {
            // Allow right click to create a marker
            if (button === _camera__WEBPACK_IMPORTED_MODULE_14__["enumMouseButton"].right) {
                if (this.root.camera.getIsMapOverlayActive()) {
                    const worldPos = this.root.camera.screenToWorld(pos);
                    this.requestSaveMarker({ worldPos });
                    return _core_signal__WEBPACK_IMPORTED_MODULE_8__["STOP_PROPAGATION"];
                }
            }
        }
    }

    /**
     * Rerenders the compass
     */
    rerenderWaypointsCompass() {
        const dims = 48;
        const indicatorSize = 30;
        const cameraPos = this.root.camera.center;

        const context = this.compassBuffer.context;
        context.clearRect(0, 0, dims, dims);

        const distanceToHub = cameraPos.length();
        const compassVisible = distanceToHub > (10 * _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize) / this.root.camera.zoomLevel;
        const targetCompassAlpha = compassVisible ? 1 : 0;

        // Fade the compas in / out
        this.currentCompassOpacity = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["lerp"])(this.currentCompassOpacity, targetCompassAlpha, 0.08);

        // Render the compass
        if (this.currentCompassOpacity > 0.01) {
            context.globalAlpha = this.currentCompassOpacity;
            const angle = cameraPos.angle() + Math.radians(45) + Math.PI / 2;
            context.translate(dims / 2, dims / 2);
            context.rotate(angle);
            this.directionIndicatorSprite.drawCentered(context, 0, 0, indicatorSize);
            context.rotate(-angle);
            context.translate(-dims / 2, -dims / 2);
            context.globalAlpha = 1;
        }

        // Render the regualr icon
        const iconOpacity = 1 - this.currentCompassOpacity;
        if (iconOpacity > 0.01) {
            context.globalAlpha = iconOpacity;
            this.waypointSprites.regular.drawCentered(context, dims / 2, dims / 2, dims * 0.7);
            context.globalAlpha = 1;
        }
    }

    /**
     * Draws the waypoints on the map
     * @param {DrawParameters} parameters
     */
    drawOverlays(parameters) {
        const mousePos = this.root.app.mousePosition;
        const desiredOpacity = this.root.camera.getIsMapOverlayActive() ? 1 : 0;
        this.currentMarkerOpacity = Object(_core_utils__WEBPACK_IMPORTED_MODULE_9__["lerp"])(this.currentMarkerOpacity, desiredOpacity, 0.08);

        this.rerenderWaypointsCompass();

        // Don't render with low opacity
        if (this.currentMarkerOpacity < 0.01) {
            return;
        }

        // Determine rendering scale
        const scale = this.getWaypointUiScale();

        // Set the font size
        const textSize = this.getTextScale();
        parameters.context.font = "bold " + textSize + "px GameFont";
        parameters.context.textBaseline = "middle";

        // Loop over all waypoints
        for (let i = 0; i < this.waypoints.length; ++i) {
            const waypoint = this.waypoints[i];

            const waypointData = this.getWaypointScreenParams(waypoint);
            if (!waypointData) {
                // Not relevant
                continue;
            }

            if (!parameters.visibleRect.containsRect(waypointData.screenBounds)) {
                // Out of screen
                continue;
            }

            const bounds = waypointData.screenBounds;
            const contentPaddingX = 7 * scale;
            const isSelected = mousePos && bounds.containsPoint(mousePos.x, mousePos.y);

            // Render the background rectangle
            parameters.context.globalAlpha = this.currentMarkerOpacity * (isSelected ? 1 : 0.7);
            parameters.context.fillStyle = "rgba(255, 255, 255, 0.7)";
            parameters.context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h);

            // Render the text
            if (waypointData.item) {
                const canvas = this.getWaypointCanvas(waypoint);
                const itemSize = 14 * scale;
                parameters.context.drawImage(
                    canvas,
                    bounds.x + contentPaddingX + 6 * scale,
                    bounds.y + bounds.h / 2 - itemSize / 2,
                    itemSize,
                    itemSize
                );
            } else if (waypointData.text) {
                // Render the text
                parameters.context.fillStyle = "#000";
                parameters.context.textBaseline = "middle";
                parameters.context.fillText(
                    waypointData.text,
                    bounds.x + contentPaddingX + 6 * scale,
                    bounds.y + bounds.h / 2
                );
                parameters.context.textBaseline = "alphabetic";
            } else {
                window.assert(false, "Waypoint has no item and text");
            }

            // Render the small icon on the left
            this.waypointSprites[waypoint.layer].drawCentered(
                parameters.context,
                bounds.x + contentPaddingX,
                bounds.y + bounds.h / 2,
                bounds.h * 0.6
            );
        }

        parameters.context.textBaseline = "alphabetic";
        parameters.context.globalAlpha = 1;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/wire_info.js":
/*!********************************************!*\
  !*** ./src/js/game/hud/parts/wire_info.js ***!
  \********************************************/
/*! exports provided: HUDWireInfo */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDWireInfo", function() { return HUDWireInfo; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _systems_wire__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../systems/wire */ "./src/js/game/systems/wire.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../theme */ "./src/js/game/theme.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../core/loader */ "./src/js/core/loader.js");







class HUDWireInfo extends _base_hud_part__WEBPACK_IMPORTED_MODULE_4__["BaseHUDPart"] {
    initialize() {
        this.spriteEmpty = _core_loader__WEBPACK_IMPORTED_MODULE_5__["Loader"].getSprite("sprites/wires/network_empty.png");
        this.spriteConflict = _core_loader__WEBPACK_IMPORTED_MODULE_5__["Loader"].getSprite("sprites/wires/network_conflict.png");
    }

    /**
     *
     * @param {import("../../../core/draw_utils").DrawParameters} parameters
     */
    drawOverlays(parameters) {
        if (this.root.currentLayer !== "wires") {
            // Not in the wires layer
            return;
        }

        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            // No mouse
            return;
        }

        const worldPos = this.root.camera.screenToWorld(mousePos);
        const tile = worldPos.toTileSpace();
        const entity = this.root.map.getLayerContentXY(tile.x, tile.y, "wires");

        if (!entity) {
            // No entity
            return;
        }

        if (
            !this.root.camera.getIsMapOverlayActive() &&
            !this.root.logic.getIsEntityIntersectedWithMatrix(entity, worldPos)
        ) {
            // Detailed intersection check
            return;
        }

        const networks = this.root.logic.getEntityWireNetworks(entity, tile);
        if (networks === null) {
            // This entity will never be able to be connected
            return;
        }

        if (networks.length === 0) {
            // No network at all
            return;
        }

        for (let i = 0; i < networks.length; ++i) {
            const network = networks[i];
            this.drawHighlightedNetwork(parameters, network);
        }

        if (networks.length === 1) {
            const network = networks[0];

            if (network.valueConflict) {
                this.spriteConflict.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60);
            } else if (!network.currentValue) {
                this.spriteEmpty.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60);
            } else {
                network.currentValue.drawItemCenteredClipped(
                    mousePos.x + 40,
                    mousePos.y + 10,
                    parameters,
                    60
                );
            }
        }
    }

    /**
     *
     *
     * @param {import("../../../core/draw_utils").DrawParameters} parameters
     * @param {WireNetwork} network
     */
    drawHighlightedNetwork(parameters, network) {
        parameters.context.globalAlpha = 0.5;

        for (let i = 0; i < network.wires.length; ++i) {
            const wire = network.wires[i];
            const staticComp = wire.components.StaticMapEntity;
            const screenTile = this.root.camera.worldToScreen(staticComp.origin.toWorldSpace());
            _map_chunk_view__WEBPACK_IMPORTED_MODULE_1__["MapChunkView"].drawSingleWiresOverviewTile({
                context: parameters.context,
                x: screenTile.x,
                y: screenTile.y,
                entity: wire,
                tileSizePixels: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * this.root.camera.zoomLevel,
                overrideColor: _theme__WEBPACK_IMPORTED_MODULE_3__["THEME"].map.wires.highlightColor,
            });
        }

        for (let i = 0; i < network.tunnels.length; ++i) {
            const tunnel = network.tunnels[i];
            const staticComp = tunnel.components.StaticMapEntity;
            const screenTile = this.root.camera.worldToScreen(staticComp.origin.toWorldSpace());
            _map_chunk_view__WEBPACK_IMPORTED_MODULE_1__["MapChunkView"].drawSingleWiresOverviewTile({
                context: parameters.context,
                x: screenTile.x,
                y: screenTile.y,
                entity: tunnel,
                tileSizePixels: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * this.root.camera.zoomLevel,
                overrideColor: _theme__WEBPACK_IMPORTED_MODULE_3__["THEME"].map.wires.highlightColor,
            });
        }
        parameters.context.globalAlpha = 1;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/wires_overlay.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/wires_overlay.js ***!
  \************************************************/
/*! exports provided: HUDWiresOverlay */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDWiresOverlay", function() { return HUDWiresOverlay; });
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../tutorial_goals */ "./src/js/game/tutorial_goals.js");
/* harmony import */ var _base_hud_part__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../base_hud_part */ "./src/js/game/hud/base_hud_part.js");










const copy = __webpack_require__(/*! clipboard-copy */ "./node_modules/clipboard-copy/index.js");
const wiresBackgroundDpi = 4;

class HUDWiresOverlay extends _base_hud_part__WEBPACK_IMPORTED_MODULE_8__["BaseHUDPart"] {
    createElements(parent) {}

    initialize() {
        // Probably not the best location, but the one which makes most sense
        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_6__["KEYMAPPINGS"].ingame.switchLayers).add(this.switchLayers, this);
        this.root.keyMapper.getBinding(_key_action_mapper__WEBPACK_IMPORTED_MODULE_6__["KEYMAPPINGS"].placement.copyWireValue).add(this.copyWireValue, this);

        this.generateTilePattern();

        this.currentAlpha = 0.0;
    }

    /**
     * Switches between layers
     */
    switchLayers() {
        if (this.root.currentLayer === "regular") {
            if (
                this.root.hubGoals.isRewardUnlocked(_tutorial_goals__WEBPACK_IMPORTED_MODULE_7__["enumHubGoalRewards"].reward_wires_painter_and_levers) ||
                ( false && false)
            ) {
                this.root.currentLayer = "wires";
            }
        } else {
            this.root.currentLayer = "regular";
        }
        this.root.signals.editModeChanged.dispatch(this.root.currentLayer);
    }

    /**
     * Generates the background pattern for the wires overlay
     */
    generateTilePattern() {
        const overlayTile = _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/wires/overlay_tile.png");
        const dims = _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize * wiresBackgroundDpi;
        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(dims, dims, {
            smooth: false,
            reusable: false,
            label: "wires-tile-pattern",
        });
        context.clearRect(0, 0, dims, dims);
        overlayTile.draw(context, 0, 0, dims, dims);
        this.tilePatternCanvas = canvas;
    }

    update() {
        const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;

        // On low performance, skip the fade
        if (this.root.entityMgr.entities.length > 5000 || this.root.dynamicTickrate.averageFps < 50) {
            this.currentAlpha = desiredAlpha;
        } else {
            this.currentAlpha = Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["lerp"])(this.currentAlpha, desiredAlpha, 0.12);
        }
    }

    /**
     * Copies the wires value below the cursor
     */
    copyWireValue() {
        if (this.root.currentLayer !== "wires") {
            return;
        }

        const mousePos = this.root.app.mousePosition;
        if (!mousePos) {
            return;
        }

        const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
        const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires");
        if (!contents) {
            return;
        }

        let value = null;
        if (contents.components.Wire) {
            const network = contents.components.Wire.linkedNetwork;
            if (network && network.hasValue()) {
                value = network.currentValue;
            }
        }

        if (contents.components.ConstantSignal) {
            value = contents.components.ConstantSignal.signal;
        }

        if (value) {
            copy(value.getAsCopyableKey());
            this.root.soundProxy.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_5__["SOUNDS"].copy);
        } else {
            copy("");
            this.root.soundProxy.playUiError();
        }
    }

    /**
     *
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        if (this.currentAlpha < 0.02) {
            return;
        }

        const hasTileGrid = !this.root.app.settings.getAllSettings().disableTileGrid;
        if (hasTileGrid && !this.cachedPatternBackground) {
            this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat");
        }

        const bounds = parameters.visibleRect;

        parameters.context.globalAlpha = this.currentAlpha;

        const scaleFactor = 1 / wiresBackgroundDpi;
        parameters.context.globalCompositeOperation = "overlay";
        parameters.context.fillStyle = "rgba(50, 200, 150, 1)";
        parameters.context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h);
        parameters.context.globalCompositeOperation = "source-over";

        parameters.context.scale(scaleFactor, scaleFactor);
        parameters.context.fillStyle = hasTileGrid
            ? this.cachedPatternBackground
            : "rgba(78, 137, 125, 0.75)";
        parameters.context.fillRect(
            bounds.x / scaleFactor,
            bounds.y / scaleFactor,
            bounds.w / scaleFactor,
            bounds.h / scaleFactor
        );
        parameters.context.scale(1 / scaleFactor, 1 / scaleFactor);

        parameters.context.globalAlpha = 1;
    }
}


/***/ }),

/***/ "./src/js/game/hud/parts/wires_toolbar.js":
/*!************************************************!*\
  !*** ./src/js/game/hud/parts/wires_toolbar.js ***!
  \************************************************/
/*! exports provided: HUDWiresToolbar */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HUDWiresToolbar", function() { return HUDWiresToolbar; });
/* harmony import */ var _base_toolbar__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./base_toolbar */ "./src/js/game/hud/parts/base_toolbar.js");
/* harmony import */ var _buildings_wire__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../buildings/wire */ "./src/js/game/buildings/wire.js");
/* harmony import */ var _buildings_constant_signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../buildings/constant_signal */ "./src/js/game/buildings/constant_signal.js");
/* harmony import */ var _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../buildings/logic_gate */ "./src/js/game/buildings/logic_gate.js");
/* harmony import */ var _buildings_lever__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../buildings/lever */ "./src/js/game/buildings/lever.js");
/* harmony import */ var _buildings_wire_tunnel__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../buildings/wire_tunnel */ "./src/js/game/buildings/wire_tunnel.js");
/* harmony import */ var _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../buildings/virtual_processor */ "./src/js/game/buildings/virtual_processor.js");
/* harmony import */ var _buildings_transistor__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../buildings/transistor */ "./src/js/game/buildings/transistor.js");
/* harmony import */ var _buildings_analyzer__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../buildings/analyzer */ "./src/js/game/buildings/analyzer.js");
/* harmony import */ var _buildings_comparator__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../buildings/comparator */ "./src/js/game/buildings/comparator.js");
/* harmony import */ var _buildings_reader__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../buildings/reader */ "./src/js/game/buildings/reader.js");
/* harmony import */ var _buildings_filter__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../buildings/filter */ "./src/js/game/buildings/filter.js");
/* harmony import */ var _buildings_display__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../../buildings/display */ "./src/js/game/buildings/display.js");
/* harmony import */ var _buildings_storage__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../buildings/storage */ "./src/js/game/buildings/storage.js");















class HUDWiresToolbar extends _base_toolbar__WEBPACK_IMPORTED_MODULE_0__["HUDBaseToolbar"] {
    constructor(root) {
        super(root, {
            primaryBuildings: [
                _buildings_wire__WEBPACK_IMPORTED_MODULE_1__["MetaWireBuilding"],
                _buildings_wire_tunnel__WEBPACK_IMPORTED_MODULE_5__["MetaWireTunnelBuilding"],
                _buildings_constant_signal__WEBPACK_IMPORTED_MODULE_2__["MetaConstantSignalBuilding"],
                _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_3__["MetaLogicGateBuilding"],
                _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_6__["MetaVirtualProcessorBuilding"],
                _buildings_analyzer__WEBPACK_IMPORTED_MODULE_8__["MetaAnalyzerBuilding"],
                _buildings_comparator__WEBPACK_IMPORTED_MODULE_9__["MetaComparatorBuilding"],
                _buildings_transistor__WEBPACK_IMPORTED_MODULE_7__["MetaTransistorBuilding"],
            ],
            secondaryBuildings: [
                _buildings_storage__WEBPACK_IMPORTED_MODULE_13__["MetaStorageBuilding"],
                _buildings_reader__WEBPACK_IMPORTED_MODULE_10__["MetaReaderBuilding"],
                _buildings_lever__WEBPACK_IMPORTED_MODULE_4__["MetaLeverBuilding"],
                _buildings_filter__WEBPACK_IMPORTED_MODULE_11__["MetaFilterBuilding"],
                _buildings_display__WEBPACK_IMPORTED_MODULE_12__["MetaDisplayBuilding"],
            ],
            visibilityCondition: () =>
                !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
            htmlElementId: "ingame_HUD_wires_toolbar",
            layer: "wires",
        });
    }
}


/***/ }),

/***/ "./src/js/game/hud/trailer_maker.js":
/*!******************************************!*\
  !*** ./src/js/game/hud/trailer_maker.js ***!
  \******************************************/
/*! exports provided: TrailerMaker */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TrailerMaker", function() { return TrailerMaker; });
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../root */ "./src/js/game/root.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _trailer_points__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./trailer_points */ "./src/js/game/hud/trailer_points.js");





/* dev:start */


const tickrate = 1 / 165;

class TrailerMaker {
    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;

        this.markers = [];
        this.playbackMarkers = null;
        this.currentPlaybackOrigin = new _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"]();
        this.currentPlaybackZoom = 3;

        window.addEventListener("keydown", ev => {
            if (ev.key === "j") {
                console.log("Record");
                this.markers.push({
                    pos: this.root.camera.center.copy(),
                    zoom: this.root.camera.zoomLevel,
                    time: 1,
                    wait: 0,
                });
            } else if (ev.key === "k") {
                console.log("Export");
                const json = JSON.stringify(this.markers);
                const handle = window.open("about:blank");
                handle.document.write(json);
            } else if (ev.key === "u") {
                if (this.playbackMarkers && this.playbackMarkers.length > 0) {
                    this.playbackMarkers = [];
                    return;
                }
                console.log("Playback");
                this.playbackMarkers = _trailer_points__WEBPACK_IMPORTED_MODULE_4__["default"].map(p => Object.assign({}, p));
                this.playbackMarkers.unshift(this.playbackMarkers[0]);
                this.currentPlaybackOrigin = _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"].fromSerializedObject(this.playbackMarkers[0].pos);

                this.currentPlaybackZoom = this.playbackMarkers[0].zoom;
                this.root.camera.center = this.currentPlaybackOrigin.copy();
                this.root.camera.zoomLevel = this.currentPlaybackZoom;
                console.log("STart at", this.currentPlaybackOrigin);

                // this.root.entityMgr.getAllWithComponent(MinerComponent).forEach(miner => {
                //     miner.components.Miner.itemChainBuffer = [];
                //     miner.components.Miner.lastMiningTime = this.root.time.now() + 5;
                //     miner.components.ItemEjector.slots.forEach(slot => (slot.item = null));
                // });

                // this.root.logic.tryPlaceBuilding({
                //     origin: new Vector(-428, -15),
                //     building: gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding),
                //     originalRotation: 0,
                //     rotation: 0,
                //     variant: "default",
                //     rotationVariant: 0,
                // });

                // this.root.logic.tryPlaceBuilding({
                //     origin: new Vector(-427, -15),
                //     building: gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding),
                //     originalRotation: 0,
                //     rotation: 0,
                //     variant: "default",
                //     rotationVariant: 0,
                // });
            }
        });
    }

    update() {
        if (this.playbackMarkers && this.playbackMarkers.length > 0) {
            const nextMarker = this.playbackMarkers[0];

            if (!nextMarker.startTime) {
                console.log("Starting to approach", nextMarker.pos);
                nextMarker.startTime = performance.now() / 1000.0;
            }

            const speed =
                _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].tileSize *
                _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].beltSpeedItemsPerSecond *
                _core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].itemSpacingOnBelts;
            // let time =
            //     this.currentPlaybackOrigin.distance(Vector.fromSerializedObject(nextMarker.pos)) / speed;
            const time = nextMarker.time;

            const progress = (performance.now() / 1000.0 - nextMarker.startTime) / time;

            if (progress > 1.0) {
                if (nextMarker.wait > 0) {
                    nextMarker.wait -= tickrate;
                } else {
                    console.log("Approached");
                    this.currentPlaybackOrigin = this.root.camera.center.copy();
                    this.currentPlaybackZoom = this.root.camera.zoomLevel;
                    this.playbackMarkers.shift();
                }
                return;
            }

            const targetPos = _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"].fromSerializedObject(nextMarker.pos);
            const targetZoom = nextMarker.zoom;

            const pos = Object(_core_vector__WEBPACK_IMPORTED_MODULE_2__["mixVector"])(this.currentPlaybackOrigin, targetPos, progress);
            const zoom = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["lerp"])(this.currentPlaybackZoom, targetZoom, progress);
            this.root.camera.zoomLevel = zoom;
            this.root.camera.center = pos;
        }
    }
}

/* dev:end */


/***/ }),

/***/ "./src/js/game/hud/trailer_points.js":
/*!*******************************************!*\
  !*** ./src/js/game/hud/trailer_points.js ***!
  \*******************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ([
    // // initial
    // { pos: { x: -13665, y: -434 }, zoom: 6, time: 1, wait: 8 },

    // // Go up to first curve
    // { pos: { x: -13665, y: -580 }, zoom: 6, time: 1, wait: 0 },

    // // To balancers
    // { pos: { x: -13450, y: -580 }, zoom: 6, time: 1, wait: 0 },

    // // To cutters
    // { pos: { x: -13350, y: -580 }, zoom: 3, time: 1, wait: 2 },

    // // To initial cutters
    // { pos: { x: -12713, y: -580 }, zoom: 3, time: 1, wait: 2.5 },

    // // To rotaters 3,2,1,0
    // { pos: { x: -12402, y: -580 }, zoom: 3, time: 1, wait: 0 },

    // // Zoom in further to stackers
    // { pos: { x: -12045, y: -580 }, zoom: 6, time: 1, wait: 4 },

    // // Focus on painter
    // { pos: { x: -11700, y: -660 }, zoom: 6, time: 1, wait: 3.5 },

    // // Zoom in to mixers
    // { pos: { x: -11463, y: -520 }, zoom: 6, time: 1, wait: 3.8 },

    // // Focus to second painter
    // { pos: { x: -11290, y: -610 }, zoom: 6, time: 1, wait: 1 },

    // // Second stacker
    // { pos: { x: -11022, y: -610 }, zoom: 6, time: 1, wait: 0 },

    // // Go right until first curve
    // { pos: { x: -10859, y: -650 }, zoom: 6, time: 1, wait: 0 },

    // // Go up to stacker
    // { pos: { x: -10859, y: -1120 }, zoom: 6, time: 1, wait: 0 },

    // // Go further up
    // { pos: { x: -10859, y: -1260 }, zoom: 6, time: 1, wait: 0 },

    // // Go left
    // { pos: { x: -11235, y: -1260 }, zoom: 6, time: 1, wait: 1 },

    // OWO Savegames
    // { pos: { x: -4939.356940622392, y: 71.76431237675517 }, zoom: 5.06640625, time: 1, wait: 1 },
    // { pos: { x: -4275.441641063683, y: 26.3603982512193 }, zoom: 0.45, time: 32, wait: 0 },

    // Eve

    //     { pos: { x: -277.22574043554704, y: 2151.1873666983033 }, zoom: 3.1, time: 0, wait: 2 },
    //     { pos: { x: -43.64015426578788, y: 1577.5520572108883 }, zoom: 1.4, time: 16, wait: 0 },
    //     { pos: { x: 133.22735227708466, y: 957.2211413984563 }, zoom: 1.4, time: 8, wait: 0 },
    //     { pos: { x: 480.20365842184424, y: -313.5485044644265 }, zoom: 1.4, time: 8, wait: 0 },
    //     {
    //         pos: { x: 452.56528647804333, y: -1341.6422407571154 },
    //         zoom: 1.4,
    //         time: 8,
    //         wait: 0,
    //     },

    // D
    { pos: { x: -7506.562977380196, y: 1777.6671860680613 }, zoom: 2.3764616075569833, time: 0, wait: 1 },
    { pos: { x: -7506.562977380196, y: 1777.6671860680613 }, zoom: 2.3764616075569833, time: 1, wait: 0 },
    { pos: { x: -6592.471896026158, y: 1841.974816890533 }, zoom: 1.4594444847409322, time: 24, wait: 0 },
    { pos: { x: -7274.384090342281, y: 729.3783696229457 }, zoom: 1.4594444847409322, time: 24, wait: 0 },
    { pos: { x: -6048.006011617565, y: 764.6297752493597 }, zoom: 1.1853320776932916, time: 24, wait: 0 },
    {
        pos: { x: -3674.7204249483366, y: 658.6366426023269 },
        zoom: 0.25332031250000003,
        time: 24,
        wait: 0,
    },
    {
        pos: { x: -1213.9916574596728, y: -1387.1496772071198 },
        zoom: 0.443058809814453,
        time: 24,
        wait: 0,
    },
    {
        pos: { x: 1722.5210292405573, y: -2457.2072755163636 },
        zoom: 0.6313986260996299,
        time: 24,
        wait: 0,
    },
    { pos: { x: 3533.263459106946, y: -1806.6756300805193 }, zoom: 1.551908182277415, time: 24, wait: 0 },
]);


/***/ }),

/***/ "./src/js/game/item_registry.js":
/*!**************************************!*\
  !*** ./src/js/game/item_registry.js ***!
  \**************************************/
/*! exports provided: initItemRegistry */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initItemRegistry", function() { return initItemRegistry; });
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./items/shape_item */ "./src/js/game/items/shape_item.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./items/boolean_item */ "./src/js/game/items/boolean_item.js");





function initItemRegistry() {
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gItemRegistry"].register(_items_shape_item__WEBPACK_IMPORTED_MODULE_1__["ShapeItem"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gItemRegistry"].register(_items_color_item__WEBPACK_IMPORTED_MODULE_2__["ColorItem"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gItemRegistry"].register(_items_boolean_item__WEBPACK_IMPORTED_MODULE_3__["BooleanItem"]);
}


/***/ }),

/***/ "./src/js/game/item_resolver.js":
/*!**************************************!*\
  !*** ./src/js/game/item_resolver.js ***!
  \**************************************/
/*! exports provided: itemResolverSingleton, typeItemSingleton */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "itemResolverSingleton", function() { return itemResolverSingleton; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "typeItemSingleton", function() { return typeItemSingleton; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./items/shape_item */ "./src/js/game/items/shape_item.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./items/color_item */ "./src/js/game/items/color_item.js");






/**
 * Resolves items so we share instances
 * @param {import("../savegame/savegame_serializer").GameRoot} root
 * @param {{$: string, data: any }} data
 */
function itemResolverSingleton(root, data) {
    const itemType = data.$;
    const itemData = data.data;

    switch (itemType) {
        case _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__["BooleanItem"].getId(): {
            return itemData ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__["BOOL_TRUE_SINGLETON"] : _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__["BOOL_FALSE_SINGLETON"];
        }
        case _items_shape_item__WEBPACK_IMPORTED_MODULE_3__["ShapeItem"].getId(): {
            return root.shapeDefinitionMgr.getShapeItemFromShortKey(itemData);
        }
        case _items_color_item__WEBPACK_IMPORTED_MODULE_4__["ColorItem"].getId(): {
            return _items_color_item__WEBPACK_IMPORTED_MODULE_4__["COLOR_ITEM_SINGLETONS"][itemData];
        }

        default: {
            window.assert(false, "Unknown item type: " + itemType);
        }
    }
}

const typeItemSingleton = _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].obj(_core_global_registries__WEBPACK_IMPORTED_MODULE_1__["gItemRegistry"], itemResolverSingleton);


/***/ }),

/***/ "./src/js/game/items/boolean_item.js":
/*!*******************************************!*\
  !*** ./src/js/game/items/boolean_item.js ***!
  \*******************************************/
/*! exports provided: BooleanItem, BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, isTrueItem, isTruthyItem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BooleanItem", function() { return BooleanItem; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BOOL_FALSE_SINGLETON", function() { return BOOL_FALSE_SINGLETON; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BOOL_TRUE_SINGLETON", function() { return BOOL_TRUE_SINGLETON; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isTrueItem", function() { return isTrueItem; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isTruthyItem", function() { return isTruthyItem; });
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");






class BooleanItem extends _base_item__WEBPACK_IMPORTED_MODULE_3__["BaseItem"] {
    static getId() {
        return "boolean_item";
    }

    static getSchema() {
        return _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__["types"].uint;
    }

    serialize() {
        return this.value;
    }

    deserialize(data) {
        this.value = data;
    }

    /** @returns {"boolean"} **/
    getItemType() {
        return "boolean";
    }

    /**
     * @returns {string}
     */
    getAsCopyableKey() {
        return this.value ? "1" : "0";
    }

    /**
     * @param {number} value
     */
    constructor(value) {
        super();
        this.value = value ? 1 : 0;
    }

    /**
     * @param {BaseItem} other
     */
    equalsImpl(other) {
        return this.value === /** @type {BooleanItem} */ (other).value;
    }

    /**
     * @param {number} x
     * @param {number} y
     * @param {number} diameter
     * @param {DrawParameters} parameters
     */
    drawItemCenteredImpl(x, y, parameters, diameter = _core_config__WEBPACK_IMPORTED_MODULE_4__["globalConfig"].defaultItemDiameter) {
        let sprite;
        if (this.value) {
            sprite = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/wires/boolean_true.png");
        } else {
            sprite = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/wires/boolean_false.png");
        }
        sprite.drawCachedCentered(parameters, x, y, diameter);
    }

    /**
     * Draws the item to a canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} size
     */
    drawFullSizeOnCanvas(context, size) {
        let sprite;
        if (this.value) {
            sprite = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/wires/boolean_true.png");
        } else {
            sprite = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/wires/boolean_false.png");
        }
        sprite.drawCentered(context, size / 2, size / 2, size);
    }
}

const BOOL_FALSE_SINGLETON = new BooleanItem(0);
const BOOL_TRUE_SINGLETON = new BooleanItem(1);

/**
 * Returns whether the item is Boolean and TRUE
 * @param {BaseItem} item
 * @returns {boolean}
 */
function isTrueItem(item) {
    return item && item.getItemType() === "boolean" && !!(/** @type {BooleanItem} */ (item).value);
}

/**
 * Returns whether the item is truthy
 * @param {BaseItem} item
 * @returns {boolean}
 */
function isTruthyItem(item) {
    if (!item) {
        return false;
    }

    if (item.getItemType() === "boolean") {
        return !!(/** @type {BooleanItem} */ (item).value);
    }

    return true;
}


/***/ }),

/***/ "./src/js/game/items/color_item.js":
/*!*****************************************!*\
  !*** ./src/js/game/items/color_item.js ***!
  \*****************************************/
/*! exports provided: ColorItem, COLOR_ITEM_SINGLETONS */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ColorItem", function() { return ColorItem; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "COLOR_ITEM_SINGLETONS", function() { return COLOR_ITEM_SINGLETONS; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../colors */ "./src/js/game/colors.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../theme */ "./src/js/game/theme.js");








class ColorItem extends _base_item__WEBPACK_IMPORTED_MODULE_4__["BaseItem"] {
    static getId() {
        return "color";
    }

    static getSchema() {
        return _savegame_serialization__WEBPACK_IMPORTED_MODULE_3__["types"].enum(_colors__WEBPACK_IMPORTED_MODULE_5__["enumColors"]);
    }

    serialize() {
        return this.color;
    }

    deserialize(data) {
        this.color = data;
    }

    /** @returns {"color"} **/
    getItemType() {
        return "color";
    }

    /**
     * @returns {string}
     */
    getAsCopyableKey() {
        return this.color;
    }

    /**
     * @param {BaseItem} other
     */
    equalsImpl(other) {
        return this.color === /** @type {ColorItem} */ (other).color;
    }

    /**
     * @param {enumColors} color
     */
    constructor(color) {
        super();
        this.color = color;
    }

    getBackgroundColorAsResource() {
        return _theme__WEBPACK_IMPORTED_MODULE_6__["THEME"].map.resources[this.color];
    }

    /**
     * Draws the item to a canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} size
     */
    drawFullSizeOnCanvas(context, size) {
        if (!this.cachedSprite) {
            this.cachedSprite = _core_loader__WEBPACK_IMPORTED_MODULE_2__["Loader"].getSprite("sprites/colors/" + this.color + ".png");
        }
        this.cachedSprite.drawCentered(context, size / 2, size / 2, size);
    }

    /**
     * @param {number} x
     * @param {number} y
     * @param {number} diameter
     * @param {DrawParameters} parameters
     */
    drawItemCenteredClipped(x, y, parameters, diameter = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter) {
        const realDiameter = diameter * 0.6;
        if (!this.cachedSprite) {
            this.cachedSprite = _core_loader__WEBPACK_IMPORTED_MODULE_2__["Loader"].getSprite("sprites/colors/" + this.color + ".png");
        }
        this.cachedSprite.drawCachedCentered(parameters, x, y, realDiameter);
    }
}

/**
 * Singleton instances
 * @type {Object<enumColors, ColorItem>}
 */
const COLOR_ITEM_SINGLETONS = {};

for (const color in _colors__WEBPACK_IMPORTED_MODULE_5__["enumColors"]) {
    COLOR_ITEM_SINGLETONS[color] = new ColorItem(color);
}


/***/ }),

/***/ "./src/js/game/items/shape_item.js":
/*!*****************************************!*\
  !*** ./src/js/game/items/shape_item.js ***!
  \*****************************************/
/*! exports provided: ShapeItem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ShapeItem", function() { return ShapeItem; });
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../theme */ "./src/js/game/theme.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");







class ShapeItem extends _base_item__WEBPACK_IMPORTED_MODULE_2__["BaseItem"] {
    static getId() {
        return "shape";
    }

    static getSchema() {
        return _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["types"].string;
    }

    serialize() {
        return this.definition.getHash();
    }

    deserialize(data) {
        this.definition = _shape_definition__WEBPACK_IMPORTED_MODULE_3__["ShapeDefinition"].fromShortKey(data);
    }

    /** @returns {"shape"} **/
    getItemType() {
        return "shape";
    }

    /**
     * @returns {string}
     */
    getAsCopyableKey() {
        return this.definition.getHash();
    }

    /**
     * @param {BaseItem} other
     */
    equalsImpl(other) {
        return this.definition.getHash() === /** @type {ShapeItem} */ (other).definition.getHash();
    }

    /**
     * @param {ShapeDefinition} definition
     */
    constructor(definition) {
        super();

        /**
         * This property must not be modified on runtime, you have to clone the class in order to change the definition
         */
        this.definition = definition;
    }

    getBackgroundColorAsResource() {
        return _theme__WEBPACK_IMPORTED_MODULE_4__["THEME"].map.resources.shape;
    }

    /**
     * Draws the item to a canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} size
     */
    drawFullSizeOnCanvas(context, size) {
        this.definition.drawFullSizeOnCanvas(context, size);
    }

    /**
     * @param {number} x
     * @param {number} y
     * @param {DrawParameters} parameters
     * @param {number=} diameter
     */
    drawItemCenteredImpl(x, y, parameters, diameter = _core_config__WEBPACK_IMPORTED_MODULE_5__["globalConfig"].defaultItemDiameter) {
        this.definition.drawCentered(x, y, parameters, diameter);
    }
}


/***/ }),

/***/ "./src/js/game/key_action_mapper.js":
/*!******************************************!*\
  !*** ./src/js/game/key_action_mapper.js ***!
  \******************************************/
/*! exports provided: KEYMAPPINGS, KEYCODE_LMB, KEYCODE_MMB, KEYCODE_RMB, getStringForKeyCode, Keybinding, KeyActionMapper */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KEYMAPPINGS", function() { return KEYMAPPINGS; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KEYCODE_LMB", function() { return KEYCODE_LMB; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KEYCODE_MMB", function() { return KEYCODE_MMB; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KEYCODE_RMB", function() { return KEYCODE_RMB; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getStringForKeyCode", function() { return getStringForKeyCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Keybinding", function() { return Keybinding; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeyActionMapper", function() { return KeyActionMapper; });
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");





function key(str) {
    return str.toUpperCase().charCodeAt(0);
}

const KEYMAPPINGS = {
    general: {
        confirm: { keyCode: 13 }, // enter
        back: { keyCode: 27, builtin: true }, // escape
    },

    ingame: {
        menuOpenShop: { keyCode: key("F") },
        menuOpenStats: { keyCode: key("G") },
        menuClose: { keyCode: key("Q") },

        toggleHud: { keyCode: 113 }, // F2
        exportScreenshot: { keyCode: 114 }, // F3PS
        toggleFPSInfo: { keyCode: 115 }, // F4

        switchLayers: { keyCode: key("E") },
    },

    navigation: {
        mapMoveUp: { keyCode: key("W") },
        mapMoveRight: { keyCode: key("D") },
        mapMoveDown: { keyCode: key("S") },
        mapMoveLeft: { keyCode: key("A") },
        mapMoveFaster: { keyCode: 16 }, //shift

        centerMap: { keyCode: 32 }, // SPACE
        mapZoomIn: { keyCode: 187, repeated: true }, // "+"
        mapZoomOut: { keyCode: 189, repeated: true }, // "-"

        createMarker: { keyCode: key("M") },
    },

    buildings: {
        // Primary Toolbar
        belt: { keyCode: key("1") },
        balancer: { keyCode: key("2") },
        underground_belt: { keyCode: key("3") },
        miner: { keyCode: key("4") },
        cutter: { keyCode: key("5") },
        rotater: { keyCode: key("6") },
        stacker: { keyCode: key("7") },
        mixer: { keyCode: key("8") },
        painter: { keyCode: key("9") },
        trash: { keyCode: key("0") },

        // Sandbox
        item_producer: { keyCode: key("L") },

        // Secondary toolbar
        storage: { keyCode: key("Y") },
        reader: { keyCode: key("U") },
        lever: { keyCode: key("I") },
        filter: { keyCode: key("O") },
        display: { keyCode: key("P") },

        // Wires toolbar
        wire: { keyCode: key("1") },
        wire_tunnel: { keyCode: key("2") },
        constant_signal: { keyCode: key("3") },
        logic_gate: { keyCode: key("4") },
        virtual_processor: { keyCode: key("5") },
        analyzer: { keyCode: key("6") },
        comparator: { keyCode: key("7") },
        transistor: { keyCode: key("8") },
    },

    placement: {
        pipette: { keyCode: key("Q") },
        rotateWhilePlacing: { keyCode: key("R") },
        rotateInverseModifier: { keyCode: 16 }, // SHIFT
        cycleBuildingVariants: { keyCode: key("T") },
        cycleBuildings: { keyCode: 9 }, // TAB
        switchDirectionLockSide: { keyCode: key("R") },

        copyWireValue: { keyCode: key("Z") },
    },

    massSelect: {
        massSelectStart: { keyCode: 17 }, // CTRL
        massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
        massSelectCopy: { keyCode: key("C") },
        massSelectCut: { keyCode: key("X") },
        confirmMassDelete: { keyCode: 46 }, // DEL
        pasteLastBlueprint: { keyCode: key("V") },
    },

    placementModifiers: {
        lockBeltDirection: { keyCode: 16 }, // SHIFT
        placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
        placeMultiple: { keyCode: 16 }, // SHIFT
        placeInverse: { keyCode: 18 }, // ALT
    },
};

// Assign ids
for (const categoryId in KEYMAPPINGS) {
    for (const mappingId in KEYMAPPINGS[categoryId]) {
        KEYMAPPINGS[categoryId][mappingId].id = mappingId;
    }
}

const KEYCODE_LMB = 1;
const KEYCODE_MMB = 2;
const KEYCODE_RMB = 3;

/**
 * Returns a keycode -> string
 * @param {number} code
 * @returns {string}
 */
function getStringForKeyCode(code) {
    // @todo: Refactor into dictionary
    switch (code) {
        case KEYCODE_LMB:
            return "LMB";
        case KEYCODE_MMB:
            return "MMB";
        case KEYCODE_RMB:
            return "RMB";
        case 4:
            return "MB4";
        case 5:
            return "MB5";
        case 8:
            return "⌫";
        case 9:
            return _translations__WEBPACK_IMPORTED_MODULE_2__["T"].global.keys.tab;
        case 13:
            return "⏎";
        case 16:
            return "⇪";
        case 17:
            return _translations__WEBPACK_IMPORTED_MODULE_2__["T"].global.keys.control;
        case 18:
            return _translations__WEBPACK_IMPORTED_MODULE_2__["T"].global.keys.alt;
        case 19:
            return "PAUSE";
        case 20:
            return "CAPS";
        case 27:
            return _translations__WEBPACK_IMPORTED_MODULE_2__["T"].global.keys.escape;
        case 32:
            return _translations__WEBPACK_IMPORTED_MODULE_2__["T"].global.keys.space;
        case 33:
            return "PGUP";
        case 34:
            return "PGDOWN";
        case 35:
            return "END";
        case 36:
            return "HOME";
        case 37:
            return "⬅";
        case 38:
            return "⬆";
        case 39:
            return "➡";
        case 40:
            return "⬇";
        case 44:
            return "PRNT";
        case 45:
            return "INS";
        case 46:
            return "DEL";
        case 93:
            return "SEL";
        case 96:
            return "NUM 0";
        case 97:
            return "NUM 1";
        case 98:
            return "NUM 2";
        case 99:
            return "NUM 3";
        case 100:
            return "NUM 4";
        case 101:
            return "NUM 5";
        case 102:
            return "NUM 6";
        case 103:
            return "NUM 7";
        case 104:
            return "NUM 8";
        case 105:
            return "NUM 9";
        case 106:
            return "*";
        case 107:
            return "+";
        case 109:
            return "-";
        case 110:
            return ".";
        case 111:
            return "/";
        case 112:
            return "F1";
        case 113:
            return "F2";
        case 114:
            return "F3";
        case 115:
            return "F4";
        case 116:
            return "F5";
        case 117:
            return "F6";
        case 118:
            return "F7";
        case 119:
            return "F8";
        case 120:
            return "F9";
        case 121:
            return "F10";
        case 122:
            return "F11";
        case 123:
            return "F12";

        case 144:
            return "NUMLOCK";
        case 145:
            return "SCRLOCK";
        case 182:
            return "COMP";
        case 183:
            return "CALC";
        case 186:
            return ";";
        case 187:
            return "+";
        case 188:
            return ",";
        case 189:
            return "-";
        case 190:
            return ".";
        case 191:
            return "/";
        case 219:
            return "[";
        case 220:
            return "\\";
        case 221:
            return "]";
        case 222:
            return "'";
    }

    return (48 <= code && code <= 57) || (65 <= code && code <= 90)
        ? String.fromCharCode(code)
        : "[" + code + "]";
}

class Keybinding {
    /**
     *
     * @param {KeyActionMapper} keyMapper
     * @param {Application} app
     * @param {object} param0
     * @param {number} param0.keyCode
     * @param {boolean=} param0.builtin
     * @param {boolean=} param0.repeated
     */
    constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
        window.assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
        this.keyMapper = keyMapper;
        this.app = app;
        this.keyCode = keyCode;
        this.builtin = builtin;
        this.repeated = repeated;

        this.signal = new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
        this.toggled = new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]();
    }

    /**
     * Returns whether this binding is currently pressed
     * @returns {boolean}
     */
    get pressed() {
        // Check if the key is down
        if (this.app.inputMgr.keysDown.has(this.keyCode)) {
            // Check if it is the top reciever
            const reciever = this.keyMapper.inputReceiver;
            return this.app.inputMgr.getTopReciever() === reciever;
        }
        return false;
    }

    /**
     * Adds an event listener
     * @param {function() : void} receiver
     * @param {object=} scope
     */
    add(receiver, scope = null) {
        this.signal.add(receiver, scope);
    }

    /**
     * @param {Element} elem
     * @returns {HTMLElement} the created element, or null if the keybindings are not shown
     *  */
    appendLabelToElement(elem) {
        if (_core_config__WEBPACK_IMPORTED_MODULE_1__["IS_MOBILE"]) {
            return null;
        }
        const spacer = document.createElement("code");
        spacer.classList.add("keybinding");
        spacer.innerHTML = getStringForKeyCode(this.keyCode);
        elem.appendChild(spacer);
        return spacer;
    }

    /**
     * Returns the key code as a nice string
     */
    getKeyCodeString() {
        return getStringForKeyCode(this.keyCode);
    }

    /**
     * Remvoes all signal receivers
     */
    clearSignalReceivers() {
        this.signal.removeAll();
    }
}

class KeyActionMapper {
    /**
     *
     * @param {GameRoot} root
     * @param {InputReceiver} inputReciever
     */
    constructor(root, inputReciever) {
        this.root = root;
        this.inputReceiver = inputReciever;

        inputReciever.keydown.add(this.handleKeydown, this);
        inputReciever.keyup.add(this.handleKeyup, this);

        /** @type {Object.<string, Keybinding>} */
        this.keybindings = {};

        const overrides = root.app.settings.getKeybindingOverrides();

        for (const category in KEYMAPPINGS) {
            for (const key in KEYMAPPINGS[category]) {
                let payload = Object.assign({}, KEYMAPPINGS[category][key]);
                if (overrides[key]) {
                    payload.keyCode = overrides[key];
                }

                this.keybindings[key] = new Keybinding(this, this.root.app, payload);

                if (false) {}
            }
        }

        inputReciever.pageBlur.add(this.onPageBlur, this);
        inputReciever.destroyed.add(this.cleanup, this);
    }

    /**
     * Returns all keybindings starting with the given id
     * @param {string} pattern
     * @returns {Array<Keybinding>}
     */
    getKeybindingsStartingWith(pattern) {
        let result = [];
        for (const key in this.keybindings) {
            if (key.startsWith(pattern)) {
                result.push(this.keybindings[key]);
            }
        }
        return result;
    }

    /**
     * Forwards the given events to the other mapper (used in tooltips)
     * @param {KeyActionMapper} receiver
     * @param {Array<string>} bindings
     */
    forward(receiver, bindings) {
        for (let i = 0; i < bindings.length; ++i) {
            const key = bindings[i];
            this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
        }
    }

    cleanup() {
        for (const key in this.keybindings) {
            this.keybindings[key].signal.removeAll();
        }
    }

    onPageBlur() {
        // Reset all down states
        // Find mapping
        for (const key in this.keybindings) {
            /** @type {Keybinding} */
            const binding = this.keybindings[key];
        }
    }

    /**
     * Internal keydown handler
     * @param {object} param0
     * @param {number} param0.keyCode
     * @param {boolean} param0.shift
     * @param {boolean} param0.alt
     * @param {boolean=} param0.initial
     */
    handleKeydown({ keyCode, shift, alt, initial }) {
        let stop = false;

        // Find mapping
        for (const key in this.keybindings) {
            /** @type {Keybinding} */
            const binding = this.keybindings[key];
            if (binding.keyCode === keyCode && (initial || binding.repeated)) {
                /** @type {Signal} */
                const signal = this.keybindings[key].signal;
                if (signal.dispatch() === _core_signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"]) {
                    return;
                }
            }
        }

        if (stop) {
            return _core_signal__WEBPACK_IMPORTED_MODULE_0__["STOP_PROPAGATION"];
        }
    }

    /**
     * Internal keyup handler
     * @param {object} param0
     * @param {number} param0.keyCode
     * @param {boolean} param0.shift
     * @param {boolean} param0.alt
     */
    handleKeyup({ keyCode, shift, alt }) {
        // Empty
    }

    /**
     * Returns a given keybinding
     * @param {{ keyCode: number }} binding
     * @returns {Keybinding}
     */
    getBinding(binding) {
        // @ts-ignore
        const id = binding.id;
        window.assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
        window.assert(this.keybindings[id], "Keybinding " + id + " not known!");
        return this.keybindings[id];
    }
}


/***/ }),

/***/ "./src/js/game/logic.js":
/*!******************************!*\
  !*** ./src/js/game/logic.js ***!
  \******************************/
/*! exports provided: GameLogic */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameLogic", function() { return GameLogic; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _components_wire__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./components/wire */ "./src/js/game/components/wire.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _systems_wire__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./systems/wire */ "./src/js/game/systems/wire.js");













const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("ingame/logic");

/**
 * Typing helper
 * @typedef {Array<{
 *  entity: Entity,
 *  slot: import("./components/item_ejector").ItemEjectorSlot,
 *  fromTile: Vector,
 *  toDirection: enumDirection
 * }>} EjectorsAffectingTile
 */

/**
 * Typing helper
 * @typedef {Array<{
 *  entity: Entity,
 *  slot: import("./components/item_acceptor").ItemAcceptorSlot,
 *  toTile: Vector,
 *  fromDirection: enumDirection
 * }>} AcceptorsAffectingTile
 */

/**
 * @typedef {{
 *     acceptors: AcceptorsAffectingTile,
 *     ejectors: EjectorsAffectingTile
 * }} AcceptorsAndEjectorsAffectingTile
 */

class GameLogic {
    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;
    }

    /**
     * Checks if the given entity can be placed
     * @param {Entity} entity
     * @param {Vector=} offset Optional, move the entity by the given offset first
     * @returns {boolean} true if the entity could be placed there
     */
    checkCanPlaceEntity(entity, offset = null) {
        // Compute area of the building
        const rect = entity.components.StaticMapEntity.getTileSpaceBounds();
        if (offset) {
            rect.x += offset.x;
            rect.y += offset.y;
        }

        // Check the whole area of the building
        for (let x = rect.x; x < rect.x + rect.w; ++x) {
            for (let y = rect.y; y < rect.y + rect.h; ++y) {
                // Check if there is any direct collision
                const otherEntity = this.root.map.getLayerContentXY(x, y, entity.layer);
                if (otherEntity) {
                    const metaClass = otherEntity.components.StaticMapEntity.getMetaBuilding();
                    if (!metaClass.getIsReplaceable()) {
                        // This one is a direct blocker
                        return false;
                    }
                }
            }
        }

        // Perform additional placement checks
        if (this.root.signals.prePlacementCheck.dispatch(entity, offset) === _core_signal__WEBPACK_IMPORTED_MODULE_2__["STOP_PROPAGATION"]) {
            return false;
        }

        return true;
    }

    /**
     * Attempts to place the given building
     * @param {object} param0
     * @param {Vector} param0.origin
     * @param {number} param0.rotation
     * @param {number} param0.originalRotation
     * @param {number} param0.rotationVariant
     * @param {string} param0.variant
     * @param {MetaBuilding} param0.building
     * @returns {Entity}
     */
    tryPlaceBuilding({ origin, rotation, rotationVariant, originalRotation, variant, building }) {
        const entity = building.createEntity({
            root: this.root,
            origin,
            rotation,
            originalRotation,
            rotationVariant,
            variant,
        });
        if (this.checkCanPlaceEntity(entity)) {
            this.freeEntityAreaBeforeBuild(entity);
            this.root.map.placeStaticEntity(entity);
            this.root.entityMgr.registerEntity(entity);
            return entity;
        }
        return null;
    }

    /**
     * Removes all entities with a RemovableMapEntityComponent which need to get
     * removed before placing this entity
     * @param {Entity} entity
     */
    freeEntityAreaBeforeBuild(entity) {
        const staticComp = entity.components.StaticMapEntity;
        const rect = staticComp.getTileSpaceBounds();
        // Remove any removeable colliding entities on the same layer
        for (let x = rect.x; x < rect.x + rect.w; ++x) {
            for (let y = rect.y; y < rect.y + rect.h; ++y) {
                const contents = this.root.map.getLayerContentXY(x, y, entity.layer);
                if (contents) {
                    window.assert(
                        contents.components.StaticMapEntity.getMetaBuilding().getIsReplaceable(),
                        "Tried to replace non-repleaceable entity"
                    );
                    if (!this.tryDeleteBuilding(contents)) {
                        window.assert(false, "Tried to replace non-repleaceable entity #2");
                    }
                }
            }
        }

        // Perform other callbacks
        this.root.signals.freeEntityAreaBeforeBuild.dispatch(entity);
    }

    /**
     * Performs a bulk operation, not updating caches in the meantime
     * @param {function} operation
     */
    performBulkOperation(operation) {
        logger.warn("Running bulk operation ...");
        window.assert(!this.root.bulkOperationRunning, "Can not run two bulk operations twice");
        this.root.bulkOperationRunning = true;
        const now = performance.now();
        const returnValue = operation();
        const duration = performance.now() - now;
        logger.log("Done in", Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["round2Digits"])(duration), "ms");
        window.assert(this.root.bulkOperationRunning, "Bulk operation = false while bulk operation was running");
        this.root.bulkOperationRunning = false;
        this.root.signals.bulkOperationFinished.dispatch();
        return returnValue;
    }

    /**
     * Returns whether the given building can get removed
     * @param {Entity} building
     */
    canDeleteBuilding(building) {
        const staticComp = building.components.StaticMapEntity;
        return staticComp.getMetaBuilding().getIsRemovable();
    }

    /**
     * Tries to delete the given building
     * @param {Entity} building
     */
    tryDeleteBuilding(building) {
        if (!this.canDeleteBuilding(building)) {
            return false;
        }
        this.root.map.removeStaticEntity(building);
        this.root.entityMgr.destroyEntity(building);
        this.root.entityMgr.processDestroyList();
        return true;
    }

    /**
     *
     * Computes the flag for a given tile
     * @param {object} param0
     * @param {enumWireVariant} param0.wireVariant
     * @param {Vector} param0.tile The tile to check at
     * @param {enumDirection} param0.edge The edge to check for
     */
    computeWireEdgeStatus({ wireVariant, tile, edge }) {
        const offset = _core_vector__WEBPACK_IMPORTED_MODULE_4__["enumDirectionToVector"][edge];
        const targetTile = tile.add(offset);

        // Search for relevant pins
        const pinEntities = this.root.map.getLayersContentsMultipleXY(targetTile.x, targetTile.y);

        // Go over all entities which could have a pin
        for (let i = 0; i < pinEntities.length; ++i) {
            const pinEntity = pinEntities[i];
            const pinComp = pinEntity.components.WiredPins;
            const staticComp = pinEntity.components.StaticMapEntity;

            // Skip those who don't have pins
            if (!pinComp) {
                continue;
            }

            // Go over all pins
            const pins = pinComp.slots;
            for (let k = 0; k < pinComp.slots.length; ++k) {
                const pinSlot = pins[k];
                const pinLocation = staticComp.localTileToWorld(pinSlot.pos);
                const pinDirection = staticComp.localDirectionToWorld(pinSlot.direction);

                // Check if the pin has the right location
                if (!pinLocation.equals(targetTile)) {
                    continue;
                }

                // Check if the pin has the right direction
                if (pinDirection !== _core_vector__WEBPACK_IMPORTED_MODULE_4__["enumInvertedDirections"][edge]) {
                    continue;
                }

                // Found a pin!
                return true;
            }
        }

        // Now check if there's a connectable entity on the wires layer
        const targetEntity = this.root.map.getTileContent(targetTile, "wires");
        if (!targetEntity) {
            return false;
        }

        const targetStaticComp = targetEntity.components.StaticMapEntity;

        // Check if its a crossing
        const wireTunnelComp = targetEntity.components.WireTunnel;
        if (wireTunnelComp) {
            return true;
        }

        // Check if its a wire
        const wiresComp = targetEntity.components.Wire;
        if (!wiresComp) {
            return false;
        }

        // It's connected if its the same variant
        return wiresComp.variant === wireVariant;
    }

    /**
     * Returns all wire networks this entity participates in on the given tile
     * @param {Entity} entity
     * @param {Vector} tile
     * @returns {Array<WireNetwork>|null} Null if the entity is never able to be connected at the given tile
     */
    getEntityWireNetworks(entity, tile) {
        let canConnectAtAll = false;

        /** @type {Set<WireNetwork>} */
        const networks = new Set();

        const staticComp = entity.components.StaticMapEntity;
        const wireComp = entity.components.Wire;
        if (wireComp) {
            canConnectAtAll = true;
            if (wireComp.linkedNetwork) {
                networks.add(wireComp.linkedNetwork);
            }
        }

        const tunnelComp = entity.components.WireTunnel;
        if (tunnelComp) {
            canConnectAtAll = true;
            for (let i = 0; i < tunnelComp.linkedNetworks.length; ++i) {
                networks.add(tunnelComp.linkedNetworks[i]);
            }
        }

        const pinsComp = entity.components.WiredPins;
        if (pinsComp) {
            const slots = pinsComp.slots;
            for (let i = 0; i < slots.length; ++i) {
                const slot = slots[i];
                const slotLocalPos = staticComp.localTileToWorld(slot.pos);
                if (slotLocalPos.equals(tile)) {
                    canConnectAtAll = true;
                    if (slot.linkedNetwork) {
                        networks.add(slot.linkedNetwork);
                    }
                }
            }
        }

        if (!canConnectAtAll) {
            return null;
        }

        return Array.from(networks);
    }

    /**
     * Returns if the entities tile *and* his overlay matrix is intersected
     * @param {Entity} entity
     * @param {Vector} worldPos
     */
    getIsEntityIntersectedWithMatrix(entity, worldPos) {
        const staticComp = entity.components.StaticMapEntity;
        const tile = worldPos.toTileSpace();

        if (!staticComp.getTileSpaceBounds().containsPoint(tile.x, tile.y)) {
            // No intersection at all
            return;
        }

        const data = Object(_building_codes__WEBPACK_IMPORTED_MODULE_5__["getBuildingDataFromCode"])(staticComp.code);
        const overlayMatrix = data.metaInstance.getSpecialOverlayRenderMatrix(
            staticComp.rotation,
            data.rotationVariant,
            data.variant,
            entity
        );
        // Always the same
        if (!overlayMatrix) {
            return true;
        }

        const localPosition = worldPos
            .divideScalar(_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize)
            .modScalar(1)
            .multiplyScalar(_map_chunk_view__WEBPACK_IMPORTED_MODULE_8__["CHUNK_OVERLAY_RES"])
            .floor();

        return !!overlayMatrix[localPosition.x + localPosition.y * 3];
    }

    g(tile, edge) {}

    /**
     * Returns the acceptors and ejectors which affect the current tile
     * @param {Vector} tile
     * @returns {AcceptorsAndEjectorsAffectingTile}
     */
    getEjectorsAndAcceptorsAtTile(tile) {
        /** @type {EjectorsAffectingTile} */
        let ejectors = [];
        /** @type {AcceptorsAffectingTile} */
        let acceptors = [];

        // Well .. please ignore this code! :D
        for (let dx = -1; dx <= 1; ++dx) {
            for (let dy = -1; dy <= 1; ++dy) {
                if (Math.abs(dx) + Math.abs(dy) !== 1) {
                    continue;
                }

                const entity = this.root.map.getLayerContentXY(tile.x + dx, tile.y + dy, "regular");
                if (entity) {
                    let ejectorSlots = [];
                    let acceptorSlots = [];

                    const staticComp = entity.components.StaticMapEntity;
                    const itemEjector = entity.components.ItemEjector;
                    const itemAcceptor = entity.components.ItemAcceptor;
                    const beltComp = entity.components.Belt;

                    if (itemEjector) {
                        ejectorSlots = itemEjector.slots.slice();
                    }

                    if (itemAcceptor) {
                        acceptorSlots = itemAcceptor.slots.slice();
                    }

                    if (beltComp) {
                        const fakeEjectorSlot = beltComp.getFakeEjectorSlot();
                        const fakeAcceptorSlot = beltComp.getFakeAcceptorSlot();
                        ejectorSlots.push(fakeEjectorSlot);
                        acceptorSlots.push(fakeAcceptorSlot);
                    }

                    for (let ejectorSlot = 0; ejectorSlot < ejectorSlots.length; ++ejectorSlot) {
                        const slot = ejectorSlots[ejectorSlot];
                        const wsTile = staticComp.localTileToWorld(slot.pos);
                        const wsDirection = staticComp.localDirectionToWorld(slot.direction);
                        const targetTile = wsTile.add(_core_vector__WEBPACK_IMPORTED_MODULE_4__["enumDirectionToVector"][wsDirection]);
                        if (targetTile.equals(tile)) {
                            ejectors.push({
                                entity,
                                slot,
                                fromTile: wsTile,
                                toDirection: wsDirection,
                            });
                        }
                    }

                    for (let acceptorSlot = 0; acceptorSlot < acceptorSlots.length; ++acceptorSlot) {
                        const slot = acceptorSlots[acceptorSlot];
                        const wsTile = staticComp.localTileToWorld(slot.pos);
                        for (let k = 0; k < slot.directions.length; ++k) {
                            const direction = slot.directions[k];
                            const wsDirection = staticComp.localDirectionToWorld(direction);

                            const sourceTile = wsTile.add(_core_vector__WEBPACK_IMPORTED_MODULE_4__["enumDirectionToVector"][wsDirection]);
                            if (sourceTile.equals(tile)) {
                                acceptors.push({
                                    entity,
                                    slot,
                                    toTile: wsTile,
                                    fromDirection: wsDirection,
                                });
                            }
                        }
                    }
                }
            }
        }
        return { ejectors, acceptors };
    }
}


/***/ }),

/***/ "./src/js/game/map.js":
/*!****************************!*\
  !*** ./src/js/game/map.js ***!
  \****************************/
/*! exports provided: BaseMap */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseMap", function() { return BaseMap; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");








class BaseMap extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__["BasicSerializableObject"] {
    static getId() {
        return "Map";
    }

    static getSchema() {
        return {
            seed: _savegame_serialization__WEBPACK_IMPORTED_MODULE_2__["types"].uint,
        };
    }

    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        super();
        this.root = root;

        this.seed = 0;

        /**
         * Mapping of 'X|Y' to chunk
         * @type {Map<string, MapChunkView>} */
        this.chunksById = new Map();
    }

    /**
     * Returns the given chunk by index
     * @param {number} chunkX
     * @param {number} chunkY
     */
    getChunk(chunkX, chunkY, createIfNotExistent = false) {
        const chunkIdentifier = chunkX + "|" + chunkY;
        let storedChunk;

        if ((storedChunk = this.chunksById.get(chunkIdentifier))) {
            return storedChunk;
        }

        if (createIfNotExistent) {
            const instance = new _map_chunk_view__WEBPACK_IMPORTED_MODULE_5__["MapChunkView"](this.root, chunkX, chunkY);
            this.chunksById.set(chunkIdentifier, instance);
            return instance;
        }

        return null;
    }

    /**
     * Gets or creates a new chunk if not existent for the given tile
     * @param {number} tileX
     * @param {number} tileY
     * @returns {MapChunkView}
     */
    getOrCreateChunkAtTile(tileX, tileY) {
        const chunkX = Math.floor(tileX / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);
        const chunkY = Math.floor(tileY / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);
        return this.getChunk(chunkX, chunkY, true);
    }

    /**
     * Gets a chunk if not existent for the given tile
     * @param {number} tileX
     * @param {number} tileY
     * @returns {MapChunkView?}
     */
    getChunkAtTileOrNull(tileX, tileY) {
        const chunkX = Math.floor(tileX / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);
        const chunkY = Math.floor(tileY / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);
        return this.getChunk(chunkX, chunkY, false);
    }

    /**
     * Checks if a given tile is within the map bounds
     * @param {Vector} tile
     * @returns {boolean}
     */
    isValidTile(tile) {
        if (false) {}
        return Number.isInteger(tile.x) && Number.isInteger(tile.y);
    }

    /**
     * Returns the tile content of a given tile
     * @param {Vector} tile
     * @param {Layer} layer
     * @returns {Entity} Entity or null
     */
    getTileContent(tile, layer) {
        if (false) {}
        const chunk = this.getChunkAtTileOrNull(tile.x, tile.y);
        return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer);
    }

    /**
     * Returns the lower layers content of the given tile
     * @param {number} x
     * @param {number} y
     * @returns {BaseItem=}
     */
    getLowerLayerContentXY(x, y) {
        return this.getOrCreateChunkAtTile(x, y).getLowerLayerFromWorldCoords(x, y);
    }

    /**
     * Returns the tile content of a given tile
     * @param {number} x
     * @param {number} y
     * @param {Layer} layer
     * @returns {Entity} Entity or null
     */
    getLayerContentXY(x, y, layer) {
        const chunk = this.getChunkAtTileOrNull(x, y);
        return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer);
    }

    /**
     * Returns the tile contents of a given tile
     * @param {number} x
     * @param {number} y
     * @returns {Array<Entity>} Entity or null
     */
    getLayersContentsMultipleXY(x, y) {
        const chunk = this.getChunkAtTileOrNull(x, y);
        if (!chunk) {
            return [];
        }
        return chunk.getLayersContentsMultipleFromWorldCoords(x, y);
    }

    /**
     * Checks if the tile is used
     * @param {Vector} tile
     * @param {Layer} layer
     * @returns {boolean}
     */
    isTileUsed(tile, layer) {
        if (false) {}
        const chunk = this.getChunkAtTileOrNull(tile.x, tile.y);
        return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer) != null;
    }

    /**
     * Checks if the tile is used
     * @param {number} x
     * @param {number} y
     * @param {Layer} layer
     * @returns {boolean}
     */
    isTileUsedXY(x, y, layer) {
        const chunk = this.getChunkAtTileOrNull(x, y);
        return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer) != null;
    }

    /**
     * Sets the tiles content
     * @param {Vector} tile
     * @param {Entity} entity
     */
    setTileContent(tile, entity) {
        if (false) {}

        this.getOrCreateChunkAtTile(tile.x, tile.y).setLayerContentFromWorldCords(
            tile.x,
            tile.y,
            entity,
            entity.layer
        );

        const staticComponent = entity.components.StaticMapEntity;
        window.assert(staticComponent, "Can only place static map entities in tiles");
    }

    /**
     * Places an entity with the StaticMapEntity component
     * @param {Entity} entity
     */
    placeStaticEntity(entity) {
        window.assert(entity.components.StaticMapEntity, "Entity is not static");
        const staticComp = entity.components.StaticMapEntity;
        const rect = staticComp.getTileSpaceBounds();
        for (let dx = 0; dx < rect.w; ++dx) {
            for (let dy = 0; dy < rect.h; ++dy) {
                const x = rect.x + dx;
                const y = rect.y + dy;
                this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, entity, entity.layer);
            }
        }
    }

    /**
     * Removes an entity with the StaticMapEntity component
     * @param {Entity} entity
     */
    removeStaticEntity(entity) {
        window.assert(entity.components.StaticMapEntity, "Entity is not static");
        const staticComp = entity.components.StaticMapEntity;
        const rect = staticComp.getTileSpaceBounds();
        for (let dx = 0; dx < rect.w; ++dx) {
            for (let dy = 0; dy < rect.h; ++dy) {
                const x = rect.x + dx;
                const y = rect.y + dy;
                this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, null, entity.layer);
            }
        }
    }

    // Internal

    /**
     * Checks a given tile for validty
     * @param {Vector} tile
     */
    internalCheckTile(tile) {
        window.assert(tile instanceof _core_vector__WEBPACK_IMPORTED_MODULE_1__["Vector"], "tile is not a vector: " + tile);
        window.assert(tile.x % 1 === 0, "Tile X is not a valid integer: " + tile.x);
        window.assert(tile.y % 1 === 0, "Tile Y is not a valid integer: " + tile.y);
    }
}


/***/ }),

/***/ "./src/js/game/map_chunk.js":
/*!**********************************!*\
  !*** ./src/js/game/map_chunk.js ***!
  \**********************************/
/*! exports provided: MapChunk */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MapChunk", function() { return MapChunk; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rng__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/rng */ "./src/js/core/rng.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./colors */ "./src/js/game/colors.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../core/rectangle */ "./src/js/core/rectangle.js");













const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("map_chunk");

class MapChunk {
    /**
     *
     * @param {GameRoot} root
     * @param {number} x
     * @param {number} y
     */
    constructor(root, x, y) {
        this.root = root;
        this.x = x;
        this.y = y;
        this.tileX = x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize;
        this.tileY = y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize;

        /**
         * Stores the contents of the lower (= map resources) layer
         *  @type {Array<Array<?BaseItem>>}
         */
        this.lowerLayer = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["make2DUndefinedArray"])(_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);

        /**
         * Stores the contents of the regular layer
         * @type {Array<Array<?Entity>>}
         */
        this.contents = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["make2DUndefinedArray"])(_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);

        /**
         * Stores the contents of the wires layer
         *  @type {Array<Array<?Entity>>}
         */
        this.wireContents = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["make2DUndefinedArray"])(_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);

        /** @type {Array<Entity>} */
        this.containedEntities = [];

        /**
         * World space rectangle, can be used for culling
         */
        this.worldSpaceRectangle = new _core_rectangle__WEBPACK_IMPORTED_MODULE_11__["Rectangle"](
            this.tileX * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            this.tileY * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize,
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize
        );

        /**
         * Tile space rectangle, can be used for culling
         */
        this.tileSpaceRectangle = new _core_rectangle__WEBPACK_IMPORTED_MODULE_11__["Rectangle"](
            this.tileX,
            this.tileY,
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize,
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize
        );

        /**
         * Which entities this chunk contains, sorted by layer
         * @type {Record<Layer, Array<Entity>>}
         */
        this.containedEntitiesByLayer = {
            regular: [],
            wires: [],
        };

        /**
         * Store which patches we have so we can render them in the overview
         * @type {Array<{pos: Vector, item: BaseItem, size: number }>}
         */
        this.patches = [];

        this.generateLowerLayer();
    }

    /**
     * Generates a patch filled with the given item
     * @param {RandomNumberGenerator} rng
     * @param {number} patchSize
     * @param {BaseItem} item
     * @param {number=} overrideX Override the X position of the patch
     * @param {number=} overrideY Override the Y position of the patch
     */
    internalGeneratePatch(rng, patchSize, item, overrideX = null, overrideY = null) {
        const border = Math.ceil(patchSize / 2 + 3);

        // Find a position within the chunk which is not blocked
        let patchX = rng.nextIntRange(border, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize - border - 1);
        let patchY = rng.nextIntRange(border, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize - border - 1);

        if (overrideX !== null) {
            patchX = overrideX;
        }

        if (overrideY !== null) {
            patchY = overrideY;
        }

        const avgPos = new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](0, 0);
        let patchesDrawn = 0;

        // Each patch consists of multiple circles
        const numCircles = patchSize;

        for (let i = 0; i <= numCircles; ++i) {
            // Determine circle parameters
            const circleRadius = Math.min(1 + i, patchSize);
            const circleRadiusSquare = circleRadius * circleRadius;
            const circleOffsetRadius = (numCircles - i) / 2 + 2;

            // We draw an elipsis actually
            const circleScaleX = rng.nextRange(0.9, 1.1);
            const circleScaleY = rng.nextRange(0.9, 1.1);

            const circleX = patchX + rng.nextIntRange(-circleOffsetRadius, circleOffsetRadius);
            const circleY = patchY + rng.nextIntRange(-circleOffsetRadius, circleOffsetRadius);

            for (let dx = -circleRadius * circleScaleX - 2; dx <= circleRadius * circleScaleX + 2; ++dx) {
                for (let dy = -circleRadius * circleScaleY - 2; dy <= circleRadius * circleScaleY + 2; ++dy) {
                    const x = Math.round(circleX + dx);
                    const y = Math.round(circleY + dy);
                    if (x >= 0 && x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize && y >= 0 && y <= _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize) {
                        const originalDx = dx / circleScaleX;
                        const originalDy = dy / circleScaleY;
                        if (originalDx * originalDx + originalDy * originalDy <= circleRadiusSquare) {
                            if (!this.lowerLayer[x][y]) {
                                this.lowerLayer[x][y] = item;
                                ++patchesDrawn;
                                avgPos.x += x;
                                avgPos.y += y;
                            }
                        }
                    } else {
                        // logger.warn("Tried to spawn resource out of chunk");
                    }
                }
            }
        }

        this.patches.push({
            pos: avgPos.divideScalar(patchesDrawn),
            item,
            size: patchSize,
        });
    }

    /**
     * Generates a color patch
     * @param {RandomNumberGenerator} rng
     * @param {number} colorPatchSize
     * @param {number} distanceToOriginInChunks
     */
    internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks) {
        // First, determine available colors
        let availableColors = [_colors__WEBPACK_IMPORTED_MODULE_6__["enumColors"].red, _colors__WEBPACK_IMPORTED_MODULE_6__["enumColors"].green];
        if (distanceToOriginInChunks > 2) {
            availableColors.push(_colors__WEBPACK_IMPORTED_MODULE_6__["enumColors"].blue);
        }
        this.internalGeneratePatch(rng, colorPatchSize, _items_color_item__WEBPACK_IMPORTED_MODULE_8__["COLOR_ITEM_SINGLETONS"][rng.choice(availableColors)]);
    }

    /**
     * Generates a shape patch
     * @param {RandomNumberGenerator} rng
     * @param {number} shapePatchSize
     * @param {number} distanceToOriginInChunks
     */
    internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
        /** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
        let subShapes = null;

        let weights = {};

        // Later there is a mix of everything
        weights = {
            [_shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].rect]: 100,
            [_shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].circle]: Math.round(50 + Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks * 2, 0, 50)),
            [_shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].star]: Math.round(20 + Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks, 0, 30)),
            [_shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].windmill]: Math.round(6 + Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks / 2, 0, 20)),
        };

        if (distanceToOriginInChunks < 7) {
            // Initial chunks can not spawn the good stuff
            weights[_shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].star] = 0;
            weights[_shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].windmill] = 0;
        }

        if (distanceToOriginInChunks < 10) {
            // Initial chunk patches always have the same shape
            const subShape = this.internalGenerateRandomSubShape(rng, weights);
            subShapes = [subShape, subShape, subShape, subShape];
        } else if (distanceToOriginInChunks < 15) {
            // Later patches can also have mixed ones
            const subShapeA = this.internalGenerateRandomSubShape(rng, weights);
            const subShapeB = this.internalGenerateRandomSubShape(rng, weights);
            subShapes = [subShapeA, subShapeA, subShapeB, subShapeB];
        } else {
            // Finally there is a mix of everything
            subShapes = [
                this.internalGenerateRandomSubShape(rng, weights),
                this.internalGenerateRandomSubShape(rng, weights),
                this.internalGenerateRandomSubShape(rng, weights),
                this.internalGenerateRandomSubShape(rng, weights),
            ];
        }

        // Makes sure windmills never spawn as whole
        let windmillCount = 0;
        for (let i = 0; i < subShapes.length; ++i) {
            if (subShapes[i] === _shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].windmill) {
                ++windmillCount;
            }
        }
        if (windmillCount > 1) {
            subShapes[0] = _shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].rect;
            subShapes[1] = _shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].rect;
        }

        const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes);
        this.internalGeneratePatch(
            rng,
            shapePatchSize,
            this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition)
        );
    }

    /**
     * Chooses a random shape with the given weights
     * @param {RandomNumberGenerator} rng
     * @param {Object.<enumSubShape, number>} weights
     * @returns {enumSubShape}
     */
    internalGenerateRandomSubShape(rng, weights) {
        // @ts-ignore
        const sum = Object.values(weights).reduce((a, b) => a + b, 0);

        const chosenNumber = rng.nextIntRange(0, sum - 1);
        let accumulated = 0;
        for (const key in weights) {
            const weight = weights[key];
            if (accumulated + weight > chosenNumber) {
                return key;
            }
            accumulated += weight;
        }

        logger.error("Failed to find matching shape in chunk generation");
        return _shape_definition__WEBPACK_IMPORTED_MODULE_10__["enumSubShape"].circle;
    }

    /**
     * Generates the lower layer "terrain"
     */
    generateLowerLayer() {
        const rng = new _core_rng__WEBPACK_IMPORTED_MODULE_2__["RandomNumberGenerator"](this.x + "|" + this.y + "|" + this.root.map.seed);

        if (this.generatePredefined(rng)) {
            return;
        }

        const chunkCenter = new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](this.x, this.y).addScalar(0.5);
        const distanceToOriginInChunks = Math.round(chunkCenter.length());

        // Determine how likely it is that there is a color patch
        const colorPatchChance = 0.9 - Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks / 25, 0, 1) * 0.5;

        if (rng.next() < colorPatchChance / 4) {
            const colorPatchSize = Math.max(2, Math.round(1 + Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks / 8, 0, 4)));
            this.internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks);
        }

        // Determine how likely it is that there is a shape patch
        const shapePatchChance = 0.9 - Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks / 25, 0, 1) * 0.5;
        if (rng.next() < shapePatchChance / 4) {
            const shapePatchSize = Math.max(2, Math.round(1 + Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])(distanceToOriginInChunks / 8, 0, 4)));
            this.internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks);
        }
    }

    /**
     * Checks if this chunk has predefined contents, and if so returns true and generates the
     * predefined contents
     * @param {RandomNumberGenerator} rng
     * @returns {boolean}
     */
    generatePredefined(rng) {
        if (this.x === 0 && this.y === 0) {
            this.internalGeneratePatch(rng, 2, _items_color_item__WEBPACK_IMPORTED_MODULE_8__["COLOR_ITEM_SINGLETONS"][_colors__WEBPACK_IMPORTED_MODULE_6__["enumColors"].red], 7, 7);
            return true;
        }
        if (this.x === -1 && this.y === 0) {
            const item = this.root.shapeDefinitionMgr.getShapeItemFromShortKey("CuCuCuCu");
            this.internalGeneratePatch(rng, 2, item, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize - 9, 7);
            return true;
        }
        if (this.x === 0 && this.y === -1) {
            const item = this.root.shapeDefinitionMgr.getShapeItemFromShortKey("RuRuRuRu");
            this.internalGeneratePatch(rng, 2, item, 5, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize - 7);
            return true;
        }

        if (this.x === -1 && this.y === -1) {
            this.internalGeneratePatch(rng, 2, _items_color_item__WEBPACK_IMPORTED_MODULE_8__["COLOR_ITEM_SINGLETONS"][_colors__WEBPACK_IMPORTED_MODULE_6__["enumColors"].green]);
            return true;
        }

        if (this.x === 5 && this.y === -2) {
            const item = this.root.shapeDefinitionMgr.getShapeItemFromShortKey("SuSuSuSu");
            this.internalGeneratePatch(rng, 2, item, 5, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize - 7);
            return true;
        }

        return false;
    }

    /**
     *
     * @param {number} worldX
     * @param {number} worldY
     * @returns {BaseItem=}
     */
    getLowerLayerFromWorldCoords(worldX, worldY) {
        const localX = worldX - this.tileX;
        const localY = worldY - this.tileY;
        window.assert(localX >= 0, "Local X is < 0");
        window.assert(localY >= 0, "Local Y is < 0");
        window.assert(localX < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local X is >= chunk size");
        window.assert(localY < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local Y is >= chunk size");
        return this.lowerLayer[localX][localY] || null;
    }

    /**
     * Returns the contents of this chunk from the given world space coordinates
     * @param {number} worldX
     * @param {number} worldY
     * @returns {Entity=}
     */
    getTileContentFromWorldCoords(worldX, worldY) {
        const localX = worldX - this.tileX;
        const localY = worldY - this.tileY;
        window.assert(localX >= 0, "Local X is < 0");
        window.assert(localY >= 0, "Local Y is < 0");
        window.assert(localX < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local X is >= chunk size");
        window.assert(localY < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local Y is >= chunk size");
        return this.contents[localX][localY] || null;
    }

    /**
     * Returns the contents of this chunk from the given world space coordinates
     * @param {number} worldX
     * @param {number} worldY
     * @param {Layer} layer
     * @returns {Entity=}
     */
    getLayerContentFromWorldCoords(worldX, worldY, layer) {
        const localX = worldX - this.tileX;
        const localY = worldY - this.tileY;
        window.assert(localX >= 0, "Local X is < 0");
        window.assert(localY >= 0, "Local Y is < 0");
        window.assert(localX < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local X is >= chunk size");
        window.assert(localY < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local Y is >= chunk size");
        if (layer === "regular") {
            return this.contents[localX][localY] || null;
        } else {
            return this.wireContents[localX][localY] || null;
        }
    }
    /**
     * Returns the contents of this chunk from the given world space coordinates
     * @param {number} worldX
     * @param {number} worldY
     * @returns {Array<Entity>}
     */
    getLayersContentsMultipleFromWorldCoords(worldX, worldY) {
        const localX = worldX - this.tileX;
        const localY = worldY - this.tileY;
        window.assert(localX >= 0, "Local X is < 0");
        window.assert(localY >= 0, "Local Y is < 0");
        window.assert(localX < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local X is >= chunk size");
        window.assert(localY < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local Y is >= chunk size");

        const regularContent = this.contents[localX][localY];
        const wireContent = this.wireContents[localX][localY];

        const result = [];
        if (regularContent) {
            result.push(regularContent);
        }
        if (wireContent) {
            result.push(wireContent);
        }
        return result;
    }

    /**
     * Returns the chunks contents from the given local coordinates
     * @param {number} localX
     * @param {number} localY
     * @returns {Entity=}
     */
    getTileContentFromLocalCoords(localX, localY) {
        window.assert(localX >= 0, "Local X is < 0");
        window.assert(localY >= 0, "Local Y is < 0");
        window.assert(localX < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local X is >= chunk size");
        window.assert(localY < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local Y is >= chunk size");

        return this.contents[localX][localY] || null;
    }

    /**
     * Sets the chunks contents
     * @param {number} tileX
     * @param {number} tileY
     * @param {Entity=} contents
     * @param {Layer} layer
     */
    setLayerContentFromWorldCords(tileX, tileY, contents, layer) {
        const localX = tileX - this.tileX;
        const localY = tileY - this.tileY;
        window.assert(localX >= 0, "Local X is < 0");
        window.assert(localY >= 0, "Local Y is < 0");
        window.assert(localX < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local X is >= chunk size");
        window.assert(localY < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize, "Local Y is >= chunk size");

        let oldContents;
        if (layer === "regular") {
            oldContents = this.contents[localX][localY];
        } else {
            oldContents = this.wireContents[localX][localY];
        }

        window.assert(contents === null || !oldContents, "Tile already used: " + tileX + " / " + tileY);

        if (oldContents) {
            // Remove from list (the old contents must be reigstered)
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["fastArrayDeleteValueIfContained"])(this.containedEntities, oldContents);
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["fastArrayDeleteValueIfContained"])(this.containedEntitiesByLayer[layer], oldContents);
        }

        if (layer === "regular") {
            this.contents[localX][localY] = contents;
        } else {
            this.wireContents[localX][localY] = contents;
        }

        if (contents) {
            if (this.containedEntities.indexOf(contents) < 0) {
                this.containedEntities.push(contents);
            }

            if (this.containedEntitiesByLayer[layer].indexOf(contents) < 0) {
                this.containedEntitiesByLayer[layer].push(contents);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/map_chunk_view.js":
/*!***************************************!*\
  !*** ./src/js/game/map_chunk_view.js ***!
  \***************************************/
/*! exports provided: CHUNK_OVERLAY_RES, MapChunkView */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CHUNK_OVERLAY_RES", function() { return CHUNK_OVERLAY_RES; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MapChunkView", function() { return MapChunkView; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _map_chunk__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./map_chunk */ "./src/js/game/map_chunk.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./theme */ "./src/js/game/theme.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../core/draw_utils */ "./src/js/core/draw_utils.js");









const CHUNK_OVERLAY_RES = 3;

class MapChunkView extends _map_chunk__WEBPACK_IMPORTED_MODULE_4__["MapChunk"] {
    /**
     *
     * @param {GameRoot} root
     * @param {number} x
     * @param {number} y
     */
    constructor(root, x, y) {
        super(root, x, y);

        /**
         * Whenever something changes, we increase this number - so we know we need to redraw
         */
        this.renderIteration = 0;

        this.markDirty();
    }

    /**
     * Marks this chunk as dirty, rerendering all caches
     */
    markDirty() {
        ++this.renderIteration;
        this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration;
    }

    /**
     * Draws the background layer
     * @param {DrawParameters} parameters
     */
    drawBackgroundLayer(parameters) {
        const systems = this.root.systemMgr.systems;
        systems.mapResources.drawChunk(parameters, this);
        systems.beltUnderlays.drawChunk(parameters, this);
        systems.belt.drawChunk(parameters, this);
    }

    /**
     * Draws the dynamic foreground layer
     * @param {DrawParameters} parameters
     */
    drawForegroundDynamicLayer(parameters) {
        const systems = this.root.systemMgr.systems;

        systems.itemEjector.drawChunk(parameters, this);
        systems.itemAcceptor.drawChunk(parameters, this);
        systems.miner.drawChunk(parameters, this);
    }

    /**
     * Draws the static foreground layer
     * @param {DrawParameters} parameters
     */
    drawForegroundStaticLayer(parameters) {
        const systems = this.root.systemMgr.systems;

        systems.staticMapEntities.drawChunk(parameters, this);
        systems.lever.drawChunk(parameters, this);
        systems.display.drawChunk(parameters, this);
        systems.storage.drawChunk(parameters, this);
        systems.itemProcessorOverlays.drawChunk(parameters, this);
    }

    /**
     * Overlay
     * @param {DrawParameters} parameters
     */
    drawOverlay(parameters) {
        const overlaySize = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize * CHUNK_OVERLAY_RES;
        const sprite = this.root.buffers.getForKey({
            key: "chunk@" + this.root.currentLayer,
            subKey: this.renderKey,
            w: overlaySize,
            h: overlaySize,
            dpi: 1,
            redrawMethod: this.generateOverlayBuffer.bind(this),
        });

        const dims = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize;
        const extrude = 0.05;

        // Draw chunk "pixel" art
        parameters.context.imageSmoothingEnabled = false;
        Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_7__["drawSpriteClipped"])({
            parameters,
            sprite,
            x: this.x * dims - extrude,
            y: this.y * dims - extrude,
            w: dims + 2 * extrude,
            h: dims + 2 * extrude,
            originalW: overlaySize,
            originalH: overlaySize,
        });

        parameters.context.imageSmoothingEnabled = true;
        const resourcesScale = this.root.app.settings.getAllSettings().mapResourcesScale;

        // Draw patch items
        if (this.root.currentLayer === "regular" && resourcesScale > 0.05) {
            const diameter = (70 / Math.pow(parameters.zoomLevel, 0.35)) * (0.2 + 2 * resourcesScale);

            for (let i = 0; i < this.patches.length; ++i) {
                const patch = this.patches[i];
                if (patch.item.getItemType() === "shape") {
                    const destX = this.x * dims + patch.pos.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                    const destY = this.y * dims + patch.pos.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                    patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
                }
            }
        }
    }

    /**
     *
     * @param {HTMLCanvasElement} canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} w
     * @param {number} h
     * @param {number} dpi
     */
    generateOverlayBuffer(canvas, context, w, h, dpi) {
        context.fillStyle =
            this.containedEntities.length > 0
                ? _theme__WEBPACK_IMPORTED_MODULE_6__["THEME"].map.chunkOverview.filled
                : _theme__WEBPACK_IMPORTED_MODULE_6__["THEME"].map.chunkOverview.empty;
        context.fillRect(0, 0, w, h);

        if (this.root.app.settings.getAllSettings().displayChunkBorders) {
            context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_6__["THEME"].map.chunkBorders;
            context.fillRect(0, 0, w, 1);
            context.fillRect(0, 1, 1, h);
        }

        for (let x = 0; x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++x) {
            const lowerArray = this.lowerLayer[x];
            const upperArray = this.contents[x];
            for (let y = 0; y < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++y) {
                const upperContent = upperArray[y];
                if (upperContent) {
                    const staticComp = upperContent.components.StaticMapEntity;
                    const data = Object(_building_codes__WEBPACK_IMPORTED_MODULE_2__["getBuildingDataFromCode"])(staticComp.code);
                    const metaBuilding = data.metaInstance;

                    const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
                        staticComp.rotation,
                        data.rotationVariant,
                        data.variant,
                        upperContent
                    );

                    if (overlayMatrix) {
                        // Draw lower content first since it "shines" through
                        const lowerContent = lowerArray[y];
                        if (lowerContent) {
                            context.fillStyle = lowerContent.getBackgroundColorAsResource();
                            context.fillRect(
                                x * CHUNK_OVERLAY_RES,
                                y * CHUNK_OVERLAY_RES,
                                CHUNK_OVERLAY_RES,
                                CHUNK_OVERLAY_RES
                            );
                        }

                        context.fillStyle = metaBuilding.getSilhouetteColor(
                            data.variant,
                            data.rotationVariant
                        );
                        for (let dx = 0; dx < 3; ++dx) {
                            for (let dy = 0; dy < 3; ++dy) {
                                const isFilled = overlayMatrix[dx + dy * 3];
                                if (isFilled) {
                                    context.fillRect(
                                        x * CHUNK_OVERLAY_RES + dx,
                                        y * CHUNK_OVERLAY_RES + dy,
                                        1,
                                        1
                                    );
                                }
                            }
                        }

                        continue;
                    } else {
                        context.fillStyle = metaBuilding.getSilhouetteColor(
                            data.variant,
                            data.rotationVariant
                        );
                        context.fillRect(
                            x * CHUNK_OVERLAY_RES,
                            y * CHUNK_OVERLAY_RES,
                            CHUNK_OVERLAY_RES,
                            CHUNK_OVERLAY_RES
                        );

                        continue;
                    }
                }

                const lowerContent = lowerArray[y];
                if (lowerContent) {
                    context.fillStyle = lowerContent.getBackgroundColorAsResource();
                    context.fillRect(
                        x * CHUNK_OVERLAY_RES,
                        y * CHUNK_OVERLAY_RES,
                        CHUNK_OVERLAY_RES,
                        CHUNK_OVERLAY_RES
                    );
                }
            }
        }

        if (this.root.currentLayer === "wires") {
            // Draw wires overlay

            context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_6__["THEME"].map.wires.overlayColor;
            context.fillRect(0, 0, w, h);

            for (let x = 0; x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++x) {
                const wiresArray = this.wireContents[x];
                for (let y = 0; y < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++y) {
                    const content = wiresArray[y];
                    if (!content) {
                        continue;
                    }
                    MapChunkView.drawSingleWiresOverviewTile({
                        context,
                        x: x * CHUNK_OVERLAY_RES,
                        y: y * CHUNK_OVERLAY_RES,
                        entity: content,
                        tileSizePixels: CHUNK_OVERLAY_RES,
                    });
                }
            }
        }
    }

    /**
     * @param {object} param0
     * @param {CanvasRenderingContext2D} param0.context
     * @param {number} param0.x
     * @param {number} param0.y
     * @param {Entity} param0.entity
     * @param {number} param0.tileSizePixels
     * @param {string=} param0.overrideColor Optionally override the color to be rendered
     */
    static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) {
        const staticComp = entity.components.StaticMapEntity;
        const data = Object(_building_codes__WEBPACK_IMPORTED_MODULE_2__["getBuildingDataFromCode"])(staticComp.code);
        const metaBuilding = data.metaInstance;
        const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
            staticComp.rotation,
            data.rotationVariant,
            data.variant,
            entity
        );
        context.fillStyle =
            overrideColor || metaBuilding.getSilhouetteColor(data.variant, data.rotationVariant);
        if (overlayMatrix) {
            for (let dx = 0; dx < 3; ++dx) {
                for (let dy = 0; dy < 3; ++dy) {
                    const isFilled = overlayMatrix[dx + dy * 3];
                    if (isFilled) {
                        context.fillRect(
                            x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES,
                            y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES,
                            tileSizePixels / CHUNK_OVERLAY_RES,
                            tileSizePixels / CHUNK_OVERLAY_RES
                        );
                    }
                }
            }
        } else {
            context.fillRect(x, y, tileSizePixels, tileSizePixels);
        }
    }

    /**
     * Draws the wires layer
     * @param {DrawParameters} parameters
     */
    drawWiresForegroundLayer(parameters) {
        const systems = this.root.systemMgr.systems;
        systems.wire.drawChunk(parameters, this);
        systems.staticMapEntities.drawWiresChunk(parameters, this);
        systems.wiredPins.drawChunk(parameters, this);
    }
}


/***/ }),

/***/ "./src/js/game/map_view.js":
/*!*********************************!*\
  !*** ./src/js/game/map_view.js ***!
  \*********************************/
/*! exports provided: MapView */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MapView", function() { return MapView; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./map */ "./src/js/game/map.js");
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./theme */ "./src/js/game/theme.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./map_chunk_view */ "./src/js/game/map_chunk_view.js");








/**
 * This is the view of the map, it extends the map which is the raw model and allows
 * to draw it
 */
class MapView extends _map__WEBPACK_IMPORTED_MODULE_2__["BaseMap"] {
    constructor(root) {
        super(root);

        /**
         * DPI of the background cache images, required in some places
         */
        this.backgroundCacheDPI = 2;

        /**
         * The cached background sprite, containing the flat background
         * @type {HTMLCanvasElement} */
        this.cachedBackgroundCanvas = null;

        /** @type {CanvasRenderingContext2D} */
        this.cachedBackgroundContext = null;
        this.internalInitializeCachedBackgroundCanvases();
        this.root.signals.aboutToDestruct.add(this.cleanup, this);

        this.root.signals.entityAdded.add(this.onEntityChanged, this);
        this.root.signals.entityDestroyed.add(this.onEntityChanged, this);
        this.root.signals.entityChanged.add(this.onEntityChanged, this);
    }

    cleanup() {
        Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_3__["freeCanvas"])(this.cachedBackgroundCanvas);
        this.cachedBackgroundCanvas = null;
    }

    /**
     * Called when an entity was added, removed or changed
     * @param {Entity} entity
     */
    onEntityChanged(entity) {
        const staticComp = entity.components.StaticMapEntity;
        if (staticComp) {
            const rect = staticComp.getTileSpaceBounds();
            for (let x = rect.x; x <= rect.right(); ++x) {
                for (let y = rect.y; y <= rect.bottom(); ++y) {
                    this.root.map.getOrCreateChunkAtTile(x, y).markDirty();
                }
            }
        }
    }

    /**
     * Draws all static entities like buildings etc.
     * @param {DrawParameters} drawParameters
     */
    drawStaticEntityDebugOverlays(drawParameters) {
        if (false) {}
    }

    /**
     * Initializes all canvases used for background rendering
     */
    internalInitializeCachedBackgroundCanvases() {
        // Background canvas
        const dims = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
        const dpi = this.backgroundCacheDPI;
        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_3__["makeOffscreenBuffer"])(dims * dpi, dims * dpi, {
            smooth: false,
            label: "map-cached-bg",
        });
        context.scale(dpi, dpi);

        context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.background;
        context.fillRect(0, 0, dims, dims);

        const borderWidth = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.gridLineWidth;
        context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_5__["THEME"].map.grid;
        context.fillRect(0, 0, dims, borderWidth);
        context.fillRect(0, borderWidth, borderWidth, dims);

        context.fillRect(dims - borderWidth, borderWidth, borderWidth, dims - 2 * borderWidth);
        context.fillRect(borderWidth, dims - borderWidth, dims, borderWidth);

        this.cachedBackgroundCanvas = canvas;
        this.cachedBackgroundContext = context;
    }

    /**
     * Draws the maps foreground
     * @param {DrawParameters} parameters
     */
    drawForeground(parameters) {
        this.drawVisibleChunks(parameters, _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__["MapChunkView"].prototype.drawForegroundDynamicLayer);
        this.drawVisibleChunks(parameters, _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__["MapChunkView"].prototype.drawForegroundStaticLayer);
    }

    /**
     * Calls a given method on all given chunks
     * @param {DrawParameters} parameters
     * @param {function} method
     */
    drawVisibleChunks(parameters, method) {
        const cullRange = parameters.visibleRect.allScaled(1 / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize);
        const top = cullRange.top();
        const right = cullRange.right();
        const bottom = cullRange.bottom();
        const left = cullRange.left();

        const border = 0;
        const minY = top - border;
        const maxY = bottom + border;
        const minX = left - border;
        const maxX = right + border;

        const chunkStartX = Math.floor(minX / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);
        const chunkStartY = Math.floor(minY / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);

        const chunkEndX = Math.floor(maxX / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);
        const chunkEndY = Math.floor(maxY / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize);

        // Render y from top down for proper blending
        for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
            for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
                const chunk = this.root.map.getChunk(chunkX, chunkY, true);
                method.call(chunk, parameters);
            }
        }
    }

    /**
     * Draws the wires foreground
     * @param {DrawParameters} parameters
     */
    drawWiresForegroundLayer(parameters) {
        this.drawVisibleChunks(parameters, _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__["MapChunkView"].prototype.drawWiresForegroundLayer);
    }

    /**
     * Draws the map overlay
     * @param {DrawParameters} parameters
     */
    drawOverlay(parameters) {
        this.drawVisibleChunks(parameters, _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__["MapChunkView"].prototype.drawOverlay);
    }

    /**
     * Draws the map background
     * @param {DrawParameters} parameters
     */
    drawBackground(parameters) {
        // Render tile grid
        if (!this.root.app.settings.getAllSettings().disableTileGrid) {
            const dpi = this.backgroundCacheDPI;
            parameters.context.scale(1 / dpi, 1 / dpi);

            parameters.context.fillStyle = parameters.context.createPattern(
                this.cachedBackgroundCanvas,
                "repeat"
            );
            parameters.context.fillRect(
                parameters.visibleRect.x * dpi,
                parameters.visibleRect.y * dpi,
                parameters.visibleRect.w * dpi,
                parameters.visibleRect.h * dpi
            );
            parameters.context.scale(dpi, dpi);
        }

        this.drawVisibleChunks(parameters, _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__["MapChunkView"].prototype.drawBackgroundLayer);
    }
}


/***/ }),

/***/ "./src/js/game/meta_building.js":
/*!**************************************!*\
  !*** ./src/js/game/meta_building.js ***!
  \**************************************/
/*! exports provided: defaultBuildingVariant, MetaBuilding */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultBuildingVariant", function() { return defaultBuildingVariant; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MetaBuilding", function() { return MetaBuilding; });
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _components_static_map_entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/static_map_entity */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./entity */ "./src/js/game/entity.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./building_codes */ "./src/js/game/building_codes.js");









const defaultBuildingVariant = "default";

class MetaBuilding {
    /**
     *
     * @param {string} id Building id
     */
    constructor(id) {
        this.id = id;
    }

    /**
     * Returns the id of this building
     */
    getId() {
        return this.id;
    }

    /**
     * Returns the edit layer of the building
     * @returns {Layer}
     */
    getLayer() {
        return "regular";
    }

    /**
     * Should return the dimensions of the building
     */
    getDimensions(variant = defaultBuildingVariant) {
        return new _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](1, 1);
    }

    /**
     * Returns whether the building has the direction lock switch available
     */
    getHasDirectionLockAvailable() {
        return false;
    }

    /**
     * Whether to stay in placement mode after having placed a building
     */
    getStayInPlacementMode() {
        return false;
    }

    /**
     * Can return a special interlaved 9 elements overlay matrix for rendering
     * @param {number} rotation
     * @param {number} rotationVariant
     * @param {string} variant
     * @param {Entity} entity
     * @returns {Array<number>|null}
     */
    getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
        return null;
    }

    /**
     * Should return additional statistics about this building
     * @param {GameRoot} root
     * @param {string} variant
     * @returns {Array<[string, string]>}
     */
    getAdditionalStatistics(root, variant) {
        return [];
    }

    /**
     * Returns whether this building can get replaced
     */
    getIsReplaceable() {
        return false;
    }

    /**
     * Whether to flip the orientation after a building has been placed - useful
     * for tunnels.
     */
    getFlipOrientationAfterPlacement() {
        return false;
    }

    /**
     * Whether to show a preview of the wires layer when placing the building
     */
    getShowWiresLayerPreview() {
        return false;
    }

    /**
     * Whether to rotate automatically in the dragging direction while placing
     * @param {string} variant
     */
    getRotateAutomaticallyWhilePlacing(variant) {
        return false;
    }

    /**
     * Returns whether this building is removable
     * @returns {boolean}
     */
    getIsRemovable() {
        return true;
    }

    /**
     * Returns the placement sound
     * @returns {string}
     */
    getPlacementSound() {
        return _platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].placeBuilding;
    }

    /**
     * @param {GameRoot} root
     */
    getAvailableVariants(root) {
        return [defaultBuildingVariant];
    }

    /**
     * Returns a preview sprite
     * @returns {AtlasSprite}
     */
    getPreviewSprite(rotationVariant = 0, variant = defaultBuildingVariant) {
        return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite(
            "sprites/buildings/" +
                this.id +
                (variant === defaultBuildingVariant ? "" : "-" + variant) +
                ".png"
        );
    }

    /**
     * Returns a sprite for blueprints
     * @returns {AtlasSprite}
     */
    getBlueprintSprite(rotationVariant = 0, variant = defaultBuildingVariant) {
        return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite(
            "sprites/blueprints/" +
                this.id +
                (variant === defaultBuildingVariant ? "" : "-" + variant) +
                ".png"
        );
    }

    /**
     * Returns whether this building is rotateable
     * @param {string} variant
     * @returns {boolean}
     */
    getIsRotateable(variant) {
        return true;
    }

    /**
     * Returns whether this building is unlocked for the given game
     * @param {GameRoot} root
     */
    getIsUnlocked(root) {
        return true;
    }

    /**
     * Should return a silhouette color for the map overview or null if not set
     * @param {string} variant
     * @param {number} rotationVariant
     */
    getSilhouetteColor(variant, rotationVariant) {
        return null;
    }

    /**
     * Should return false if the pins are already included in the sprite of the building
     * @returns {boolean}
     */
    getRenderPins() {
        return true;
    }

    /**
     * Creates the entity without placing it
     * @param {object} param0
     * @param {GameRoot} param0.root
     * @param {Vector} param0.origin Origin tile
     * @param {number=} param0.rotation Rotation
     * @param {number} param0.originalRotation Original Rotation
     * @param {number} param0.rotationVariant Rotation variant
     * @param {string} param0.variant
     */
    createEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) {
        const entity = new _entity__WEBPACK_IMPORTED_MODULE_5__["Entity"](root);
        entity.layer = this.getLayer();
        entity.addComponent(
            new _components_static_map_entity__WEBPACK_IMPORTED_MODULE_4__["StaticMapEntityComponent"]({
                origin: new _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"](origin.x, origin.y),
                rotation,
                originalRotation,
                tileSize: this.getDimensions(variant).copy(),
                code: Object(_building_codes__WEBPACK_IMPORTED_MODULE_7__["getCodeFromBuildingData"])(this, variant, rotationVariant),
            })
        );
        this.setupEntityComponents(entity, root);
        this.updateVariants(entity, rotationVariant, variant);
        return entity;
    }

    /**
     * Returns the sprite for a given variant
     * @param {number} rotationVariant
     * @param {string} variant
     * @returns {AtlasSprite}
     */
    getSprite(rotationVariant, variant) {
        return _core_loader__WEBPACK_IMPORTED_MODULE_0__["Loader"].getSprite(
            "sprites/buildings/" +
                this.id +
                (variant === defaultBuildingVariant ? "" : "-" + variant) +
                ".png"
        );
    }

    /**
     * Should compute the optimal rotation variant on the given tile
     * @param {object} param0
     * @param {GameRoot} param0.root
     * @param {Vector} param0.tile
     * @param {number} param0.rotation
     * @param {string} param0.variant
     * @param {Layer} param0.layer
     * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
     */
    computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
        if (!this.getIsRotateable(variant)) {
            return {
                rotation: 0,
                rotationVariant: 0,
            };
        }
        return {
            rotation,
            rotationVariant: 0,
        };
    }

    /**
     * Should update the entity to match the given variants
     * @param {Entity} entity
     * @param {number} rotationVariant
     * @param {string} variant
     */
    updateVariants(entity, rotationVariant, variant) {}

    // PRIVATE INTERFACE

    /**
     * Should setup the entity components
     * @param {Entity} entity
     * @param {GameRoot} root
     */
    setupEntityComponents(entity, root) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }
}


/***/ }),

/***/ "./src/js/game/meta_building_registry.js":
/*!***********************************************!*\
  !*** ./src/js/game/meta_building_registry.js ***!
  \***********************************************/
/*! exports provided: initMetaBuildingRegistry, initBuildingCodesAfterResourcesLoaded */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initMetaBuildingRegistry", function() { return initMetaBuildingRegistry; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "initBuildingCodesAfterResourcesLoaded", function() { return initBuildingCodesAfterResourcesLoaded; });
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");
/* harmony import */ var _buildings_analyzer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./buildings/analyzer */ "./src/js/game/buildings/analyzer.js");
/* harmony import */ var _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./buildings/balancer */ "./src/js/game/buildings/balancer.js");
/* harmony import */ var _buildings_belt__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./buildings/belt */ "./src/js/game/buildings/belt.js");
/* harmony import */ var _buildings_comparator__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./buildings/comparator */ "./src/js/game/buildings/comparator.js");
/* harmony import */ var _buildings_constant_signal__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./buildings/constant_signal */ "./src/js/game/buildings/constant_signal.js");
/* harmony import */ var _buildings_cutter__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./buildings/cutter */ "./src/js/game/buildings/cutter.js");
/* harmony import */ var _buildings_display__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./buildings/display */ "./src/js/game/buildings/display.js");
/* harmony import */ var _buildings_filter__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./buildings/filter */ "./src/js/game/buildings/filter.js");
/* harmony import */ var _buildings_hub__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./buildings/hub */ "./src/js/game/buildings/hub.js");
/* harmony import */ var _buildings_item_producer__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./buildings/item_producer */ "./src/js/game/buildings/item_producer.js");
/* harmony import */ var _buildings_lever__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./buildings/lever */ "./src/js/game/buildings/lever.js");
/* harmony import */ var _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./buildings/logic_gate */ "./src/js/game/buildings/logic_gate.js");
/* harmony import */ var _buildings_miner__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./buildings/miner */ "./src/js/game/buildings/miner.js");
/* harmony import */ var _buildings_mixer__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./buildings/mixer */ "./src/js/game/buildings/mixer.js");
/* harmony import */ var _buildings_painter__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./buildings/painter */ "./src/js/game/buildings/painter.js");
/* harmony import */ var _buildings_reader__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./buildings/reader */ "./src/js/game/buildings/reader.js");
/* harmony import */ var _buildings_rotater__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./buildings/rotater */ "./src/js/game/buildings/rotater.js");
/* harmony import */ var _buildings_stacker__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./buildings/stacker */ "./src/js/game/buildings/stacker.js");
/* harmony import */ var _buildings_storage__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./buildings/storage */ "./src/js/game/buildings/storage.js");
/* harmony import */ var _buildings_transistor__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./buildings/transistor */ "./src/js/game/buildings/transistor.js");
/* harmony import */ var _buildings_trash__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./buildings/trash */ "./src/js/game/buildings/trash.js");
/* harmony import */ var _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./buildings/underground_belt */ "./src/js/game/buildings/underground_belt.js");
/* harmony import */ var _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./buildings/virtual_processor */ "./src/js/game/buildings/virtual_processor.js");
/* harmony import */ var _buildings_wire__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./buildings/wire */ "./src/js/game/buildings/wire.js");
/* harmony import */ var _buildings_wire_tunnel__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./buildings/wire_tunnel */ "./src/js/game/buildings/wire_tunnel.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _components_wire__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./components/wire */ "./src/js/game/components/wire.js");
/* harmony import */ var _key_action_mapper__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./meta_building */ "./src/js/game/meta_building.js");

































const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("building_registry");

function initMetaBuildingRegistry() {
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["MetaBalancerBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_miner__WEBPACK_IMPORTED_MODULE_15__["MetaMinerBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_cutter__WEBPACK_IMPORTED_MODULE_8__["MetaCutterBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_rotater__WEBPACK_IMPORTED_MODULE_19__["MetaRotaterBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_stacker__WEBPACK_IMPORTED_MODULE_20__["MetaStackerBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_mixer__WEBPACK_IMPORTED_MODULE_16__["MetaMixerBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_painter__WEBPACK_IMPORTED_MODULE_17__["MetaPainterBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_trash__WEBPACK_IMPORTED_MODULE_23__["MetaTrashBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_storage__WEBPACK_IMPORTED_MODULE_21__["MetaStorageBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_belt__WEBPACK_IMPORTED_MODULE_5__["MetaBeltBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["MetaUndergroundBeltBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_hub__WEBPACK_IMPORTED_MODULE_11__["MetaHubBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_constant_signal__WEBPACK_IMPORTED_MODULE_7__["MetaConstantSignalBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["MetaLogicGateBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_lever__WEBPACK_IMPORTED_MODULE_13__["MetaLeverBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_filter__WEBPACK_IMPORTED_MODULE_10__["MetaFilterBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_wire_tunnel__WEBPACK_IMPORTED_MODULE_27__["MetaWireTunnelBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_display__WEBPACK_IMPORTED_MODULE_9__["MetaDisplayBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["MetaVirtualProcessorBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_reader__WEBPACK_IMPORTED_MODULE_18__["MetaReaderBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_transistor__WEBPACK_IMPORTED_MODULE_22__["MetaTransistorBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_analyzer__WEBPACK_IMPORTED_MODULE_3__["MetaAnalyzerBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_comparator__WEBPACK_IMPORTED_MODULE_6__["MetaComparatorBuilding"]);
    _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].register(_buildings_item_producer__WEBPACK_IMPORTED_MODULE_12__["MetaItemProducerBuilding"]);

    // Belt
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(1, _buildings_belt__WEBPACK_IMPORTED_MODULE_5__["MetaBeltBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 0);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(2, _buildings_belt__WEBPACK_IMPORTED_MODULE_5__["MetaBeltBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 1);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(3, _buildings_belt__WEBPACK_IMPORTED_MODULE_5__["MetaBeltBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 2);

    // Balancer
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(4, _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["MetaBalancerBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(5, _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["MetaBalancerBuilding"], _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["enumBalancerVariants"].merger);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(6, _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["MetaBalancerBuilding"], _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["enumBalancerVariants"].mergerInverse);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(47, _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["MetaBalancerBuilding"], _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["enumBalancerVariants"].splitter);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(48, _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["MetaBalancerBuilding"], _buildings_balancer__WEBPACK_IMPORTED_MODULE_4__["enumBalancerVariants"].splitterInverse);

    // Miner
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(7, _buildings_miner__WEBPACK_IMPORTED_MODULE_15__["MetaMinerBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(8, _buildings_miner__WEBPACK_IMPORTED_MODULE_15__["MetaMinerBuilding"], _buildings_miner__WEBPACK_IMPORTED_MODULE_15__["enumMinerVariants"].chainable);

    // Cutter
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(9, _buildings_cutter__WEBPACK_IMPORTED_MODULE_8__["MetaCutterBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(10, _buildings_cutter__WEBPACK_IMPORTED_MODULE_8__["MetaCutterBuilding"], _buildings_cutter__WEBPACK_IMPORTED_MODULE_8__["enumCutterVariants"].quad);

    // Rotater
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(11, _buildings_rotater__WEBPACK_IMPORTED_MODULE_19__["MetaRotaterBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(12, _buildings_rotater__WEBPACK_IMPORTED_MODULE_19__["MetaRotaterBuilding"], _buildings_rotater__WEBPACK_IMPORTED_MODULE_19__["enumRotaterVariants"].ccw);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(13, _buildings_rotater__WEBPACK_IMPORTED_MODULE_19__["MetaRotaterBuilding"], _buildings_rotater__WEBPACK_IMPORTED_MODULE_19__["enumRotaterVariants"].rotate180);

    // Stacker
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(14, _buildings_stacker__WEBPACK_IMPORTED_MODULE_20__["MetaStackerBuilding"]);

    // Mixer
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(15, _buildings_mixer__WEBPACK_IMPORTED_MODULE_16__["MetaMixerBuilding"]);

    // Painter
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(16, _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["MetaPainterBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(17, _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["MetaPainterBuilding"], _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["enumPainterVariants"].mirrored);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(18, _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["MetaPainterBuilding"], _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["enumPainterVariants"].double);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(19, _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["MetaPainterBuilding"], _buildings_painter__WEBPACK_IMPORTED_MODULE_17__["enumPainterVariants"].quad);

    // Trash
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(20, _buildings_trash__WEBPACK_IMPORTED_MODULE_23__["MetaTrashBuilding"]);

    // Storage
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(21, _buildings_storage__WEBPACK_IMPORTED_MODULE_21__["MetaStorageBuilding"]);

    // Underground belt
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(22, _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["MetaUndergroundBeltBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 0);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(23, _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["MetaUndergroundBeltBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 1);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(24, _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["MetaUndergroundBeltBuilding"], _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["enumUndergroundBeltVariants"].tier2, 0);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(25, _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["MetaUndergroundBeltBuilding"], _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_24__["enumUndergroundBeltVariants"].tier2, 1);

    // Hub
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(26, _buildings_hub__WEBPACK_IMPORTED_MODULE_11__["MetaHubBuilding"]);

    // Wire
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(27, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 0);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(28, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 1);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(29, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 2);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(30, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"], 3);

    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(52, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _components_wire__WEBPACK_IMPORTED_MODULE_29__["enumWireVariant"].second, 0);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(53, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _components_wire__WEBPACK_IMPORTED_MODULE_29__["enumWireVariant"].second, 1);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(54, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _components_wire__WEBPACK_IMPORTED_MODULE_29__["enumWireVariant"].second, 2);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(55, _buildings_wire__WEBPACK_IMPORTED_MODULE_26__["MetaWireBuilding"], _components_wire__WEBPACK_IMPORTED_MODULE_29__["enumWireVariant"].second, 3);

    // Constant signal
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(31, _buildings_constant_signal__WEBPACK_IMPORTED_MODULE_7__["MetaConstantSignalBuilding"]);

    // Logic gate
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(32, _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["MetaLogicGateBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(34, _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["MetaLogicGateBuilding"], _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["enumLogicGateVariants"].not);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(35, _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["MetaLogicGateBuilding"], _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["enumLogicGateVariants"].xor);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(36, _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["MetaLogicGateBuilding"], _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_14__["enumLogicGateVariants"].or);

    // Transistor
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(38, _buildings_transistor__WEBPACK_IMPORTED_MODULE_22__["MetaTransistorBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(60, _buildings_transistor__WEBPACK_IMPORTED_MODULE_22__["MetaTransistorBuilding"], _buildings_transistor__WEBPACK_IMPORTED_MODULE_22__["enumTransistorVariants"].mirrored);

    // Lever
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(33, _buildings_lever__WEBPACK_IMPORTED_MODULE_13__["MetaLeverBuilding"]);

    // Filter
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(37, _buildings_filter__WEBPACK_IMPORTED_MODULE_10__["MetaFilterBuilding"]);

    // Wire tunnel
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(39, _buildings_wire_tunnel__WEBPACK_IMPORTED_MODULE_27__["MetaWireTunnelBuilding"]);

    // Display
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(40, _buildings_display__WEBPACK_IMPORTED_MODULE_9__["MetaDisplayBuilding"]);

    // Virtual Processor
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(42, _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["MetaVirtualProcessorBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(44, _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["MetaVirtualProcessorBuilding"], _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["enumVirtualProcessorVariants"].rotater);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(45, _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["MetaVirtualProcessorBuilding"], _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["enumVirtualProcessorVariants"].unstacker);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(50, _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["MetaVirtualProcessorBuilding"], _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["enumVirtualProcessorVariants"].stacker);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(51, _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["MetaVirtualProcessorBuilding"], _buildings_virtual_processor__WEBPACK_IMPORTED_MODULE_25__["enumVirtualProcessorVariants"].painter);

    // Analyzer
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(46, _buildings_comparator__WEBPACK_IMPORTED_MODULE_6__["MetaComparatorBuilding"]);
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(43, _buildings_analyzer__WEBPACK_IMPORTED_MODULE_3__["MetaAnalyzerBuilding"]);

    // Reader
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(49, _buildings_reader__WEBPACK_IMPORTED_MODULE_18__["MetaReaderBuilding"]);

    // Item producer
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["registerBuildingVariant"])(61, _buildings_item_producer__WEBPACK_IMPORTED_MODULE_12__["MetaItemProducerBuilding"]);

    // Propagate instances
    for (const key in _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"]) {
        _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"][key].metaInstance = _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].findByClass(
            _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"][key].metaClass
        );
    }

    for (const key in _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"]) {
        const variant = _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"][key];
        window.assert(variant.metaClass, "Variant has no meta: " + key);

        if (typeof variant.rotationVariant === "undefined") {
            variant.rotationVariant = 0;
        }
        if (typeof variant.variant === "undefined") {
            variant.variant = _meta_building__WEBPACK_IMPORTED_MODULE_31__["defaultBuildingVariant"];
        }
    }

    // Check for valid keycodes
    if (false) {}

    logger.log("Registered", _core_global_registries__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].getNumEntries(), "buildings");
    logger.log("Registered", Object.keys(_building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"]).length, "building codes");
}

/**
 * Once all sprites are loaded, propagates the cache
 */
function initBuildingCodesAfterResourcesLoaded() {
    logger.log("Propagating sprite cache");
    for (const key in _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"]) {
        const variant = _building_codes__WEBPACK_IMPORTED_MODULE_28__["gBuildingVariants"][key];

        variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
        variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
            variant.rotationVariant,
            variant.variant
        );
        variant.silhouetteColor = variant.metaInstance.getSilhouetteColor(
            variant.variant,
            variant.rotationVariant
        );
    }

    // Update caches
    Object(_building_codes__WEBPACK_IMPORTED_MODULE_28__["buildBuildingCodeCache"])();
}


/***/ }),

/***/ "./src/js/game/modes/regular.js":
/*!**************************************!*\
  !*** ./src/js/game/modes/regular.js ***!
  \**************************************/
/*! exports provided: generateLevelDefinitions, RegularGameMode */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "generateLevelDefinitions", function() { return generateLevelDefinitions; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RegularGameMode", function() { return RegularGameMode; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _game_mode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../game_mode */ "./src/js/game/game_mode.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../tutorial_goals */ "./src/js/game/tutorial_goals.js");





const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
const finalGameShape = "RuCw--Cw:----Ru--";
const preparementShape = "CpRpCp--:SwSwSwSw";
const blueprintShape = "CbCbCbRb:CwCwCwCw";

// Tiers need % of the previous tier as requirement too
const tierGrowth = 2.5;

/**
 * Generates all upgrades
 * @returns {Object<string, import("../game_mode").UpgradeTiers>} */
function generateUpgrades(limitedVersion = false) {
    const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1];
    const numEndgameUpgrades = limitedVersion ? 0 : 1000 - fixedImprovements.length - 1;

    function generateInfiniteUnlocks() {
        return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
            required: [
                { shape: preparementShape, amount: 30000 + i * 10000 },
                { shape: finalGameShape, amount: 20000 + i * 5000 },
                { shape: rocketShape, amount: 20000 + i * 5000 },
            ],
            excludePrevious: true,
        }));
    }

    // Fill in endgame upgrades
    for (let i = 0; i < numEndgameUpgrades; ++i) {
        if (i < 20) {
            fixedImprovements.push(0.1);
        } else if (i < 50) {
            fixedImprovements.push(0.05);
        } else if (i < 100) {
            fixedImprovements.push(0.025);
        } else {
            fixedImprovements.push(0.0125);
        }
    }

    const upgrades = {
        belt: [
            {
                required: [{ shape: "CuCuCuCu", amount: 30 }],
            },
            {
                required: [{ shape: "--CuCu--", amount: 500 }],
            },
            {
                required: [{ shape: "CpCpCpCp", amount: 1000 }],
            },
            {
                required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }],
            },
            {
                required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }],
            },
            {
                required: [{ shape: preparementShape, amount: 25000 }],
                excludePrevious: true,
            },
            {
                required: [
                    { shape: preparementShape, amount: 25000 },
                    { shape: finalGameShape, amount: 50000 },
                ],
                excludePrevious: true,
            },
            ...generateInfiniteUnlocks(),
        ],

        miner: [
            {
                required: [{ shape: "RuRuRuRu", amount: 300 }],
            },
            {
                required: [{ shape: "Cu------", amount: 800 }],
            },
            {
                required: [{ shape: "ScScScSc", amount: 3500 }],
            },
            {
                required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }],
            },
            {
                required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }],
            },
            {
                required: [{ shape: preparementShape, amount: 25000 }],
                excludePrevious: true,
            },
            {
                required: [
                    { shape: preparementShape, amount: 25000 },
                    { shape: finalGameShape, amount: 50000 },
                ],
                excludePrevious: true,
            },
            ...generateInfiniteUnlocks(),
        ],

        processors: [
            {
                required: [{ shape: "SuSuSuSu", amount: 500 }],
            },
            {
                required: [{ shape: "RuRu----", amount: 600 }],
            },
            {
                required: [{ shape: "CgScScCg", amount: 3500 }],
            },
            {
                required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }],
            },
            {
                required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }],
            },
            {
                required: [{ shape: preparementShape, amount: 25000 }],
                excludePrevious: true,
            },
            {
                required: [
                    { shape: preparementShape, amount: 25000 },
                    { shape: finalGameShape, amount: 50000 },
                ],
                excludePrevious: true,
            },
            ...generateInfiniteUnlocks(),
        ],

        painting: [
            {
                required: [{ shape: "RbRb----", amount: 600 }],
            },
            {
                required: [{ shape: "WrWrWrWr", amount: 3800 }],
            },
            {
                required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }],
            },
            {
                required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }],
            },
            {
                required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }],
            },
            {
                required: [{ shape: preparementShape, amount: 25000 }],
                excludePrevious: true,
            },
            {
                required: [
                    { shape: preparementShape, amount: 25000 },
                    { shape: finalGameShape, amount: 50000 },
                ],
                excludePrevious: true,
            },
            ...generateInfiniteUnlocks(),
        ],
    };

    // Automatically generate tier levels
    for (const upgradeId in upgrades) {
        const upgradeTiers = upgrades[upgradeId];

        let currentTierRequirements = [];
        for (let i = 0; i < upgradeTiers.length; ++i) {
            const tierHandle = upgradeTiers[i];
            tierHandle.improvement = fixedImprovements[i];
            const originalRequired = tierHandle.required.slice();

            for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
                const oldTierRequirement = currentTierRequirements[k];
                if (!tierHandle.excludePrevious) {
                    tierHandle.required.unshift({
                        shape: oldTierRequirement.shape,
                        amount: oldTierRequirement.amount,
                    });
                }
            }
            currentTierRequirements.push(
                ...originalRequired.map(req => ({
                    amount: req.amount,
                    shape: req.shape,
                }))
            );
            currentTierRequirements.forEach(tier => {
                tier.amount = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["findNiceIntegerValue"])(tier.amount * tierGrowth);
            });
        }
    }

    // VALIDATE
    if (false) {}

    return upgrades;
}

/**
 * Generates the level definitions
 * @param {boolean} limitedVersion
 */
function generateLevelDefinitions(limitedVersion = false) {
    const levelDefinitions = [
        // 1
        // Circle
        {
            shape: "CuCuCuCu", // belts t1
            required: 30,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_cutter_and_trash,
        },

        // 2
        // Cutter
        {
            shape: "----CuCu", //
            required: 40,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].no_reward,
        },

        // 3
        // Rectangle
        {
            shape: "RuRuRuRu", // miners t1
            required: 70,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_balancer,
        },

        // 4
        {
            shape: "RuRu----", // processors t2
            required: 70,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_rotater,
        },

        // 5
        // Rotater
        {
            shape: "Cu----Cu", // belts t2
            required: 170,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_tunnel,
        },

        // 6
        {
            shape: "Cu------", // miners t2
            required: 270,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_painter,
        },

        // 7
        // Painter
        {
            shape: "CrCrCrCr", // unused
            required: 300,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_rotater_ccw,
        },

        // 8
        {
            shape: "RbRb----", // painter t2
            required: 480,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_mixer,
        },

        // 9
        // Mixing (purple)
        {
            shape: "CpCpCpCp", // belts t3
            required: 600,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_merger,
        },

        // 10
        // STACKER: Star shape + cyan
        {
            shape: "ScScScSc", // miners t3
            required: 800,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_stacker,
        },

        // 11
        // Chainable miner
        {
            shape: "CgScScCg", // processors t3
            required: 1000,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_miner_chainable,
        },

        // 12
        // Blueprints
        {
            shape: "CbCbCbRb:CwCwCwCw",
            required: 1000,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_blueprints,
        },

        // 13
        // Tunnel Tier 2
        {
            shape: "RpRpRpRp:CwCwCwCw", // painting t3
            required: 3800,
            reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_underground_belt_tier_2,
        },

        // DEMO STOPS HERE
        ...(limitedVersion
            ? [
                  {
                      shape: "RpRpRpRp:CwCwCwCw",
                      required: 0,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_demo_end,
                  },
              ]
            : [
                  // 14
                  // Belt reader
                  {
                      shape: "--Cg----:--Cr----", // unused
                      required: 8, // Per second!
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_belt_reader,
                      throughputOnly: true,
                  },

                  // 15
                  // Storage
                  {
                      shape: "SrSrSrSr:CyCyCyCy", // unused
                      required: 10000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_storage,
                  },

                  // 16
                  // Quad Cutter
                  {
                      shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
                      required: 6000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_cutter_quad,
                  },

                  // 17
                  // Double painter
                  {
                      shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
                      required: 20000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_painter_double,
                  },

                  // 18
                  // Rotater (180deg)
                  {
                      shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
                      required: 20000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_rotater_180,
                  },

                  // 19
                  // Compact splitter
                  {
                      shape: "CpRpCp--:SwSwSwSw",
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_splitter,
                  },

                  // 20
                  // WIRES
                  {
                      shape: finalGameShape,
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_wires_painter_and_levers,
                  },

                  // 21
                  // Filter
                  {
                      shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr",
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_filter,
                  },

                  // 22
                  // Constant signal
                  {
                      shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_constant_signal,
                  },

                  // 23
                  // Display
                  {
                      shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_display,
                  },

                  // 24 Logic gates
                  {
                      shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_logic_gates,
                  },

                  // 25 Virtual Processing
                  {
                      shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
                      required: 25000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_virtual_processing,
                  },

                  // 26 Freeplay
                  {
                      shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
                      required: 50000,
                      reward: _tutorial_goals__WEBPACK_IMPORTED_MODULE_3__["enumHubGoalRewards"].reward_freeplay,
                  },
              ]),
    ];

    if (false) {}

    return levelDefinitions;
}

const fullVersionUpgrades = generateUpgrades(false);
const demoVersionUpgrades = generateUpgrades(true);

const fullVersionLevels = generateLevelDefinitions(false);
const demoVersionLevels = generateLevelDefinitions(true);

class RegularGameMode extends _game_mode__WEBPACK_IMPORTED_MODULE_1__["GameMode"] {
    constructor(root) {
        super(root);
    }

    getUpgrades() {
        return this.root.app.restrictionMgr.getHasExtendedUpgrades()
            ? fullVersionUpgrades
            : demoVersionUpgrades;
    }

    getIsFreeplayAvailable() {
        return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay();
    }

    getBlueprintShapeKey() {
        return blueprintShape;
    }

    getLevelDefinitions() {
        return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay()
            ? fullVersionLevels
            : demoVersionLevels;
    }
}


/***/ }),

/***/ "./src/js/game/production_analytics.js":
/*!*********************************************!*\
  !*** ./src/js/game/production_analytics.js ***!
  \*********************************************/
/*! exports provided: enumAnalyticsDataSource, ProductionAnalytics */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumAnalyticsDataSource", function() { return enumAnalyticsDataSource; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ProductionAnalytics", function() { return ProductionAnalytics; });
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./shape_definition */ "./src/js/game/shape_definition.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./items/shape_item */ "./src/js/game/items/shape_item.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");







/** @enum {string} */
const enumAnalyticsDataSource = {
    produced: "produced",
    stored: "stored",
    delivered: "delivered",
};

class ProductionAnalytics extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__["BasicSerializableObject"] {
    static getId() {
        return "ProductionAnalytics";
    }

    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        super();
        this.root = root;

        this.history = {
            [enumAnalyticsDataSource.produced]: [],
            [enumAnalyticsDataSource.stored]: [],
            [enumAnalyticsDataSource.delivered]: [],
        };

        for (let i = 0; i < _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].statisticsGraphSlices; ++i) {
            this.startNewSlice();
        }

        this.root.signals.shapeDelivered.add(this.onShapeDelivered, this);
        this.root.signals.itemProduced.add(this.onItemProduced, this);

        this.lastAnalyticsSlice = 0;
    }

    /**
     * @param {ShapeDefinition} definition
     */
    onShapeDelivered(definition) {
        const key = definition.getHash();
        const entry = this.history[enumAnalyticsDataSource.delivered];
        entry[entry.length - 1][key] = (entry[entry.length - 1][key] || 0) + 1;
    }

    /**
     * @param {BaseItem} item
     */
    onItemProduced(item) {
        if (item.getItemType() === "shape") {
            const definition = /** @type {ShapeItem} */ (item).definition;
            const key = definition.getHash();
            const entry = this.history[enumAnalyticsDataSource.produced];
            entry[entry.length - 1][key] = (entry[entry.length - 1][key] || 0) + 1;
        }
    }

    /**
     * Starts a new time slice
     */
    startNewSlice() {
        for (const key in this.history) {
            if (key === enumAnalyticsDataSource.stored) {
                // Copy stored data
                this.history[key].push(Object.assign({}, this.root.hubGoals.storedShapes));
            } else {
                this.history[key].push({});
            }
            while (this.history[key].length > _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].statisticsGraphSlices) {
                this.history[key].shift();
            }
        }
    }

    /**
     * Returns the current rate of a given shape
     * @param {enumAnalyticsDataSource} dataSource
     * @param {ShapeDefinition} definition
     */
    getCurrentShapeRate(dataSource, definition) {
        const slices = this.history[dataSource];
        return slices[slices.length - 2][definition.getHash()] || 0;
    }

    /**
     * Returns the rate of a given shape, <historyOffset> frames ago
     * @param {enumAnalyticsDataSource} dataSource
     * @param {ShapeDefinition} definition
     * @param {number} historyOffset
     */
    getPastShapeRate(dataSource, definition, historyOffset) {
        window.assert(
            historyOffset >= 0 && historyOffset < _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].statisticsGraphSlices - 1,
            "Invalid slice offset: " + historyOffset
        );

        const slices = this.history[dataSource];
        return slices[slices.length - 2 - historyOffset][definition.getHash()] || 0;
    }

    /**
     * Returns the rates of all shapes
     * @param {enumAnalyticsDataSource} dataSource
     */
    getCurrentShapeRates(dataSource) {
        const slices = this.history[dataSource];

        // First, copy current slice
        const baseValues = Object.assign({}, slices[slices.length - 2]);

        // Add past values
        for (let i = 0; i < 10; ++i) {
            const pastValues = slices[slices.length - i - 3];
            for (const key in pastValues) {
                baseValues[key] = baseValues[key] || 0;
            }
        }

        return baseValues;
    }

    update() {
        if (this.root.time.now() - this.lastAnalyticsSlice > _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].analyticsSliceDurationSeconds) {
            this.lastAnalyticsSlice = this.root.time.now();
            this.startNewSlice();
        }
    }
}


/***/ }),

/***/ "./src/js/game/root.js":
/*!*****************************!*\
  !*** ./src/js/game/root.js ***!
  \*****************************/
/*! exports provided: layers, GameRoot */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "layers", function() { return layers; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameRoot", function() { return GameRoot; });
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_rng__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/rng */ "./src/js/core/rng.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* eslint-disable no-unused-vars */




// Type hints


const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("game/root");

/** @type {Array<Layer>} */
const layers = ["regular", "wires"];

/**
 * The game root is basically the whole game state at a given point,
 * combining all important classes. We don't have globals, but this
 * class is passed to almost all game classes.
 */
class GameRoot {
    /**
     * Constructs a new game root
     * @param {Application} app
     */
    constructor(app) {
        this.app = app;

        /** @type {Savegame} */
        this.savegame = null;

        /** @type {InGameState} */
        this.gameState = null;

        /** @type {KeyActionMapper} */
        this.keyMapper = null;

        // Store game dimensions
        this.gameWidth = 500;
        this.gameHeight = 500;

        // Stores whether the current session is a fresh game (true), or was continued (false)
        /** @type {boolean} */
        this.gameIsFresh = true;

        // Stores whether the logic is already initialized
        /** @type {boolean} */
        this.logicInitialized = false;

        // Stores whether the game is already initialized, that is, all systems etc have been created
        /** @type {boolean} */
        this.gameInitialized = false;

        /**
         * Whether a bulk operation is running
         */
        this.bulkOperationRunning = false;

        //////// Other properties ///////

        /** @type {Camera} */
        this.camera = null;

        /** @type {HTMLCanvasElement} */
        this.canvas = null;

        /** @type {CanvasRenderingContext2D} */
        this.context = null;

        /** @type {MapView} */
        this.map = null;

        /** @type {GameLogic} */
        this.logic = null;

        /** @type {EntityManager} */
        this.entityMgr = null;

        /** @type {GameHUD} */
        this.hud = null;

        /** @type {GameSystemManager} */
        this.systemMgr = null;

        /** @type {GameTime} */
        this.time = null;

        /** @type {HubGoals} */
        this.hubGoals = null;

        /** @type {BufferMaintainer} */
        this.buffers = null;

        /** @type {AutomaticSave} */
        this.automaticSave = null;

        /** @type {SoundProxy} */
        this.soundProxy = null;

        /** @type {ShapeDefinitionManager} */
        this.shapeDefinitionMgr = null;

        /** @type {ProductionAnalytics} */
        this.productionAnalytics = null;

        /** @type {DynamicTickrate} */
        this.dynamicTickrate = null;

        /** @type {Layer} */
        this.currentLayer = "regular";

        /** @type {GameMode} */
        this.gameMode = null;

        this.signals = {
            // Entities
            entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            entityAdded: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            entityChanged: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            // Global
            resized: /** @type {TypedSignal<[number, number]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            readyToRender: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            aboutToDestruct: /** @type {TypedSignal<[]>} */ new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"](),

            // Game Hooks
            gameSaved: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()), // Game got saved
            gameRestored: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()), // Game got restored

            gameFrameStarted: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()), // New frame

            storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            upgradePurchased: /** @type {TypedSignal<[string]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            // Called right after game is initialized
            postLoadHook: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
            itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            // Called to check if an entity can be placed, second parameter is an additional offset.
            // Use to introduce additional placement checks
            prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),

            // Called before actually placing an entity, use to perform additional logic
            // for freeing space before actually placing.
            freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new _core_signal__WEBPACK_IMPORTED_MODULE_0__["Signal"]()),
        };

        // RNG's
        /** @type {Object.<string, Object.<string, RandomNumberGenerator>>} */
        this.rngs = {};

        // Work queue
        this.queue = {
            requireRedraw: false,
        };
    }

    /**
     * Destructs the game root
     */
    destruct() {
        logger.log("destructing root");
        this.signals.aboutToDestruct.dispatch();

        this.reset();
    }

    /**
     * Resets the whole root and removes all properties
     */
    reset() {
        if (this.signals) {
            // Destruct all signals
            for (let i = 0; i < this.signals.length; ++i) {
                this.signals[i].removeAll();
            }
        }

        if (this.hud) {
            this.hud.cleanup();
        }
        if (this.camera) {
            this.camera.cleanup();
        }

        // Finally free all properties
        for (let prop in this) {
            if (this.hasOwnProperty(prop)) {
                delete this[prop];
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/shape_definition.js":
/*!*****************************************!*\
  !*** ./src/js/game/shape_definition.js ***!
  \*****************************************/
/*! exports provided: enumSubShape, enumSubShapeToShortcode, enumShortcodeToSubShape, createSimpleShape, ShapeDefinition */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumSubShape", function() { return enumSubShape; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumSubShapeToShortcode", function() { return enumSubShapeToShortcode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumShortcodeToSubShape", function() { return enumShortcodeToSubShape; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createSimpleShape", function() { return createSimpleShape; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ShapeDefinition", function() { return ShapeDefinition; });
/* harmony import */ var _core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/buffer_utils */ "./src/js/core/buffer_utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_dpi_manager__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/dpi_manager */ "./src/js/core/dpi_manager.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./colors */ "./src/js/game/colors.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./theme */ "./src/js/game/theme.js");









/**
 * @typedef {{
 *   subShape: enumSubShape,
 *   color: enumColors,
 * }} ShapeLayerItem
 */

/**
 * Order is Q1 (tr), Q2(br), Q3(bl), Q4(tl)
 * @typedef {[ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?, ShapeLayerItem?]} ShapeLayer
 */

const arrayQuadrantIndexToOffset = [
    new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](1, -1), // tr
    new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](1, 1), // br
    new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](-1, 1), // bl
    new _core_vector__WEBPACK_IMPORTED_MODULE_4__["Vector"](-1, -1), // tl
];

/** @enum {string} */
const enumSubShape = {
    rect: "rect",
    circle: "circle",
    star: "star",
    windmill: "windmill",
};

/** @enum {string} */
const enumSubShapeToShortcode = {
    [enumSubShape.rect]: "R",
    [enumSubShape.circle]: "C",
    [enumSubShape.star]: "S",
    [enumSubShape.windmill]: "W",
};

/** @enum {enumSubShape} */
const enumShortcodeToSubShape = {};
for (const key in enumSubShapeToShortcode) {
    enumShortcodeToSubShape[enumSubShapeToShortcode[key]] = key;
}

/**
 * Converts the given parameters to a valid shape definition
 * @param {*} layers
 * @returns {Array<import("./shape_definition").ShapeLayer>}
 */
function createSimpleShape(layers) {
    layers.forEach(layer => {
        layer.forEach(item => {
            if (item) {
                item.color = item.color || _colors__WEBPACK_IMPORTED_MODULE_6__["enumColors"].uncolored;
            }
        });
    });
    return layers;
}

/**
 * Cache which shapes are valid short keys and which not
 * @type {Map<string, boolean>}
 */
const SHORT_KEY_CACHE = new Map();

class ShapeDefinition extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_5__["BasicSerializableObject"] {
    static getId() {
        return "ShapeDefinition";
    }

    static getSchema() {
        return {};
    }

    deserialize(data) {
        const errorCode = super.deserialize(data);
        if (errorCode) {
            return errorCode;
        }
        const definition = ShapeDefinition.fromShortKey(data);
        this.layers = /** @type {Array<ShapeLayer>} */ (definition.layers);
    }

    serialize() {
        return this.getHash();
    }

    /**
     *
     * @param {object} param0
     * @param {Array<ShapeLayer>=} param0.layers
     */
    constructor({ layers = [] }) {
        super();

        /**
         * The layers from bottom to top
         * @type {Array<ShapeLayer>}
         */
        this.layers = layers;

        /** @type {string} */
        this.cachedHash = null;

        // Set on demand
        this.bufferGenerator = null;
    }

    /**
     * Generates the definition from the given short key
     * @param {string} key
     * @returns {ShapeDefinition}
     */
    static fromShortKey(key) {
        const sourceLayers = key.split(":");
        let layers = [];
        for (let i = 0; i < sourceLayers.length; ++i) {
            const text = sourceLayers[i];
            window.assert(text.length === 8, "Invalid shape short key: " + key);

            /** @type {ShapeLayer} */
            const quads = [null, null, null, null];
            for (let quad = 0; quad < 4; ++quad) {
                const shapeText = text[quad * 2 + 0];
                const subShape = enumShortcodeToSubShape[shapeText];
                const color = _colors__WEBPACK_IMPORTED_MODULE_6__["enumShortcodeToColor"][text[quad * 2 + 1]];
                if (subShape) {
                    window.assert(color, "Invalid shape short key:", key);
                    quads[quad] = {
                        subShape,
                        color,
                    };
                } else if (shapeText !== "-") {
                    window.assert(false, "Invalid shape key: " + shapeText);
                }
            }
            layers.push(quads);
        }

        const definition = new ShapeDefinition({ layers });
        // We know the hash so save some work
        definition.cachedHash = key;
        return definition;
    }

    /**
     * Checks if a given string is a valid short key
     * @param {string} key
     * @returns {boolean}
     */
    static isValidShortKey(key) {
        if (SHORT_KEY_CACHE.has(key)) {
            return SHORT_KEY_CACHE.get(key);
        }

        const result = ShapeDefinition.isValidShortKeyInternal(key);
        SHORT_KEY_CACHE.set(key, result);
        return result;
    }

    /**
     * INTERNAL
     * Checks if a given string is a valid short key
     * @param {string} key
     * @returns {boolean}
     */
    static isValidShortKeyInternal(key) {
        const sourceLayers = key.split(":");
        let layers = [];
        for (let i = 0; i < sourceLayers.length; ++i) {
            const text = sourceLayers[i];
            if (text.length !== 8) {
                return false;
            }

            /** @type {ShapeLayer} */
            const quads = [null, null, null, null];
            let anyFilled = false;
            for (let quad = 0; quad < 4; ++quad) {
                const shapeText = text[quad * 2 + 0];
                const colorText = text[quad * 2 + 1];
                const subShape = enumShortcodeToSubShape[shapeText];
                const color = _colors__WEBPACK_IMPORTED_MODULE_6__["enumShortcodeToColor"][colorText];

                // Valid shape
                if (subShape) {
                    if (!color) {
                        // Invalid color
                        return false;
                    }
                    quads[quad] = {
                        subShape,
                        color,
                    };
                    anyFilled = true;
                } else if (shapeText === "-") {
                    // Make sure color is empty then, too
                    if (colorText !== "-") {
                        return false;
                    }
                } else {
                    // Invalid shape key
                    return false;
                }
            }

            if (!anyFilled) {
                // Empty layer
                return false;
            }
            layers.push(quads);
        }

        if (layers.length === 0 || layers.length > 4) {
            return false;
        }

        return true;
    }

    /**
     * Internal method to clone the shape definition
     * @returns {Array<ShapeLayer>}
     */
    internalCloneLayers() {
        return JSON.parse(JSON.stringify(this.layers));
    }

    /**
     * Returns if the definition is entirely empty^
     * @returns {boolean}
     */
    isEntirelyEmpty() {
        return this.layers.length === 0;
    }

    /**
     * Returns a unique id for this shape
     * @returns {string}
     */
    getHash() {
        if (this.cachedHash) {
            return this.cachedHash;
        }

        let id = "";
        for (let layerIndex = 0; layerIndex < this.layers.length; ++layerIndex) {
            const layer = this.layers[layerIndex];

            for (let quadrant = 0; quadrant < layer.length; ++quadrant) {
                const item = layer[quadrant];
                if (item) {
                    id += enumSubShapeToShortcode[item.subShape] + _colors__WEBPACK_IMPORTED_MODULE_6__["enumColorToShortcode"][item.color];
                } else {
                    id += "--";
                }
            }

            if (layerIndex < this.layers.length - 1) {
                id += ":";
            }
        }
        this.cachedHash = id;
        return id;
    }

    /**
     * Draws the shape definition
     * @param {number} x
     * @param {number} y
     * @param {DrawParameters} parameters
     * @param {number=} diameter
     */
    drawCentered(x, y, parameters, diameter = 20) {
        const dpi = Object(_core_dpi_manager__WEBPACK_IMPORTED_MODULE_2__["smoothenDpi"])(_core_config__WEBPACK_IMPORTED_MODULE_1__["globalConfig"].shapesSharpness * parameters.zoomLevel);

        if (!this.bufferGenerator) {
            this.bufferGenerator = this.internalGenerateShapeBuffer.bind(this);
        }

        const key = diameter + "/" + dpi + "/" + this.cachedHash;
        const canvas = parameters.root.buffers.getForKey({
            key: "shapedef",
            subKey: key,
            w: diameter,
            h: diameter,
            dpi,
            redrawMethod: this.bufferGenerator,
        });
        parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
    }

    /**
     * Draws the item to a canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} size
     */
    drawFullSizeOnCanvas(context, size) {
        this.internalGenerateShapeBuffer(null, context, size, size, 1);
    }

    /**
     * Generates this shape as a canvas
     * @param {number} size
     */
    generateAsCanvas(size = 120) {
        const [canvas, context] = Object(_core_buffer_utils__WEBPACK_IMPORTED_MODULE_0__["makeOffscreenBuffer"])(size, size, {
            smooth: true,
            label: "definition-canvas-cache-" + this.getHash(),
            reusable: false,
        });

        this.internalGenerateShapeBuffer(canvas, context, size, size, 1);
        return canvas;
    }

    /**
     *
     * @param {HTMLCanvasElement} canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} w
     * @param {number} h
     * @param {number} dpi
     */
    internalGenerateShapeBuffer(canvas, context, w, h, dpi) {
        context.translate((w * dpi) / 2, (h * dpi) / 2);
        context.scale((dpi * w) / 23, (dpi * h) / 23);

        context.fillStyle = "#e9ecf7";

        const quadrantSize = 10;
        const quadrantHalfSize = quadrantSize / 2;

        context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_7__["THEME"].items.circleBackground;
        context.beginCircle(0, 0, quadrantSize * 1.15);
        context.fill();

        for (let layerIndex = 0; layerIndex < this.layers.length; ++layerIndex) {
            const quadrants = this.layers[layerIndex];

            const layerScale = Math.max(0.1, 0.9 - layerIndex * 0.22);

            for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
                if (!quadrants[quadrantIndex]) {
                    continue;
                }
                const { subShape, color } = quadrants[quadrantIndex];

                const quadrantPos = arrayQuadrantIndexToOffset[quadrantIndex];
                const centerQuadrantX = quadrantPos.x * quadrantHalfSize;
                const centerQuadrantY = quadrantPos.y * quadrantHalfSize;

                const rotation = Math.radians(quadrantIndex * 90);

                context.translate(centerQuadrantX, centerQuadrantY);
                context.rotate(rotation);

                context.fillStyle = _colors__WEBPACK_IMPORTED_MODULE_6__["enumColorsToHexCode"][color];
                context.strokeStyle = _theme__WEBPACK_IMPORTED_MODULE_7__["THEME"].items.outline;
                context.lineWidth = _theme__WEBPACK_IMPORTED_MODULE_7__["THEME"].items.outlineWidth;

                const insetPadding = 0.0;

                switch (subShape) {
                    case enumSubShape.rect: {
                        context.beginPath();
                        const dims = quadrantSize * layerScale;
                        context.rect(
                            insetPadding + -quadrantHalfSize,
                            -insetPadding + quadrantHalfSize - dims,
                            dims,
                            dims
                        );

                        break;
                    }
                    case enumSubShape.star: {
                        context.beginPath();
                        const dims = quadrantSize * layerScale;

                        let originX = insetPadding - quadrantHalfSize;
                        let originY = -insetPadding + quadrantHalfSize - dims;

                        const moveInwards = dims * 0.4;
                        context.moveTo(originX, originY + moveInwards);
                        context.lineTo(originX + dims, originY);
                        context.lineTo(originX + dims - moveInwards, originY + dims);
                        context.lineTo(originX, originY + dims);
                        context.closePath();
                        break;
                    }

                    case enumSubShape.windmill: {
                        context.beginPath();
                        const dims = quadrantSize * layerScale;

                        let originX = insetPadding - quadrantHalfSize;
                        let originY = -insetPadding + quadrantHalfSize - dims;
                        const moveInwards = dims * 0.4;
                        context.moveTo(originX, originY + moveInwards);
                        context.lineTo(originX + dims, originY);
                        context.lineTo(originX + dims, originY + dims);
                        context.lineTo(originX, originY + dims);
                        context.closePath();
                        break;
                    }

                    case enumSubShape.circle: {
                        context.beginPath();
                        context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize);
                        context.arc(
                            insetPadding + -quadrantHalfSize,
                            -insetPadding + quadrantHalfSize,
                            quadrantSize * layerScale,
                            -Math.PI * 0.5,
                            0
                        );
                        context.closePath();
                        break;
                    }

                    default: {
                        window.assert(false, "Unkown sub shape: " + subShape);
                    }
                }

                context.fill();
                context.stroke();

                context.rotate(-rotation);
                context.translate(-centerQuadrantX, -centerQuadrantY);
            }
        }
    }

    /**
     * Returns a definition with only the given quadrants
     * @param {Array<number>} includeQuadrants
     * @returns {ShapeDefinition}
     */
    cloneFilteredByQuadrants(includeQuadrants) {
        const newLayers = this.internalCloneLayers();
        for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
            const quadrants = newLayers[layerIndex];
            let anyContents = false;
            for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
                if (includeQuadrants.indexOf(quadrantIndex) < 0) {
                    quadrants[quadrantIndex] = null;
                } else if (quadrants[quadrantIndex]) {
                    anyContents = true;
                }
            }

            // Check if the layer is entirely empty
            if (!anyContents) {
                newLayers.splice(layerIndex, 1);
                layerIndex -= 1;
            }
        }
        return new ShapeDefinition({ layers: newLayers });
    }

    /**
     * Returns a definition which was rotated clockwise
     * @returns {ShapeDefinition}
     */
    cloneRotateCW() {
        const newLayers = this.internalCloneLayers();
        for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
            const quadrants = newLayers[layerIndex];
            quadrants.unshift(quadrants[3]);
            quadrants.pop();
        }
        return new ShapeDefinition({ layers: newLayers });
    }

    /**
     * Returns a definition which was rotated counter clockwise
     * @returns {ShapeDefinition}
     */
    cloneRotateCCW() {
        const newLayers = this.internalCloneLayers();
        for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
            const quadrants = newLayers[layerIndex];
            quadrants.push(quadrants[0]);
            quadrants.shift();
        }
        return new ShapeDefinition({ layers: newLayers });
    }

    /**
     * Returns a definition which was rotated 180 degrees
     * @returns {ShapeDefinition}
     */
    cloneRotate180() {
        const newLayers = this.internalCloneLayers();
        for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
            const quadrants = newLayers[layerIndex];
            quadrants.push(quadrants.shift(), quadrants.shift());
        }
        return new ShapeDefinition({ layers: newLayers });
    }

    /**
     * Stacks the given shape definition on top.
     * @param {ShapeDefinition} definition
     */
    cloneAndStackWith(definition) {
        if (this.isEntirelyEmpty() || definition.isEntirelyEmpty()) {
            window.assert(false, "Can not stack entirely empty definition");
        }

        const bottomShapeLayers = this.layers;
        const bottomShapeHighestLayerByQuad = [-1, -1, -1, -1];

        for (let layer = bottomShapeLayers.length - 1; layer >= 0; --layer) {
            const shapeLayer = bottomShapeLayers[layer];
            for (let quad = 0; quad < 4; ++quad) {
                const shapeQuad = shapeLayer[quad];
                if (shapeQuad !== null && bottomShapeHighestLayerByQuad[quad] < layer) {
                    bottomShapeHighestLayerByQuad[quad] = layer;
                }
            }
        }

        const topShapeLayers = definition.layers;
        const topShapeLowestLayerByQuad = [4, 4, 4, 4];

        for (let layer = 0; layer < topShapeLayers.length; ++layer) {
            const shapeLayer = topShapeLayers[layer];
            for (let quad = 0; quad < 4; ++quad) {
                const shapeQuad = shapeLayer[quad];
                if (shapeQuad !== null && topShapeLowestLayerByQuad[quad] > layer) {
                    topShapeLowestLayerByQuad[quad] = layer;
                }
            }
        }

        /**
         * We want to find the number `layerToMergeAt` such that when the top shape is placed at that
         * layer, the smallest gap between shapes is only 1. Instead of doing a guess-and-check method to
         * find the appropriate layer, we just calculate all the gaps assuming a merge at layer 0, even
         * though they go negative, and calculating the number to add to it so the minimum gap is 1 (ends
         * up being 1 - minimum).
         */
        const gapsBetweenShapes = [];
        for (let quad = 0; quad < 4; ++quad) {
            gapsBetweenShapes.push(topShapeLowestLayerByQuad[quad] - bottomShapeHighestLayerByQuad[quad]);
        }
        const smallestGapBetweenShapes = Math.min(...gapsBetweenShapes);
        // Can't merge at a layer lower than 0
        const layerToMergeAt = Math.max(1 - smallestGapBetweenShapes, 0);

        const mergedLayers = this.internalCloneLayers();
        for (let layer = mergedLayers.length; layer < layerToMergeAt + topShapeLayers.length; ++layer) {
            mergedLayers.push([null, null, null, null]);
        }

        for (let layer = 0; layer < topShapeLayers.length; ++layer) {
            const layerMergingAt = layerToMergeAt + layer;
            const bottomShapeLayer = mergedLayers[layerMergingAt];
            const topShapeLayer = topShapeLayers[layer];
            for (let quad = 0; quad < 4; quad++) {
                window.assert(!(bottomShapeLayer[quad] && topShapeLayer[quad]), "Shape merge: Sub shape got lost");
                bottomShapeLayer[quad] = bottomShapeLayer[quad] || topShapeLayer[quad];
            }
        }

        // Limit to 4 layers at max
        mergedLayers.splice(4);

        return new ShapeDefinition({ layers: mergedLayers });
    }

    /**
     * Clones the shape and colors everything in the given color
     * @param {enumColors} color
     */
    cloneAndPaintWith(color) {
        const newLayers = this.internalCloneLayers();

        for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
            const quadrants = newLayers[layerIndex];
            for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
                const item = quadrants[quadrantIndex];
                if (item) {
                    item.color = color;
                }
            }
        }
        return new ShapeDefinition({ layers: newLayers });
    }

    /**
     * Clones the shape and colors everything in the given colors
     * @param {[enumColors, enumColors, enumColors, enumColors]} colors
     */
    cloneAndPaintWith4Colors(colors) {
        const newLayers = this.internalCloneLayers();

        for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
            const quadrants = newLayers[layerIndex];
            for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
                const item = quadrants[quadrantIndex];
                if (item) {
                    item.color = colors[quadrantIndex] || item.color;
                }
            }
        }
        return new ShapeDefinition({ layers: newLayers });
    }
}


/***/ }),

/***/ "./src/js/game/shape_definition_manager.js":
/*!*************************************************!*\
  !*** ./src/js/game/shape_definition_manager.js ***!
  \*************************************************/
/*! exports provided: ShapeDefinitionManager */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ShapeDefinitionManager", function() { return ShapeDefinitionManager; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./colors */ "./src/js/game/colors.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./items/shape_item */ "./src/js/game/items/shape_item.js");
/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./root */ "./src/js/game/root.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./shape_definition */ "./src/js/game/shape_definition.js");







const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("shape_definition_manager");

class ShapeDefinitionManager extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_1__["BasicSerializableObject"] {
    static getId() {
        return "ShapeDefinitionManager";
    }

    /**
     *
     * @param {GameRoot} root
     */
    constructor(root) {
        super();
        this.root = root;

        /**
         * Store a cache from key -> definition
         * @type {Object<string, ShapeDefinition>}
         */
        this.shapeKeyToDefinition = {};

        /**
         * Store a cache from key -> item
         */
        this.shapeKeyToItem = {};

        // Caches operations in the form of 'operation/def1[/def2]'
        /** @type {Object.<string, Array<ShapeDefinition>|ShapeDefinition>} */
        this.operationCache = {};
    }

    /**
     * Returns a shape instance from a given short key
     * @param {string} hash
     * @returns {ShapeDefinition}
     */
    getShapeFromShortKey(hash) {
        const cached = this.shapeKeyToDefinition[hash];
        if (cached) {
            return cached;
        }
        return (this.shapeKeyToDefinition[hash] = _shape_definition__WEBPACK_IMPORTED_MODULE_5__["ShapeDefinition"].fromShortKey(hash));
    }

    /**
     * Returns a item instance from a given short key
     * @param {string} hash
     * @returns {ShapeItem}
     */
    getShapeItemFromShortKey(hash) {
        const cached = this.shapeKeyToItem[hash];
        if (cached) {
            return cached;
        }
        const definition = this.getShapeFromShortKey(hash);
        return (this.shapeKeyToItem[hash] = new _items_shape_item__WEBPACK_IMPORTED_MODULE_3__["ShapeItem"](definition));
    }

    /**
     * Returns a shape item for a given definition
     * @param {ShapeDefinition} definition
     * @returns {ShapeItem}
     */
    getShapeItemFromDefinition(definition) {
        return this.getShapeItemFromShortKey(definition.getHash());
    }

    /**
     * Registers a new shape definition
     * @param {ShapeDefinition} definition
     */
    registerShapeDefinition(definition) {
        const id = definition.getHash();
        window.assert(!this.shapeKeyToDefinition[id], "Shape Definition " + id + " already exists");
        this.shapeKeyToDefinition[id] = definition;
        // logger.log("Registered shape with key", id);
    }

    /**
     * Generates a definition for splitting a shape definition in two halfs
     * @param {ShapeDefinition} definition
     * @returns {[ShapeDefinition, ShapeDefinition]}
     */
    shapeActionCutHalf(definition) {
        const key = "cut/" + definition.getHash();
        if (this.operationCache[key]) {
            return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key]);
        }
        const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
        const leftSide = definition.cloneFilteredByQuadrants([0, 1]);

        return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
            this.registerOrReturnHandle(rightSide),
            this.registerOrReturnHandle(leftSide),
        ]);
    }

    /**
     * Generates a definition for splitting a shape definition in four quads
     * @param {ShapeDefinition} definition
     * @returns {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]}
     */
    shapeActionCutQuad(definition) {
        const key = "cut-quad/" + definition.getHash();
        if (this.operationCache[key]) {
            return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this
                .operationCache[key]);
        }

        return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this.operationCache[
            key
        ] = [
            this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([0])),
            this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([1])),
            this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([2])),
            this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([3])),
        ]);
    }

    /**
     * Generates a definition for rotating a shape clockwise
     * @param {ShapeDefinition} definition
     * @returns {ShapeDefinition}
     */
    shapeActionRotateCW(definition) {
        const key = "rotate-cw/" + definition.getHash();
        if (this.operationCache[key]) {
            return /** @type {ShapeDefinition} */ (this.operationCache[key]);
        }

        const rotated = definition.cloneRotateCW();

        return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
            rotated
        ));
    }

    /**
     * Generates a definition for rotating a shape counter clockwise
     * @param {ShapeDefinition} definition
     * @returns {ShapeDefinition}
     */
    shapeActionRotateCCW(definition) {
        const key = "rotate-ccw/" + definition.getHash();
        if (this.operationCache[key]) {
            return /** @type {ShapeDefinition} */ (this.operationCache[key]);
        }

        const rotated = definition.cloneRotateCCW();

        return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
            rotated
        ));
    }

    /**
     * Generates a definition for rotating a shape FL
     * @param {ShapeDefinition} definition
     * @returns {ShapeDefinition}
     */
    shapeActionRotate180(definition) {
        const key = "rotate-fl/" + definition.getHash();
        if (this.operationCache[key]) {
            return /** @type {ShapeDefinition} */ (this.operationCache[key]);
        }

        const rotated = definition.cloneRotate180();

        return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
            rotated
        ));
    }

    /**
     * Generates a definition for stacking the upper definition onto the lower one
     * @param {ShapeDefinition} lowerDefinition
     * @param {ShapeDefinition} upperDefinition
     * @returns {ShapeDefinition}
     */
    shapeActionStack(lowerDefinition, upperDefinition) {
        const key = "stack/" + lowerDefinition.getHash() + "/" + upperDefinition.getHash();
        if (this.operationCache[key]) {
            return /** @type {ShapeDefinition} */ (this.operationCache[key]);
        }
        const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
        return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
            stacked
        ));
    }

    /**
     * Generates a definition for painting it with the given color
     * @param {ShapeDefinition} definition
     * @param {enumColors} color
     * @returns {ShapeDefinition}
     */
    shapeActionPaintWith(definition, color) {
        const key = "paint/" + definition.getHash() + "/" + color;
        if (this.operationCache[key]) {
            return /** @type {ShapeDefinition} */ (this.operationCache[key]);
        }
        const colorized = definition.cloneAndPaintWith(color);
        return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
            colorized
        ));
    }

    /**
     * Generates a definition for painting it with the 4 colors
     * @param {ShapeDefinition} definition
     * @param {[enumColors, enumColors, enumColors, enumColors]} colors
     * @returns {ShapeDefinition}
     */
    shapeActionPaintWith4Colors(definition, colors) {
        const key = "paint4/" + definition.getHash() + "/" + colors.join(",");
        if (this.operationCache[key]) {
            return /** @type {ShapeDefinition} */ (this.operationCache[key]);
        }
        const colorized = definition.cloneAndPaintWith4Colors(colors);
        return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
            colorized
        ));
    }

    /**
     * Checks if we already have cached this definition, and if so throws it away and returns the already
     * cached variant
     * @param {ShapeDefinition} definition
     */
    registerOrReturnHandle(definition) {
        const id = definition.getHash();
        if (this.shapeKeyToDefinition[id]) {
            return this.shapeKeyToDefinition[id];
        }
        this.shapeKeyToDefinition[id] = definition;
        // logger.log("Registered shape with key (2)", id);
        return definition;
    }

    /**
     *
     * @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes
     * @returns {ShapeDefinition}
     */
    getDefinitionFromSimpleShapes(subShapes, color = _colors__WEBPACK_IMPORTED_MODULE_2__["enumColors"].uncolored) {
        const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map(
            subShape => ({ subShape, color })
        ));

        return this.registerOrReturnHandle(new _shape_definition__WEBPACK_IMPORTED_MODULE_5__["ShapeDefinition"]({ layers: [shapeLayer] }));
    }
}


/***/ }),

/***/ "./src/js/game/sound_proxy.js":
/*!************************************!*\
  !*** ./src/js/game/sound_proxy.js ***!
  \************************************/
/*! exports provided: SoundProxy */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SoundProxy", function() { return SoundProxy; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");




const avgSoundDurationSeconds = 0.1;
const maxOngoingSounds = 2;
const maxOngoingUiSounds = 5;

// Proxy to the application sound instance
class SoundProxy {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        this.root = root;

        // Store a list of sounds and when we started them
        this.playing3DSounds = [];
        this.playingUiSounds = [];
    }

    /**
     * Plays a new ui sound
     * @param {string} id Sound ID
     */
    playUi(id) {
        window.assert(typeof id === "string", "Not a valid sound id: " + id);
        this.internalUpdateOngoingSounds();
        if (this.playingUiSounds.length > maxOngoingUiSounds) {
            // Too many ongoing sounds
            return false;
        }

        this.root.app.sound.playUiSound(id);
        this.playingUiSounds.push(this.root.time.realtimeNow());
    }

    /**
     * Plays the ui click sound
     */
    playUiClick() {
        this.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_1__["SOUNDS"].uiClick);
    }

    /**
     * Plays the ui error sound
     */
    playUiError() {
        this.playUi(_platform_sound__WEBPACK_IMPORTED_MODULE_1__["SOUNDS"].uiError);
    }

    /**
     * Plays a 3D sound whose volume is scaled based on where it was emitted
     * @param {string} id Sound ID
     * @param {Vector} pos World space position
     */
    play3D(id, pos) {
        window.assert(typeof id === "string", "Not a valid sound id: " + id);
        window.assert(pos instanceof _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"], "Invalid sound position");
        this.internalUpdateOngoingSounds();

        if (this.playing3DSounds.length > maxOngoingSounds) {
            // Too many ongoing sounds
            return false;
        }

        this.root.app.sound.play3DSound(id, pos, this.root);
        this.playing3DSounds.push(this.root.time.realtimeNow());
        return true;
    }

    /**
     * Updates the list of ongoing sounds
     */
    internalUpdateOngoingSounds() {
        const now = this.root.time.realtimeNow();
        for (let i = 0; i < this.playing3DSounds.length; ++i) {
            if (now - this.playing3DSounds[i] > avgSoundDurationSeconds) {
                this.playing3DSounds.splice(i, 1);
                i -= 1;
            }
        }

        for (let i = 0; i < this.playingUiSounds.length; ++i) {
            if (now - this.playingUiSounds[i] > avgSoundDurationSeconds) {
                this.playingUiSounds.splice(i, 1);
                i -= 1;
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/belt.js":
/*!*************************************!*\
  !*** ./src/js/game/systems/belt.js ***!
  \*************************************/
/*! exports provided: BELT_ANIM_COUNT, BeltSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BELT_ANIM_COUNT", function() { return BELT_ANIM_COUNT; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltSystem", function() { return BeltSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _belt_path__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../belt_path */ "./src/js/game/belt_path.js");
/* harmony import */ var _buildings_belt__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../buildings/belt */ "./src/js/game/buildings/belt.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _components_belt__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../components/belt */ "./src/js/game/components/belt.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../meta_building */ "./src/js/game/meta_building.js");

















const BELT_ANIM_COUNT = 14;

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_4__["createLogger"])("belt");

/**
 * Manages all belts
 */
class BeltSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_13__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_belt__WEBPACK_IMPORTED_MODULE_11__["BeltComponent"]]);
        /**
         * @type {Object.<enumDirection, Array<AtlasSprite>>}
         */
        this.beltSprites = {
            [_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].top]: _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/belt/built/forward_0.png"),
            [_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].left]: _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/belt/built/left_0.png"),
            [_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].right]: _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/belt/built/right_0.png"),
        };

        /**
         * @type {Object.<enumDirection, Array<AtlasSprite>>}
         */
        this.beltAnimations = {
            [_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].top]: [],
            [_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].left]: [],
            [_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].right]: [],
        };

        for (let i = 0; i < BELT_ANIM_COUNT; ++i) {
            this.beltAnimations[_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].top].push(
                _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/belt/built/forward_" + i + ".png")
            );
            this.beltAnimations[_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].left].push(
                _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/belt/built/left_" + i + ".png")
            );
            this.beltAnimations[_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].right].push(
                _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/belt/built/right_" + i + ".png")
            );
        }

        this.root.signals.entityDestroyed.add(this.onEntityDestroyed, this);
        this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this);

        // Notice: These must come *after* the entity destroyed signals
        this.root.signals.entityAdded.add(this.onEntityAdded, this);
        this.root.signals.entityAdded.add(this.updateSurroundingBeltPlacement, this);

        /** @type {Array<BeltPath>} */
        this.beltPaths = [];
    }

    /**
     * Serializes all belt paths
     * @returns {Array<object>}
     */
    serializePaths() {
        let data = [];
        for (let i = 0; i < this.beltPaths.length; ++i) {
            data.push(this.beltPaths[i].serialize());
        }
        return data;
    }

    /**
     * Deserializes all belt paths
     * @param {Array<any>} data
     */
    deserializePaths(data) {
        if (!Array.isArray(data)) {
            return "Belt paths are not an array: " + typeof data;
        }

        for (let i = 0; i < data.length; ++i) {
            const path = _belt_path__WEBPACK_IMPORTED_MODULE_8__["BeltPath"].fromSerialized(this.root, data[i]);
            // If path is a string, that means its an error
            if (!(path instanceof _belt_path__WEBPACK_IMPORTED_MODULE_8__["BeltPath"])) {
                return "Failed to create path from belt data: " + path;
            }
            this.beltPaths.push(path);
        }

        if (this.beltPaths.length === 0) {
            // Old savegames might not have paths yet
            logger.warn("Recomputing belt paths (most likely the savegame is old or empty)");
            this.recomputeAllBeltPaths();
        } else {
            logger.warn("Restored", this.beltPaths.length, "belt paths");
        }

        if (false) {}
    }

    /**
     * Updates the belt placement after an entity has been added / deleted
     * @param {Entity} entity
     */
    updateSurroundingBeltPlacement(entity) {
        if (!this.root.gameInitialized) {
            return;
        }

        const staticComp = entity.components.StaticMapEntity;
        if (!staticComp) {
            return;
        }

        const metaBelt = _core_global_registries__WEBPACK_IMPORTED_MODULE_2__["gMetaBuildingRegistry"].findByClass(_buildings_belt__WEBPACK_IMPORTED_MODULE_9__["MetaBeltBuilding"]);
        // Compute affected area
        const originalRect = staticComp.getTileSpaceBounds();
        const affectedArea = originalRect.expandedInAllDirections(1);

        /** @type {Set<BeltPath>} */
        const changedPaths = new Set();

        for (let x = affectedArea.x; x < affectedArea.right(); ++x) {
            for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) {
                if (originalRect.containsPoint(x, y)) {
                    // Make sure we don't update the original entity
                    continue;
                }

                const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y);
                for (let i = 0; i < targetEntities.length; ++i) {
                    const targetEntity = targetEntities[i];

                    const targetBeltComp = targetEntity.components.Belt;
                    const targetStaticComp = targetEntity.components.StaticMapEntity;

                    if (!targetBeltComp) {
                        // Not a belt
                        continue;
                    }

                    const {
                        rotation,
                        rotationVariant,
                    } = metaBelt.computeOptimalDirectionAndRotationVariantAtTile({
                        root: this.root,
                        tile: new _core_vector__WEBPACK_IMPORTED_MODULE_7__["Vector"](x, y),
                        rotation: targetStaticComp.originalRotation,
                        variant: _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"],
                        layer: targetEntity.layer,
                    });

                    // Compute delta to see if anything changed
                    const newDirection = _buildings_belt__WEBPACK_IMPORTED_MODULE_9__["arrayBeltVariantToRotation"][rotationVariant];

                    if (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) {
                        const originalPath = targetBeltComp.assignedPath;

                        // Ok, first remove it from its current path
                        this.deleteEntityFromPath(targetBeltComp.assignedPath, targetEntity);

                        // Change stuff
                        targetStaticComp.rotation = rotation;
                        metaBelt.updateVariants(targetEntity, rotationVariant, _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]);

                        // Update code as well
                        targetStaticComp.code = Object(_building_codes__WEBPACK_IMPORTED_MODULE_10__["getCodeFromBuildingData"])(
                            metaBelt,
                            _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"],
                            rotationVariant
                        );

                        // Update the original path since it might have picked up the entit1y
                        originalPath.onPathChanged();

                        // Now add it again
                        this.addEntityToPaths(targetEntity);

                        // Sanity
                        if (false) {}

                        // Make sure the chunks know about the update
                        this.root.signals.entityChanged.dispatch(targetEntity);
                    }

                    if (targetBeltComp.assignedPath) {
                        changedPaths.add(targetBeltComp.assignedPath);
                    }
                }
            }
        }

        // notify all paths *afterwards* to avoid multi-updates
        changedPaths.forEach(path => path.onSurroundingsChanged());

        if (false) {}
    }

    /**
     * Called when an entity got destroyed
     * @param {Entity} entity
     */
    onEntityDestroyed(entity) {
        if (!this.root.gameInitialized) {
            return;
        }

        if (!entity.components.Belt) {
            return;
        }

        const assignedPath = entity.components.Belt.assignedPath;
        window.assert(assignedPath, "Entity has no belt path assigned");
        this.deleteEntityFromPath(assignedPath, entity);
        if (false) {}
    }

    /**
     * Attempts to delete the belt from its current path
     * @param {BeltPath} path
     * @param {Entity} entity
     */
    deleteEntityFromPath(path, entity) {
        if (path.entityPath.length === 1) {
            // This is a single entity path, easy to do, simply erase whole path
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["fastArrayDeleteValue"])(this.beltPaths, path);
            return;
        }

        // Notice: Since there might be circular references, it is important to check
        // which role the entity has
        if (path.isStartEntity(entity)) {
            // We tried to delete the start
            path.deleteEntityOnStart(entity);
        } else if (path.isEndEntity(entity)) {
            // We tried to delete the end
            path.deleteEntityOnEnd(entity);
        } else {
            // We tried to delete something inbetween
            const newPath = path.deleteEntityOnPathSplitIntoTwo(entity);
            this.beltPaths.push(newPath);
        }

        // Sanity
        entity.components.Belt.assignedPath = null;
    }

    /**
     * Adds the given entity to the appropriate paths
     * @param {Entity} entity
     */
    addEntityToPaths(entity) {
        const fromEntity = this.findSupplyingEntity(entity);
        const toEntity = this.findFollowUpEntity(entity);

        // Check if we can add the entity to the previous path
        if (fromEntity) {
            const fromPath = fromEntity.components.Belt.assignedPath;
            fromPath.extendOnEnd(entity);

            // Check if we now can extend the current path by the next path
            if (toEntity) {
                const toPath = toEntity.components.Belt.assignedPath;

                if (fromPath === toPath) {
                    // This is a circular dependency -> Ignore
                } else {
                    fromPath.extendByPath(toPath);

                    // Delete now obsolete path
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["fastArrayDeleteValue"])(this.beltPaths, toPath);
                }
            }
        } else {
            if (toEntity) {
                // Prepend it to the other path
                const toPath = toEntity.components.Belt.assignedPath;
                toPath.extendOnBeginning(entity);
            } else {
                // This is an empty belt path
                const path = new _belt_path__WEBPACK_IMPORTED_MODULE_8__["BeltPath"](this.root, [entity]);
                this.beltPaths.push(path);
            }
        }
    }

    /**
     * Called when an entity got added
     * @param {Entity} entity
     */
    onEntityAdded(entity) {
        if (!this.root.gameInitialized) {
            return;
        }

        if (!entity.components.Belt) {
            return;
        }

        this.addEntityToPaths(entity);
        if (false) {}
    }

    /**
     * Draws all belt paths
     * @param {DrawParameters} parameters
     */
    drawBeltItems(parameters) {
        for (let i = 0; i < this.beltPaths.length; ++i) {
            this.beltPaths[i].draw(parameters);
        }
    }

    /**
     * Verifies all belt paths
     */
    debug_verifyBeltPaths() {
        for (let i = 0; i < this.beltPaths.length; ++i) {
            this.beltPaths[i].debug_checkIntegrity("general-verify");
        }

        const belts = this.root.entityMgr.getAllWithComponent(_components_belt__WEBPACK_IMPORTED_MODULE_11__["BeltComponent"]);
        for (let i = 0; i < belts.length; ++i) {
            const path = belts[i].components.Belt.assignedPath;
            if (!path) {
                throw new Error("Belt has no path: " + belts[i].uid);
            }
            if (this.beltPaths.indexOf(path) < 0) {
                throw new Error("Path of entity not contained: " + belts[i].uid);
            }
        }
    }

    /**
     * Finds the follow up entity for a given belt. Used for building the dependencies
     * @param {Entity} entity
     * @returns {Entity|null}
     */
    findFollowUpEntity(entity) {
        const staticComp = entity.components.StaticMapEntity;
        const beltComp = entity.components.Belt;

        const followUpDirection = staticComp.localDirectionToWorld(beltComp.direction);
        const followUpVector = _core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirectionToVector"][followUpDirection];

        const followUpTile = staticComp.origin.add(followUpVector);
        const followUpEntity = this.root.map.getLayerContentXY(followUpTile.x, followUpTile.y, entity.layer);

        // Check if there's a belt at the tile we point to
        if (followUpEntity) {
            const followUpBeltComp = followUpEntity.components.Belt;
            if (followUpBeltComp) {
                const followUpStatic = followUpEntity.components.StaticMapEntity;

                const acceptedDirection = followUpStatic.localDirectionToWorld(_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].top);
                if (acceptedDirection === followUpDirection) {
                    return followUpEntity;
                }
            }
        }

        return null;
    }

    /**
     * Finds the supplying belt for a given belt. Used for building the dependencies
     * @param {Entity} entity
     * @returns {Entity|null}
     */
    findSupplyingEntity(entity) {
        const staticComp = entity.components.StaticMapEntity;

        const supplyDirection = staticComp.localDirectionToWorld(_core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirection"].bottom);
        const supplyVector = _core_vector__WEBPACK_IMPORTED_MODULE_7__["enumDirectionToVector"][supplyDirection];

        const supplyTile = staticComp.origin.add(supplyVector);
        const supplyEntity = this.root.map.getLayerContentXY(supplyTile.x, supplyTile.y, entity.layer);

        // Check if there's a belt at the tile we point to
        if (supplyEntity) {
            const supplyBeltComp = supplyEntity.components.Belt;
            if (supplyBeltComp) {
                const supplyStatic = supplyEntity.components.StaticMapEntity;
                const otherDirection = supplyStatic.localDirectionToWorld(
                    _core_vector__WEBPACK_IMPORTED_MODULE_7__["enumInvertedDirections"][supplyBeltComp.direction]
                );

                if (otherDirection === supplyDirection) {
                    return supplyEntity;
                }
            }
        }

        return null;
    }

    /**
     * Recomputes the belt path network. Only required for old savegames
     */
    recomputeAllBeltPaths() {
        logger.warn("Recomputing all belt paths");
        const visitedUids = new Set();

        const result = [];

        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            if (visitedUids.has(entity.uid)) {
                continue;
            }

            // Mark entity as visited
            visitedUids.add(entity.uid);

            // Compute path, start with entity and find precedors / successors
            const path = [entity];

            // Prevent infinite loops
            let maxIter = 99999;

            // Find precedors
            let prevEntity = this.findSupplyingEntity(entity);
            while (prevEntity && --maxIter > 0) {
                if (visitedUids.has(prevEntity.uid)) {
                    break;
                }
                path.unshift(prevEntity);
                visitedUids.add(prevEntity.uid);
                prevEntity = this.findSupplyingEntity(prevEntity);
            }

            // Find succedors
            let nextEntity = this.findFollowUpEntity(entity);
            while (nextEntity && --maxIter > 0) {
                if (visitedUids.has(nextEntity.uid)) {
                    break;
                }

                path.push(nextEntity);
                visitedUids.add(nextEntity.uid);
                nextEntity = this.findFollowUpEntity(nextEntity);
            }

            window.assert(maxIter > 1, "Ran out of iterations");
            result.push(new _belt_path__WEBPACK_IMPORTED_MODULE_8__["BeltPath"](this.root, path));
        }

        logger.log("Found", this.beltPaths.length, "belt paths");
        this.beltPaths = result;
    }

    /**
     * Updates all belts
     */
    update() {
        if (false) {}

        for (let i = 0; i < this.beltPaths.length; ++i) {
            this.beltPaths[i].update();
        }

        if (false) {}
    }

    /**
     * Draws a given chunk
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        // Limit speed to avoid belts going backwards
        const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10);

        // SYNC with systems/item_acceptor.js:drawEntityUnderlays!
        // 126 / 42 is the exact animation speed of the png animation
        const animationIndex = Math.floor(
            ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts
        );
        const contents = chunk.containedEntitiesByLayer.regular;

        if (this.root.app.settings.getAllSettings().simplifiedBelts) {
            // POTATO Mode: Only show items when belt is hovered
            let hoveredBeltPath = null;
            const mousePos = this.root.app.mousePosition;
            if (mousePos && this.root.currentLayer === "regular") {
                const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
                const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
                if (contents && contents.components.Belt) {
                    hoveredBeltPath = contents.components.Belt.assignedPath;
                }
            }

            for (let i = 0; i < contents.length; ++i) {
                const entity = contents[i];
                if (entity.components.Belt) {
                    const direction = entity.components.Belt.direction;
                    let sprite = this.beltAnimations[direction][0];

                    if (entity.components.Belt.assignedPath === hoveredBeltPath) {
                        sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
                    }

                    // Culling happens within the static map entity component
                    entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0);
                }
            }
        } else {
            for (let i = 0; i < contents.length; ++i) {
                const entity = contents[i];
                if (entity.components.Belt) {
                    const direction = entity.components.Belt.direction;
                    const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];

                    // Culling happens within the static map entity component
                    entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0);
                }
            }
        }
    }

    /**
     * Draws the belt path debug overlays
     * @param {DrawParameters} parameters
     */
    drawBeltPathDebug(parameters) {
        for (let i = 0; i < this.beltPaths.length; ++i) {
            this.beltPaths[i].drawDebug(parameters);
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/belt_reader.js":
/*!********************************************!*\
  !*** ./src/js/game/systems/belt_reader.js ***!
  \********************************************/
/*! exports provided: BeltReaderSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltReaderSystem", function() { return BeltReaderSystem; });
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _components_belt_reader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/belt_reader */ "./src/js/game/components/belt_reader.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");





class BeltReaderSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_0__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_belt_reader__WEBPACK_IMPORTED_MODULE_1__["BeltReaderComponent"]]);
    }

    update() {
        const now = this.root.time.now();
        const minimumTime = now - _core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].readerAnalyzeIntervalSeconds;
        const minimumTimeForThroughput = now - 1;
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];

            const readerComp = entity.components.BeltReader;
            const pinsComp = entity.components.WiredPins;

            // Remove outdated items
            while (readerComp.lastItemTimes[0] < minimumTime) {
                readerComp.lastItemTimes.shift();
            }

            pinsComp.slots[1].value = readerComp.lastItem;
            pinsComp.slots[0].value =
                (readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) >
                minimumTimeForThroughput
                    ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_3__["BOOL_TRUE_SINGLETON"]
                    : _items_boolean_item__WEBPACK_IMPORTED_MODULE_3__["BOOL_FALSE_SINGLETON"];

            if (now - readerComp.lastThroughputComputation > 0.5) {
                // Compute throughput
                readerComp.lastThroughputComputation = now;

                let throughput = 0;
                if (readerComp.lastItemTimes.length < 2) {
                    throughput = 0;
                } else {
                    let averageSpacing = 0;
                    let averageSpacingNum = 0;
                    for (let i = 0; i < readerComp.lastItemTimes.length - 1; ++i) {
                        averageSpacing += readerComp.lastItemTimes[i + 1] - readerComp.lastItemTimes[i];
                        ++averageSpacingNum;
                    }

                    throughput = 1 / (averageSpacing / averageSpacingNum);
                }

                readerComp.lastThroughput = Math.min(_core_config__WEBPACK_IMPORTED_MODULE_2__["globalConfig"].beltSpeedItemsPerSecond * 23.9, throughput);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/belt_underlays.js":
/*!***********************************************!*\
  !*** ./src/js/game/systems/belt_underlays.js ***!
  \***********************************************/
/*! exports provided: BeltUnderlaysSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BeltUnderlaysSystem", function() { return BeltUnderlaysSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/stale_area_detector */ "./src/js/core/stale_area_detector.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_belt__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../components/belt */ "./src/js/game/components/belt.js");
/* harmony import */ var _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../components/belt_underlays */ "./src/js/game/components/belt_underlays.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _belt__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./belt */ "./src/js/game/systems/belt.js");
















/**
 * Mapping from underlay type to clip rect
 * @type {Object<enumClippedBeltUnderlayType, Rectangle>}
 */
const enumUnderlayTypeToClipRect = {
    [_components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].none]: null,
    [_components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].full]: _core_sprites__WEBPACK_IMPORTED_MODULE_4__["FULL_CLIP_RECT"],
    [_components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].topOnly]: new _core_rectangle__WEBPACK_IMPORTED_MODULE_3__["Rectangle"](0, 0, 1, 0.5),
    [_components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].bottomOnly]: new _core_rectangle__WEBPACK_IMPORTED_MODULE_3__["Rectangle"](0, 0.5, 1, 0.5),
};

class BeltUnderlaysSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_12__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["BeltUnderlaysComponent"]]);

        this.underlayBeltSprites = [];

        for (let i = 0; i < _belt__WEBPACK_IMPORTED_MODULE_14__["BELT_ANIM_COUNT"]; ++i) {
            this.underlayBeltSprites.push(_core_loader__WEBPACK_IMPORTED_MODULE_2__["Loader"].getSprite("sprites/belt/built/forward_" + i + ".png"));
        }

        // Automatically recompute areas
        this.staleArea = new _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_5__["StaleAreaDetector"]({
            root,
            name: "belt-underlay",
            recomputeMethod: this.recomputeStaleArea.bind(this),
        });

        this.staleArea.recomputeOnComponentsChanged(
            [_components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["BeltUnderlaysComponent"], _components_belt__WEBPACK_IMPORTED_MODULE_7__["BeltComponent"], _components_item_acceptor__WEBPACK_IMPORTED_MODULE_9__["ItemAcceptorComponent"], _components_item_ejector__WEBPACK_IMPORTED_MODULE_10__["ItemEjectorComponent"]],
            1
        );
    }

    update() {
        this.staleArea.update();
    }

    /**
     * Called when an area changed - Resets all caches in the given area
     * @param {Rectangle} area
     */
    recomputeStaleArea(area) {
        for (let x = 0; x < area.w; ++x) {
            for (let y = 0; y < area.h; ++y) {
                const tileX = area.x + x;
                const tileY = area.y + y;
                const entity = this.root.map.getLayerContentXY(tileX, tileY, "regular");
                if (entity) {
                    const underlayComp = entity.components.BeltUnderlays;
                    if (underlayComp) {
                        for (let i = 0; i < underlayComp.underlays.length; ++i) {
                            underlayComp.underlays[i].cachedType = null;
                        }
                    }
                }
            }
        }
    }

    /**
     * Checks if a given tile is connected and has an acceptor
     * @param {Vector} tile
     * @param {enumDirection} fromDirection
     * @returns {boolean}
     */
    checkIsAcceptorConnected(tile, fromDirection) {
        const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
        if (!contents) {
            return false;
        }

        const staticComp = contents.components.StaticMapEntity;

        // Check if its a belt, since then its simple
        const beltComp = contents.components.Belt;
        if (beltComp) {
            return staticComp.localDirectionToWorld(_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirection"].bottom) === fromDirection;
        }

        // Check if there's an item acceptor
        const acceptorComp = contents.components.ItemAcceptor;
        if (acceptorComp) {
            // Check each slot to see if its connected
            for (let i = 0; i < acceptorComp.slots.length; ++i) {
                const slot = acceptorComp.slots[i];
                const slotTile = staticComp.localTileToWorld(slot.pos);

                // Step 1: Check if the tile matches
                if (!slotTile.equals(tile)) {
                    continue;
                }

                // Step 2: Check if any of the directions matches
                for (let j = 0; j < slot.directions.length; ++j) {
                    const slotDirection = staticComp.localDirectionToWorld(slot.directions[j]);
                    if (slotDirection === fromDirection) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Checks if a given tile is connected and has an ejector
     * @param {Vector} tile
     * @param {enumDirection} toDirection
     * @returns {boolean}
     */
    checkIsEjectorConnected(tile, toDirection) {
        const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
        if (!contents) {
            return false;
        }

        const staticComp = contents.components.StaticMapEntity;

        // Check if its a belt, since then its simple
        const beltComp = contents.components.Belt;
        if (beltComp) {
            return staticComp.localDirectionToWorld(beltComp.direction) === toDirection;
        }

        // Check for an ejector
        const ejectorComp = contents.components.ItemEjector;
        if (ejectorComp) {
            // Check each slot to see if its connected
            for (let i = 0; i < ejectorComp.slots.length; ++i) {
                const slot = ejectorComp.slots[i];
                const slotTile = staticComp.localTileToWorld(slot.pos);

                // Step 1: Check if the tile matches
                if (!slotTile.equals(tile)) {
                    continue;
                }

                // Step 2: Check if the direction matches
                const slotDirection = staticComp.localDirectionToWorld(slot.direction);
                if (slotDirection === toDirection) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Computes the flag for a given tile
     * @param {Entity} entity
     * @param {import("../components/belt_underlays").BeltUnderlayTile} underlayTile
     * @returns {enumClippedBeltUnderlayType} The type of the underlay
     */
    computeBeltUnderlayType(entity, underlayTile) {
        if (underlayTile.cachedType) {
            return underlayTile.cachedType;
        }

        const staticComp = entity.components.StaticMapEntity;

        const transformedPos = staticComp.localTileToWorld(underlayTile.pos);
        const destX = transformedPos.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
        const destY = transformedPos.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;

        // Extract direction and angle
        const worldDirection = staticComp.localDirectionToWorld(underlayTile.direction);
        const worldDirectionVector = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToVector"][worldDirection];

        // Figure out if there is anything connected at the top
        const connectedTop = this.checkIsAcceptorConnected(
            transformedPos.add(worldDirectionVector),
            _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumInvertedDirections"][worldDirection]
        );

        // Figure out if there is anything connected at the bottom
        const connectedBottom = this.checkIsEjectorConnected(
            transformedPos.sub(worldDirectionVector),
            worldDirection
        );

        let flag = _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].none;

        if (connectedTop && connectedBottom) {
            flag = _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].full;
        } else if (connectedTop) {
            flag = _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].topOnly;
        } else if (connectedBottom) {
            flag = _components_belt_underlays__WEBPACK_IMPORTED_MODULE_8__["enumClippedBeltUnderlayType"].bottomOnly;
        }

        return (underlayTile.cachedType = flag);
    }

    /**
     * Draws a given chunk
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        // Limit speed to avoid belts going backwards
        const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10);

        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const underlayComp = entity.components.BeltUnderlays;
            if (!underlayComp) {
                continue;
            }

            const staticComp = entity.components.StaticMapEntity;
            const underlays = underlayComp.underlays;
            for (let i = 0; i < underlays.length; ++i) {
                // Extract underlay parameters
                const { pos, direction } = underlays[i];
                const transformedPos = staticComp.localTileToWorld(pos);
                const destX = transformedPos.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                const destY = transformedPos.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;

                // Culling, Part 1: Check if the chunk contains the tile
                if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) {
                    continue;
                }

                // Culling, Part 2: Check if the overlay is visible
                if (
                    !parameters.visibleRect.containsRect4Params(
                        destX,
                        destY,
                        _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                        _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize
                    )
                ) {
                    continue;
                }

                // Extract direction and angle
                const worldDirection = staticComp.localDirectionToWorld(direction);
                const angle = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToAngle"][worldDirection];

                const underlayType = this.computeBeltUnderlayType(entity, underlays[i]);
                const clipRect = enumUnderlayTypeToClipRect[underlayType];
                if (!clipRect) {
                    // Empty
                    continue;
                }

                // Actually draw the sprite
                const x = destX + _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;
                const y = destY + _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;
                const angleRadians = Math.radians(angle);

                // SYNC with systems/belt.js:drawSingleEntity!
                const animationIndex = Math.floor(
                    ((this.root.time.realtimeNow() * speedMultiplier * _belt__WEBPACK_IMPORTED_MODULE_14__["BELT_ANIM_COUNT"] * 126) / 42) *
                        _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts
                );
                parameters.context.translate(x, y);
                parameters.context.rotate(angleRadians);
                this.underlayBeltSprites[
                    animationIndex % this.underlayBeltSprites.length
                ].drawCachedWithClipRect(
                    parameters,
                    -_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize,
                    -_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize,
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                    clipRect
                );
                parameters.context.rotate(-angleRadians);
                parameters.context.translate(-x, -y);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/constant_signal.js":
/*!************************************************!*\
  !*** ./src/js/game/systems/constant_signal.js ***!
  \************************************************/
/*! exports provided: ConstantSignalSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ConstantSignalSystem", function() { return ConstantSignalSystem; });
/* harmony import */ var trim__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! trim */ "./node_modules/trim/index.js");
/* harmony import */ var trim__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(trim__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/modal_dialog_elements */ "./src/js/core/modal_dialog_elements.js");
/* harmony import */ var _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/modal_dialog_forms */ "./src/js/core/modal_dialog_forms.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../colors */ "./src/js/game/colors.js");
/* harmony import */ var _components_constant_signal__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../components/constant_signal */ "./src/js/game/components/constant_signal.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../shape_definition */ "./src/js/game/shape_definition.js");















class ConstantSignalSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_10__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_constant_signal__WEBPACK_IMPORTED_MODULE_8__["ConstantSignalComponent"]]);

        this.root.signals.entityManuallyPlaced.add(entity =>
            this.editConstantSignal(entity, { deleteOnCancel: true })
        );
    }

    update() {
        // Set signals
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const pinsComp = entity.components.WiredPins;
            const signalComp = entity.components.ConstantSignal;
            pinsComp.slots[0].value = signalComp.signal;
        }
    }

    /**
     * Asks the entity to enter a valid signal code
     * @param {Entity} entity
     * @param {object} param0
     * @param {boolean=} param0.deleteOnCancel
     */
    editConstantSignal(entity, { deleteOnCancel = true }) {
        if (!entity.components.ConstantSignal) {
            return;
        }

        // Ok, query, but also save the uid because it could get stale
        const uid = entity.uid;

        const signalValueInput = new _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_3__["FormElementInput"]({
            id: "signalValue",
            label: Object(_core_utils__WEBPACK_IMPORTED_MODULE_4__["fillInLinkIntoTranslation"])(_translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.editSignal.descShortKey, _core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].shapeViewer),
            placeholder: "",
            defaultValue: "",
            validator: val => this.parseSignalCode(val),
        });

        const itemInput = new _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_3__["FormElementItemChooser"]({
            id: "signalItem",
            label: null,
            items: [
                _items_boolean_item__WEBPACK_IMPORTED_MODULE_11__["BOOL_FALSE_SINGLETON"],
                _items_boolean_item__WEBPACK_IMPORTED_MODULE_11__["BOOL_TRUE_SINGLETON"],
                ...Object.values(_items_color_item__WEBPACK_IMPORTED_MODULE_12__["COLOR_ITEM_SINGLETONS"]),
                this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
                    this.root.hubGoals.currentGoal.definition
                ),
                this.root.shapeDefinitionMgr.getShapeItemFromShortKey(
                    this.root.gameMode.getBlueprintShapeKey()
                ),
                ...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
                    this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
                ),
            ],
        });

        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_2__["DialogWithForm"]({
            app: this.root.app,
            title: _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.editSignal.title,
            desc: _translations__WEBPACK_IMPORTED_MODULE_5__["T"].dialogs.editSignal.descItems,
            formElements: [itemInput, signalValueInput],
            buttons: ["cancel:bad:escape", "ok:good:enter"],
            closeButton: false,
        });
        this.root.hud.parts.dialogs.internalShowDialog(dialog);

        // When confirmed, set the signal
        const closeHandler = () => {
            if (!this.root || !this.root.entityMgr) {
                // Game got stopped
                return;
            }

            const entityRef = this.root.entityMgr.findByUid(uid, false);
            if (!entityRef) {
                // outdated
                return;
            }

            const constantComp = entityRef.components.ConstantSignal;
            if (!constantComp) {
                // no longer interesting
                return;
            }

            if (itemInput.chosenItem) {
                console.log(itemInput.chosenItem);
                constantComp.signal = itemInput.chosenItem;
            } else {
                constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
            }
        };

        dialog.buttonSignals.ok.add(closeHandler);
        dialog.valueChosen.add(closeHandler);

        // When cancelled, destroy the entity again
        if (deleteOnCancel) {
            dialog.buttonSignals.cancel.add(() => {
                if (!this.root || !this.root.entityMgr) {
                    // Game got stopped
                    return;
                }

                const entityRef = this.root.entityMgr.findByUid(uid, false);
                if (!entityRef) {
                    // outdated
                    return;
                }

                const constantComp = entityRef.components.ConstantSignal;
                if (!constantComp) {
                    // no longer interesting
                    return;
                }

                this.root.logic.tryDeleteBuilding(entityRef);
            });
        }
    }

    /**
     * Tries to parse a signal code
     * @param {string} code
     * @returns {BaseItem}
     */
    parseSignalCode(code) {
        if (!this.root || !this.root.shapeDefinitionMgr) {
            // Stale reference
            return null;
        }

        code = trim__WEBPACK_IMPORTED_MODULE_0___default()(code);
        const codeLower = code.toLowerCase();

        if (_colors__WEBPACK_IMPORTED_MODULE_7__["enumColors"][codeLower]) {
            return _items_color_item__WEBPACK_IMPORTED_MODULE_12__["COLOR_ITEM_SINGLETONS"][codeLower];
        }
        if (code === "1" || codeLower === "true") {
            return _items_boolean_item__WEBPACK_IMPORTED_MODULE_11__["BOOL_TRUE_SINGLETON"];
        }

        if (code === "0" || codeLower === "false") {
            return _items_boolean_item__WEBPACK_IMPORTED_MODULE_11__["BOOL_FALSE_SINGLETON"];
        }

        if (_shape_definition__WEBPACK_IMPORTED_MODULE_13__["ShapeDefinition"].isValidShortKey(code)) {
            return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code);
        }

        return null;
    }
}


/***/ }),

/***/ "./src/js/game/systems/display.js":
/*!****************************************!*\
  !*** ./src/js/game/systems/display.js ***!
  \****************************************/
/*! exports provided: DisplaySystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DisplaySystem", function() { return DisplaySystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../colors */ "./src/js/game/colors.js");
/* harmony import */ var _components_display__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/display */ "./src/js/game/components/display.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");










class DisplaySystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_5__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_display__WEBPACK_IMPORTED_MODULE_4__["DisplayComponent"]]);

        /** @type {Object<string, import("../../core/draw_utils").AtlasSprite>} */
        this.displaySprites = {};

        for (const colorId in _colors__WEBPACK_IMPORTED_MODULE_3__["enumColors"]) {
            if (colorId === _colors__WEBPACK_IMPORTED_MODULE_3__["enumColors"].uncolored) {
                continue;
            }
            this.displaySprites[colorId] = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/wires/display/" + colorId + ".png");
        }
    }

    /**
     * Returns the color / value a display should show
     * @param {BaseItem} value
     * @returns {BaseItem}
     */
    getDisplayItem(value) {
        if (!value) {
            return null;
        }

        switch (value.getItemType()) {
            case "boolean": {
                return Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_6__["isTrueItem"])(value) ? _items_color_item__WEBPACK_IMPORTED_MODULE_7__["COLOR_ITEM_SINGLETONS"][_colors__WEBPACK_IMPORTED_MODULE_3__["enumColors"].white] : null;
            }

            case "color": {
                const item = /**@type {ColorItem} */ (value);
                return item.color === _colors__WEBPACK_IMPORTED_MODULE_3__["enumColors"].uncolored ? null : item;
            }

            case "shape": {
                return value;
            }

            default:
                window.assert(false, "Unknown item type: " + value.getItemType());
        }
    }

    /**
     * Draws a given chunk
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            if (entity && entity.components.Display) {
                const pinsComp = entity.components.WiredPins;
                const network = pinsComp.slots[0].linkedNetwork;

                if (!network || !network.hasValue()) {
                    continue;
                }

                const value = this.getDisplayItem(network.currentValue);

                if (!value) {
                    continue;
                }

                const origin = entity.components.StaticMapEntity.origin;
                if (value.getItemType() === "color") {
                    this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered(
                        parameters,
                        (origin.x + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                        (origin.y + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                        _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize
                    );
                } else if (value.getItemType() === "shape") {
                    value.drawItemCenteredClipped(
                        (origin.x + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                        (origin.y + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                        parameters,
                        30
                    );
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/filter.js":
/*!***************************************!*\
  !*** ./src/js/game/systems/filter.js ***!
  \***************************************/
/*! exports provided: FilterSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FilterSystem", function() { return FilterSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _components_filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/filter */ "./src/js/game/components/filter.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");







const MAX_ITEMS_IN_QUEUE = 2;

class FilterSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_4__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_filter__WEBPACK_IMPORTED_MODULE_2__["FilterComponent"]]);
    }

    update() {
        const progress =
            this.root.dynamicTickrate.deltaSeconds *
            this.root.hubGoals.getBeltBaseSpeed() *
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts;

        const requiredProgress = 1 - progress;

        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const filterComp = entity.components.Filter;
            const ejectorComp = entity.components.ItemEjector;

            // Process payloads
            const slotsAndLists = [filterComp.pendingItemsToLeaveThrough, filterComp.pendingItemsToReject];
            for (let slotIndex = 0; slotIndex < slotsAndLists.length; ++slotIndex) {
                const pendingItems = slotsAndLists[slotIndex];

                for (let j = 0; j < pendingItems.length; ++j) {
                    const nextItem = pendingItems[j];
                    // Advance next item
                    nextItem.progress = Math.min(requiredProgress, nextItem.progress + progress);
                    // Check if it's ready to eject
                    if (nextItem.progress >= requiredProgress - 1e-5) {
                        if (ejectorComp.tryEject(slotIndex, nextItem.item)) {
                            pendingItems.shift();
                        }
                    }
                }
            }
        }
    }

    /**
     *
     * @param {Entity} entity
     * @param {number} slot
     * @param {BaseItem} item
     */
    tryAcceptItem(entity, slot, item) {
        const network = entity.components.WiredPins.slots[0].linkedNetwork;
        if (!network || !network.hasValue()) {
            // Filter is not connected
            return false;
        }

        const value = network.currentValue;
        const filterComp = entity.components.Filter;
        window.assert(filterComp, "entity is no filter");

        // Figure out which list we have to check
        let listToCheck;
        if (value.equals(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]) || value.equals(item)) {
            listToCheck = filterComp.pendingItemsToLeaveThrough;
        } else {
            listToCheck = filterComp.pendingItemsToReject;
        }

        if (listToCheck.length >= MAX_ITEMS_IN_QUEUE) {
            // Busy
            return false;
        }

        // Actually accept item
        listToCheck.push({
            item,
            progress: 0.0,
        });
        return true;
    }
}


/***/ }),

/***/ "./src/js/game/systems/hub.js":
/*!************************************!*\
  !*** ./src/js/game/systems/hub.js ***!
  \************************************/
/*! exports provided: HubSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HubSystem", function() { return HubSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_dpi_manager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/dpi_manager */ "./src/js/core/dpi_manager.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/draw_utils */ "./src/js/core/draw_utils.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../translations */ "./src/js/translations.js");
/* harmony import */ var _components_hub__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../components/hub */ "./src/js/game/components/hub.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");













const HUB_SIZE_TILES = 4;
const HUB_SIZE_PIXELS = HUB_SIZE_TILES * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;

class HubSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_11__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_hub__WEBPACK_IMPORTED_MODULE_9__["HubComponent"]]);

        this.hubSprite = _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/buildings/hub.png");
    }

    /**
     * @param {DrawParameters} parameters
     */
    draw(parameters) {
        for (let i = 0; i < this.allEntities.length; ++i) {
            this.drawEntity(parameters, this.allEntities[i]);
        }
    }

    update() {
        for (let i = 0; i < this.allEntities.length; ++i) {
            // Set hub goal
            const entity = this.allEntities[i];
            const pinsComp = entity.components.WiredPins;
            pinsComp.slots[0].value = this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
                this.root.hubGoals.currentGoal.definition
            );
        }
    }
    /**
     *
     * @param {HTMLCanvasElement} canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} w
     * @param {number} h
     * @param {number} dpi
     */
    redrawHubBaseTexture(canvas, context, w, h, dpi) {
        // This method is quite ugly, please ignore it!

        context.scale(dpi, dpi);

        const parameters = new _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__["DrawParameters"]({
            context,
            visibleRect: new _core_rectangle__WEBPACK_IMPORTED_MODULE_5__["Rectangle"](0, 0, w, h),
            desiredAtlasScale: _core_sprites__WEBPACK_IMPORTED_MODULE_6__["ORIGINAL_SPRITE_SCALE"],
            zoomLevel: dpi * 0.75,
            root: this.root,
        });

        context.clearRect(0, 0, w, h);

        this.hubSprite.draw(context, 0, 0, w, h);

        if (this.root.hubGoals.isEndOfDemoReached()) {
            // End of demo
            context.font = "bold 12px GameFont";
            context.fillStyle = "#fd0752";
            context.textAlign = "center";
            context.fillText(_translations__WEBPACK_IMPORTED_MODULE_8__["T"].buildings.hub.endOfDemo.toUpperCase(), w / 2, h / 2 + 6);
            context.textAlign = "left";

            return;
        }

        const definition = this.root.hubGoals.currentGoal.definition;
        definition.drawCentered(45, 58, parameters, 36);

        const goals = this.root.hubGoals.currentGoal;

        const textOffsetX = 70;
        const textOffsetY = 61;

        if (goals.throughputOnly) {
            // Throughput
            const deliveredText = _translations__WEBPACK_IMPORTED_MODULE_8__["T"].ingame.statistics.shapesDisplayUnits.second.replace(
                "<shapes>",
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["formatBigNumber"])(goals.required)
            );

            context.font = "bold 12px GameFont";
            context.fillStyle = "#64666e";
            context.textAlign = "left";
            context.fillText(deliveredText, textOffsetX, textOffsetY);
        } else {
            // Deliver count
            const delivered = this.root.hubGoals.getCurrentGoalDelivered();
            const deliveredText = "" + Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["formatBigNumber"])(delivered);

            if (delivered > 9999) {
                context.font = "bold 16px GameFont";
            } else if (delivered > 999) {
                context.font = "bold 20px GameFont";
            } else {
                context.font = "bold 25px GameFont";
            }
            context.fillStyle = "#64666e";
            context.textAlign = "left";
            context.fillText(deliveredText, textOffsetX, textOffsetY);

            // Required
            context.font = "13px GameFont";
            context.fillStyle = "#a4a6b0";
            context.fillText("/ " + Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["formatBigNumber"])(goals.required), textOffsetX, textOffsetY + 13);
        }

        // Reward
        const rewardText = _translations__WEBPACK_IMPORTED_MODULE_8__["T"].storyRewards[goals.reward].title.toUpperCase();
        if (rewardText.length > 12) {
            context.font = "bold 8px GameFont";
        } else {
            context.font = "bold 10px GameFont";
        }
        context.fillStyle = "#fd0752";
        context.textAlign = "center";

        context.fillText(rewardText, HUB_SIZE_PIXELS / 2, 105);

        // Level "8"
        context.font = "bold 10px GameFont";
        context.fillStyle = "#fff";
        context.fillText("" + this.root.hubGoals.level, 27, 32);

        // "LVL"
        context.textAlign = "center";
        context.fillStyle = "#fff";
        context.font = "bold 6px GameFont";
        context.fillText(_translations__WEBPACK_IMPORTED_MODULE_8__["T"].buildings.hub.levelShortcut, 27, 22);

        // "Deliver"
        context.fillStyle = "#64666e";
        context.font = "bold 10px GameFont";
        context.fillText(_translations__WEBPACK_IMPORTED_MODULE_8__["T"].buildings.hub.deliver.toUpperCase(), HUB_SIZE_PIXELS / 2, 30);

        // "To unlock"
        const unlockText = _translations__WEBPACK_IMPORTED_MODULE_8__["T"].buildings.hub.toUnlock.toUpperCase();
        if (unlockText.length > 15) {
            context.font = "bold 8px GameFont";
        } else {
            context.font = "bold 10px GameFont";
        }
        context.fillText(_translations__WEBPACK_IMPORTED_MODULE_8__["T"].buildings.hub.toUnlock.toUpperCase(), HUB_SIZE_PIXELS / 2, 92);

        context.textAlign = "left";
    }

    /**
     * @param {DrawParameters} parameters
     * @param {Entity} entity
     */
    drawEntity(parameters, entity) {
        const staticComp = entity.components.StaticMapEntity;
        if (!staticComp.shouldBeDrawn(parameters)) {
            return;
        }

        // Deliver count
        const delivered = this.root.hubGoals.getCurrentGoalDelivered();
        const deliveredText = "" + Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["formatBigNumber"])(delivered);

        const dpi = Object(_core_dpi_manager__WEBPACK_IMPORTED_MODULE_1__["smoothenDpi"])(_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].shapesSharpness * parameters.zoomLevel);
        const canvas = parameters.root.buffers.getForKey({
            key: "hub",
            subKey: dpi + "/" + this.root.hubGoals.level + "/" + deliveredText,
            w: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * 4,
            h: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * 4,
            dpi,
            redrawMethod: this.redrawHubBaseTexture.bind(this),
        });

        const extrude = 8;
        Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_3__["drawSpriteClipped"])({
            parameters,
            sprite: canvas,
            x: staticComp.origin.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize - extrude,
            y: staticComp.origin.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize - extrude,
            w: HUB_SIZE_PIXELS + 2 * extrude,
            h: HUB_SIZE_PIXELS + 2 * extrude,
            originalW: HUB_SIZE_PIXELS * dpi,
            originalH: HUB_SIZE_PIXELS * dpi,
        });
    }
}


/***/ }),

/***/ "./src/js/game/systems/item_acceptor.js":
/*!**********************************************!*\
  !*** ./src/js/game/systems/item_acceptor.js ***!
  \**********************************************/
/*! exports provided: ItemAcceptorSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemAcceptorSystem", function() { return ItemAcceptorSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");








class ItemAcceptorSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_5__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_item_acceptor__WEBPACK_IMPORTED_MODULE_4__["ItemAcceptorComponent"]]);

        // Well ... it's better to be verbose I guess?
        this.accumulatedTicksWhileInMapOverview = 0;
    }

    update() {
        if (this.root.app.settings.getAllSettings().simplifiedBelts) {
            // Disabled in potato mode
            return;
        }

        // This system doesn't render anything while in map overview,
        // so simply accumulate ticks
        if (this.root.camera.getIsMapOverlayActive()) {
            ++this.accumulatedTicksWhileInMapOverview;
            return;
        }

        // Compute how much ticks we missed
        const numTicks = 1 + this.accumulatedTicksWhileInMapOverview;
        const progress =
            this.root.dynamicTickrate.deltaSeconds *
            2 *
            this.root.hubGoals.getBeltBaseSpeed() *
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts * // * 2 because its only a half tile
            numTicks;

        // Reset accumulated ticks
        this.accumulatedTicksWhileInMapOverview = 0;

        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const aceptorComp = entity.components.ItemAcceptor;
            const animations = aceptorComp.itemConsumptionAnimations;

            // Process item consumption animations to avoid items popping from the belts
            for (let animIndex = 0; animIndex < animations.length; ++animIndex) {
                const anim = animations[animIndex];
                anim.animProgress += progress;
                if (anim.animProgress > 1) {
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["fastArrayDelete"])(animations, animIndex);
                    animIndex -= 1;
                }
            }
        }
    }

    /**
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        if (this.root.app.settings.getAllSettings().simplifiedBelts) {
            // Disabled in potato mode
            return;
        }

        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const acceptorComp = entity.components.ItemAcceptor;
            if (!acceptorComp) {
                continue;
            }

            const staticComp = entity.components.StaticMapEntity;
            for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
                const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
                    animIndex
                ];

                const slotData = acceptorComp.slots[slotIndex];
                const realSlotPos = staticComp.localTileToWorld(slotData.pos);

                if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) {
                    // Not within this chunk
                    continue;
                }

                const fadeOutDirection = _core_vector__WEBPACK_IMPORTED_MODULE_3__["enumDirectionToVector"][staticComp.localDirectionToWorld(direction)];
                const finalTile = realSlotPos.subScalars(
                    fadeOutDirection.x * (animProgress / 2 - 0.5),
                    fadeOutDirection.y * (animProgress / 2 - 0.5)
                );

                item.drawItemCenteredClipped(
                    (finalTile.x + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                    (finalTile.y + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                    parameters,
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter
                );
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/item_ejector.js":
/*!*********************************************!*\
  !*** ./src/js/game/systems/item_ejector.js ***!
  \*********************************************/
/*! exports provided: ItemEjectorSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemEjectorSystem", function() { return ItemEjectorSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/stale_area_detector */ "./src/js/core/stale_area_detector.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _components_belt__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../components/belt */ "./src/js/game/components/belt.js");
/* harmony import */ var _components_item_acceptor__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../components/item_acceptor */ "./src/js/game/components/item_acceptor.js");
/* harmony import */ var _components_item_ejector__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../components/item_ejector */ "./src/js/game/components/item_ejector.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");














const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("systems/ejector");

class ItemEjectorSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_11__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_item_ejector__WEBPACK_IMPORTED_MODULE_9__["ItemEjectorComponent"]]);

        this.staleAreaDetector = new _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_4__["StaleAreaDetector"]({
            root: this.root,
            name: "item-ejector",
            recomputeMethod: this.recomputeArea.bind(this),
        });

        this.staleAreaDetector.recomputeOnComponentsChanged(
            [_components_item_ejector__WEBPACK_IMPORTED_MODULE_9__["ItemEjectorComponent"], _components_item_acceptor__WEBPACK_IMPORTED_MODULE_8__["ItemAcceptorComponent"], _components_belt__WEBPACK_IMPORTED_MODULE_7__["BeltComponent"]],
            1
        );

        this.root.signals.postLoadHook.add(this.recomputeCacheFull, this);
    }

    /**
     * Recomputes an area after it changed
     * @param {Rectangle} area
     */
    recomputeArea(area) {
        /** @type {Set<number>} */
        const seenUids = new Set();
        for (let x = 0; x < area.w; ++x) {
            for (let y = 0; y < area.h; ++y) {
                const tileX = area.x + x;
                const tileY = area.y + y;
                // @NOTICE: Item ejector currently only supports regular layer
                const contents = this.root.map.getLayerContentXY(tileX, tileY, "regular");
                if (contents && contents.components.ItemEjector) {
                    if (!seenUids.has(contents.uid)) {
                        seenUids.add(contents.uid);
                        this.recomputeSingleEntityCache(contents);
                    }
                }
            }
        }
    }

    /**
     * Recomputes the whole cache after the game has loaded
     */
    recomputeCacheFull() {
        logger.log("Full cache recompute in post load hook");
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            this.recomputeSingleEntityCache(entity);
        }
    }

    /**
     * @param {Entity} entity
     */
    recomputeSingleEntityCache(entity) {
        const ejectorComp = entity.components.ItemEjector;
        const staticComp = entity.components.StaticMapEntity;

        for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) {
            const ejectorSlot = ejectorComp.slots[slotIndex];

            // Clear the old cache.
            ejectorSlot.cachedDestSlot = null;
            ejectorSlot.cachedTargetEntity = null;
            ejectorSlot.cachedBeltPath = null;

            // Figure out where and into which direction we eject items
            const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
            const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction);
            const ejectSlotWsDirectionVector = _core_vector__WEBPACK_IMPORTED_MODULE_5__["enumDirectionToVector"][ejectSlotWsDirection];
            const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);

            // Try to find the given acceptor component to take the item
            // Since there can be cross layer dependencies, check on all layers
            const targetEntities = this.root.map.getLayersContentsMultipleXY(
                ejectSlotTargetWsTile.x,
                ejectSlotTargetWsTile.y
            );

            for (let i = 0; i < targetEntities.length; ++i) {
                const targetEntity = targetEntities[i];

                const targetStaticComp = targetEntity.components.StaticMapEntity;
                const targetBeltComp = targetEntity.components.Belt;

                // Check for belts (special case)
                if (targetBeltComp) {
                    const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(_core_vector__WEBPACK_IMPORTED_MODULE_5__["enumDirection"].top);
                    if (ejectSlotWsDirection === beltAcceptingDirection) {
                        ejectorSlot.cachedTargetEntity = targetEntity;
                        ejectorSlot.cachedBeltPath = targetBeltComp.assignedPath;
                        break;
                    }
                }

                // Check for item acceptors
                const targetAcceptorComp = targetEntity.components.ItemAcceptor;
                if (!targetAcceptorComp) {
                    // Entity doesn't accept items
                    continue;
                }

                const matchingSlot = targetAcceptorComp.findMatchingSlot(
                    targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
                    targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection)
                );

                if (!matchingSlot) {
                    // No matching slot found
                    continue;
                }

                // A slot can always be connected to one other slot only
                ejectorSlot.cachedTargetEntity = targetEntity;
                ejectorSlot.cachedDestSlot = matchingSlot;
                break;
            }
        }
    }

    update() {
        this.staleAreaDetector.update();

        // Precompute effective belt speed
        let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;

        if (false) {}

        // Go over all cache entries
        for (let i = 0; i < this.allEntities.length; ++i) {
            const sourceEntity = this.allEntities[i];
            const sourceEjectorComp = sourceEntity.components.ItemEjector;

            const slots = sourceEjectorComp.slots;
            for (let j = 0; j < slots.length; ++j) {
                const sourceSlot = slots[j];
                const item = sourceSlot.item;
                if (!item) {
                    // No item available to be ejected
                    continue;
                }

                // Advance items on the slot
                sourceSlot.progress = Math.min(
                    1,
                    sourceSlot.progress +
                        progressGrowth *
                            this.root.hubGoals.getBeltBaseSpeed() *
                            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts
                );

                if (false) {}

                // Check if we are still in the process of ejecting, can't proceed then
                if (sourceSlot.progress < 1.0) {
                    continue;
                }

                // Check if we are ejecting to a belt path
                const destPath = sourceSlot.cachedBeltPath;
                if (destPath) {
                    // Try passing the item over
                    if (destPath.tryAcceptItem(item)) {
                        sourceSlot.item = null;
                    }

                    // Always stop here, since there can *either* be a belt path *or*
                    // a slot
                    continue;
                }

                // Check if the target acceptor can actually accept this item
                const destEntity = sourceSlot.cachedTargetEntity;
                const destSlot = sourceSlot.cachedDestSlot;
                if (destSlot) {
                    const targetAcceptorComp = destEntity.components.ItemAcceptor;
                    if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
                        continue;
                    }

                    // Try to hand over the item
                    if (this.tryPassOverItem(item, destEntity, destSlot.index)) {
                        // Handover successful, clear slot
                        if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
                            targetAcceptorComp.onItemAccepted(
                                destSlot.index,
                                destSlot.acceptedDirection,
                                item
                            );
                        }
                        sourceSlot.item = null;
                        continue;
                    }
                }
            }
        }
    }

    /**
     *
     * @param {BaseItem} item
     * @param {Entity} receiver
     * @param {number} slotIndex
     */
    tryPassOverItem(item, receiver, slotIndex) {
        // Try figuring out how what to do with the item
        // @TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.

        const beltComp = receiver.components.Belt;
        if (beltComp) {
            const path = beltComp.assignedPath;
            window.assert(path, "belt has no path");
            if (path.tryAcceptItem(item)) {
                return true;
            }
            // Belt can have nothing else
            return false;
        }

        const itemProcessorComp = receiver.components.ItemProcessor;
        if (itemProcessorComp) {
            // Check for potential filters
            if (!this.root.systemMgr.systems.itemProcessor.checkRequirements(receiver, item, slotIndex)) {
                return false;
            }

            // Its an item processor ..
            if (itemProcessorComp.tryTakeItem(item, slotIndex)) {
                return true;
            }
            // Item processor can have nothing else
            return false;
        }

        const undergroundBeltComp = receiver.components.UndergroundBelt;
        if (undergroundBeltComp) {
            // Its an underground belt. yay.
            if (
                undergroundBeltComp.tryAcceptExternalItem(
                    item,
                    this.root.hubGoals.getUndergroundBeltBaseSpeed()
                )
            ) {
                return true;
            }

            // Underground belt can have nothing else
            return false;
        }

        const storageComp = receiver.components.Storage;
        if (storageComp) {
            // It's a storage
            if (storageComp.canAcceptItem(item)) {
                storageComp.takeItem(item);
                return true;
            }

            // Storage can't have anything else
            return false;
        }

        const filterComp = receiver.components.Filter;
        if (filterComp) {
            // It's a filter! Unfortunately the filter has to know a lot about it's
            // surrounding state and components, so it can't be within the component itself.
            if (this.root.systemMgr.systems.filter.tryAcceptItem(receiver, slotIndex, item)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        if (this.root.app.settings.getAllSettings().simplifiedBelts) {
            // Disabled in potato mode
            return;
        }

        const contents = chunk.containedEntitiesByLayer.regular;

        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const ejectorComp = entity.components.ItemEjector;
            if (!ejectorComp) {
                continue;
            }

            const staticComp = entity.components.StaticMapEntity;

            for (let i = 0; i < ejectorComp.slots.length; ++i) {
                const slot = ejectorComp.slots[i];
                const ejectedItem = slot.item;

                if (!ejectedItem) {
                    // No item
                    continue;
                }

                if (!ejectorComp.renderFloatingItems && !slot.cachedTargetEntity) {
                    // Not connected to any building
                    continue;
                }

                // Limit the progress to the maximum available space on the next belt (also see #1000)
                let progress = slot.progress;
                const nextBeltPath = slot.cachedBeltPath;
                if (nextBeltPath) {
                    progress = Math.min(
                        progress,
                        nextBeltPath.spacingToFirstItem / _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].itemSpacingOnBelts
                    );
                }

                // Skip if the item would barely be visible
                if (progress < 0.05) {
                    continue;
                }

                const realPosition = staticComp.localTileToWorld(slot.pos);
                if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) {
                    // Not within this chunk
                    continue;
                }

                const realDirection = staticComp.localDirectionToWorld(slot.direction);
                const realDirectionVector = _core_vector__WEBPACK_IMPORTED_MODULE_5__["enumDirectionToVector"][realDirection];

                const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * progress;
                const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * progress;

                const worldX = tileX * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                const worldY = tileY * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;

                ejectedItem.drawItemCenteredClipped(
                    worldX,
                    worldY,
                    parameters,
                    _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter
                );
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/item_processor.js":
/*!***********************************************!*\
  !*** ./src/js/game/systems/item_processor.js ***!
  \***********************************************/
/*! exports provided: ItemProcessorSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemProcessorSystem", function() { return ItemProcessorSystem; });
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../colors */ "./src/js/game/colors.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../items/shape_item */ "./src/js/game/items/shape_item.js");









/**
 * We need to allow queuing charges, otherwise the throughput will stall
 */
const MAX_QUEUED_CHARGES = 2;

/**
 * Whole data for a produced item
 *
 * @typedef {{
 *   item: BaseItem,
 *   preferredSlot?: number,
 *   requiredSlot?: number,
 *   doNotTrack?: boolean
 * }} ProducedItem
 */

/**
 * Type of a processor implementation
 * @typedef {{
 *   entity: Entity,
 *   items: Array<{ item: BaseItem, sourceSlot: number }>,
 *   itemsBySlot: Object<number, BaseItem>,
 *   outItems: Array<ProducedItem>
 *   }} ProcessorImplementationPayload
 */

class ItemProcessorSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_4__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["ItemProcessorComponent"]]);

        /**
         * @type {Object<enumItemProcessorTypes, function(ProcessorImplementationPayload) : string>}
         */
        this.handlers = {
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].balancer]: this.process_BALANCER,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].cutter]: this.process_CUTTER,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].cutterQuad]: this.process_CUTTER_QUAD,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].rotater]: this.process_ROTATER,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].rotaterCCW]: this.process_ROTATER_CCW,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].rotater180]: this.process_ROTATER_180,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].stacker]: this.process_STACKER,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].trash]: this.process_TRASH,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].mixer]: this.process_MIXER,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].painter]: this.process_PAINTER,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].painterDouble]: this.process_PAINTER_DOUBLE,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].painterQuad]: this.process_PAINTER_QUAD,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].hub]: this.process_HUB,
            [_components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorTypes"].reader]: this.process_READER,
        };

        // Bind all handlers
        for (const key in this.handlers) {
            this.handlers[key] = this.handlers[key].bind(this);
        }
    }

    update() {
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];

            const processorComp = entity.components.ItemProcessor;
            const ejectorComp = entity.components.ItemEjector;

            const currentCharge = processorComp.ongoingCharges[0];

            if (currentCharge) {
                // Process next charge
                if (currentCharge.remainingTime > 0.0) {
                    currentCharge.remainingTime -= this.root.dynamicTickrate.deltaSeconds;
                    if (currentCharge.remainingTime < 0.0) {
                        // Add bonus time, this is the time we spent too much
                        processorComp.bonusTime += -currentCharge.remainingTime;
                    }
                }

                // Check if it finished
                if (currentCharge.remainingTime <= 0.0) {
                    const itemsToEject = currentCharge.items;

                    // Go over all items and try to eject them
                    for (let j = 0; j < itemsToEject.length; ++j) {
                        const { item, requiredSlot, preferredSlot } = itemsToEject[j];

                        window.assert(ejectorComp, "To eject items, the building needs to have an ejector");

                        let slot = null;
                        if (requiredSlot !== null && requiredSlot !== undefined) {
                            // We have a slot override, check if that is free
                            if (ejectorComp.canEjectOnSlot(requiredSlot)) {
                                slot = requiredSlot;
                            }
                        } else if (preferredSlot !== null && preferredSlot !== undefined) {
                            // We have a slot preference, try using it but otherwise use a free slot
                            if (ejectorComp.canEjectOnSlot(preferredSlot)) {
                                slot = preferredSlot;
                            } else {
                                slot = ejectorComp.getFirstFreeSlot();
                            }
                        } else {
                            // We can eject on any slot
                            slot = ejectorComp.getFirstFreeSlot();
                        }

                        if (slot !== null) {
                            // Alright, we can actually eject
                            if (!ejectorComp.tryEject(slot, item)) {
                                window.assert(false, "Failed to eject");
                            } else {
                                itemsToEject.splice(j, 1);
                                j -= 1;
                            }
                        }
                    }

                    // If the charge was entirely emptied to the outputs, start the next charge
                    if (itemsToEject.length === 0) {
                        processorComp.ongoingCharges.shift();
                    }
                }
            }

            // Check if we have an empty queue and can start a new charge
            if (processorComp.ongoingCharges.length < MAX_QUEUED_CHARGES) {
                if (this.canProcess(entity)) {
                    this.startNewCharge(entity);
                }
            }
        }
    }

    /**
     * Returns true if the entity should accept the given item on the given slot.
     * This should only be called with matching items! I.e. if a color item is expected
     * on the given slot, then only a color item must be passed.
     * @param {Entity} entity
     * @param {BaseItem} item The item to accept
     * @param {number} slotIndex The slot index
     * @returns {boolean}
     */
    checkRequirements(entity, item, slotIndex) {
        const itemProcessorComp = entity.components.ItemProcessor;
        const pinsComp = entity.components.WiredPins;

        switch (itemProcessorComp.processingRequirement) {
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorRequirements"].painterQuad: {
                if (slotIndex === 0) {
                    // Always accept the shape
                    return true;
                }

                // Check the network value at the given slot
                const network = pinsComp.slots[slotIndex - 1].linkedNetwork;
                const slotIsEnabled = network && network.hasValue() && Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(network.currentValue);
                if (!slotIsEnabled) {
                    return false;
                }
                return true;
            }

            // By default, everything is accepted
            default:
                return true;
        }
    }

    /**
     * Checks whether it's possible to process something
     * @param {Entity} entity
     */
    canProcess(entity) {
        const processorComp = entity.components.ItemProcessor;

        switch (processorComp.processingRequirement) {
            // DEFAULT
            // By default, we can start processing once all inputs are there
            case null: {
                return processorComp.inputSlots.length >= processorComp.inputsPerCharge;
            }

            // QUAD PAINTER
            // For the quad painter, it might be possible to start processing earlier
            case _components_item_processor__WEBPACK_IMPORTED_MODULE_2__["enumItemProcessorRequirements"].painterQuad: {
                const pinsComp = entity.components.WiredPins;

                /** @type {Object.<number, { item: BaseItem, sourceSlot: number }>} */
                const itemsBySlot = {};
                for (let i = 0; i < processorComp.inputSlots.length; ++i) {
                    itemsBySlot[processorComp.inputSlots[i].sourceSlot] = processorComp.inputSlots[i];
                }

                // First slot is the shape, so if it's not there we can't do anything
                if (!itemsBySlot[0]) {
                    return false;
                }

                const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
                const slotStatus = [];

                // Check which slots are enabled
                for (let i = 0; i < 4; ++i) {
                    // Extract the network value on the Nth pin
                    const network = pinsComp.slots[i].linkedNetwork;
                    const networkValue = network && network.hasValue() ? network.currentValue : null;

                    // If there is no "1" on that slot, don't paint there
                    if (!Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(networkValue)) {
                        slotStatus.push(false);
                        continue;
                    }

                    slotStatus.push(true);
                }

                // All slots are disabled
                if (!slotStatus.includes(true)) {
                    return false;
                }

                // Check if all colors of the enabled slots are there
                for (let i = 0; i < slotStatus.length; ++i) {
                    if (slotStatus[i] && !itemsBySlot[1 + i]) {
                        // A slot which is enabled wasn't enabled. Make sure if there is anything on the quadrant,
                        // it is not possible to paint, but if there is nothing we can ignore it
                        for (let j = 0; j < 4; ++j) {
                            const layer = shapeItem.definition.layers[j];
                            if (layer && layer[i]) {
                                return false;
                            }
                        }
                    }
                }

                return true;
            }

            default:
                window.assert(false, "Unknown requirement for " + processorComp.processingRequirement);
        }
    }

    /**
     * Starts a new charge for the entity
     * @param {Entity} entity
     */
    startNewCharge(entity) {
        const processorComp = entity.components.ItemProcessor;

        // First, take items
        const items = processorComp.inputSlots;
        processorComp.inputSlots = [];

        /** @type {Object<string, BaseItem>} */
        const itemsBySlot = {};
        for (let i = 0; i < items.length; ++i) {
            itemsBySlot[items[i].sourceSlot] = items[i].item;
        }

        /** @type {Array<ProducedItem>} */
        const outItems = [];

        /** @type {function(ProcessorImplementationPayload) : void} */
        const handler = this.handlers[processorComp.type];
        window.assert(handler, "No handler for processor type defined: " + processorComp.type);

        // Call implementation
        handler({
            entity,
            items,
            itemsBySlot,
            outItems,
        });

        // Track produced items
        for (let i = 0; i < outItems.length; ++i) {
            if (!outItems[i].doNotTrack) {
                this.root.signals.itemProduced.dispatch(outItems[i].item);
            }
        }

        // Queue Charge
        const baseSpeed = this.root.hubGoals.getProcessorBaseSpeed(processorComp.type);
        const originalTime = 1 / baseSpeed;

        const bonusTimeToApply = Math.min(originalTime, processorComp.bonusTime);
        const timeToProcess = originalTime - bonusTimeToApply;

        processorComp.bonusTime -= bonusTimeToApply;
        processorComp.ongoingCharges.push({
            items: outItems,
            remainingTime: timeToProcess,
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_BALANCER(payload) {
        window.assert(
            payload.entity.components.ItemEjector,
            "To be a balancer, the building needs to have an ejector"
        );
        const availableSlots = payload.entity.components.ItemEjector.slots.length;
        const processorComp = payload.entity.components.ItemProcessor;

        const nextSlot = processorComp.nextOutputSlot++ % availableSlots;

        for (let i = 0; i < payload.items.length; ++i) {
            payload.outItems.push({
                item: payload.items[i].item,
                preferredSlot: (nextSlot + i) % availableSlots,
                doNotTrack: true,
            });
        }
        return true;
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_CUTTER(payload) {
        const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
        window.assert(inputItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for cut is not a shape");
        const inputDefinition = inputItem.definition;

        const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);

        for (let i = 0; i < cutDefinitions.length; ++i) {
            const definition = cutDefinitions[i];
            if (!definition.isEntirelyEmpty()) {
                payload.outItems.push({
                    item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
                    requiredSlot: i,
                });
            }
        }
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_CUTTER_QUAD(payload) {
        const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
        window.assert(inputItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for cut is not a shape");
        const inputDefinition = inputItem.definition;

        const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);

        for (let i = 0; i < cutDefinitions.length; ++i) {
            const definition = cutDefinitions[i];
            if (!definition.isEntirelyEmpty()) {
                payload.outItems.push({
                    item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
                    requiredSlot: i,
                });
            }
        }
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_ROTATER(payload) {
        const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
        window.assert(inputItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for rotation is not a shape");
        const inputDefinition = inputItem.definition;

        const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_ROTATER_CCW(payload) {
        const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
        window.assert(inputItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for rotation is not a shape");
        const inputDefinition = inputItem.definition;

        const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition);
        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_ROTATER_180(payload) {
        const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
        window.assert(inputItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for rotation is not a shape");
        const inputDefinition = inputItem.definition;

        const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotate180(inputDefinition);
        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_STACKER(payload) {
        const lowerItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
        const upperItem = /** @type {ShapeItem} */ (payload.itemsBySlot[1]);

        window.assert(lowerItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for lower stack is not a shape");
        window.assert(upperItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for upper stack is not a shape");

        const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack(
            lowerItem.definition,
            upperItem.definition
        );
        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_TRASH(payload) {
        // Do nothing ..
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_MIXER(payload) {
        // Find both colors and combine them
        const item1 = /** @type {ColorItem} */ (payload.items[0].item);
        const item2 = /** @type {ColorItem} */ (payload.items[1].item);
        window.assert(item1 instanceof _items_color_item__WEBPACK_IMPORTED_MODULE_6__["ColorItem"], "Input for color mixer is not a color");
        window.assert(item2 instanceof _items_color_item__WEBPACK_IMPORTED_MODULE_6__["ColorItem"], "Input for color mixer is not a color");

        const color1 = item1.color;
        const color2 = item2.color;

        // Try finding mixer color, and if we can't mix it we simply return the same color
        const mixedColor = _colors__WEBPACK_IMPORTED_MODULE_1__["enumColorMixingResults"][color1][color2];
        let resultColor = color1;
        if (mixedColor) {
            resultColor = mixedColor;
        }
        payload.outItems.push({
            item: _items_color_item__WEBPACK_IMPORTED_MODULE_6__["COLOR_ITEM_SINGLETONS"][resultColor],
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_PAINTER(payload) {
        const shapeItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
        const colorItem = /** @type {ColorItem} */ (payload.itemsBySlot[1]);

        const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
            shapeItem.definition,
            colorItem.color
        );

        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_PAINTER_DOUBLE(payload) {
        const shapeItem1 = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
        const shapeItem2 = /** @type {ShapeItem} */ (payload.itemsBySlot[1]);
        const colorItem = /** @type {ColorItem} */ (payload.itemsBySlot[2]);

        window.assert(shapeItem1 instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for painter is not a shape");
        window.assert(shapeItem2 instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for painter is not a shape");
        window.assert(colorItem instanceof _items_color_item__WEBPACK_IMPORTED_MODULE_6__["ColorItem"], "Input for painter is not a color");

        const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
            shapeItem1.definition,
            colorItem.color
        );

        const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
            shapeItem2.definition,
            colorItem.color
        );
        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1),
        });

        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_PAINTER_QUAD(payload) {
        const shapeItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
        window.assert(shapeItem instanceof _items_shape_item__WEBPACK_IMPORTED_MODULE_7__["ShapeItem"], "Input for painter is not a shape");

        /** @type {Array<enumColors>} */
        const colors = [null, null, null, null];
        for (let i = 0; i < 4; ++i) {
            if (payload.itemsBySlot[i + 1]) {
                colors[i] = /** @type {ColorItem} */ (payload.itemsBySlot[i + 1]).color;
            }
        }

        const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors(
            shapeItem.definition,
            /** @type {[string, string, string, string]} */ (colors)
        );

        payload.outItems.push({
            item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
        });
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_READER(payload) {
        // Pass through the item
        const item = payload.itemsBySlot[0];
        payload.outItems.push({
            item,
            doNotTrack: true,
        });

        // Track the item
        const readerComp = payload.entity.components.BeltReader;
        readerComp.lastItemTimes.push(this.root.time.now());
        readerComp.lastItem = item;
    }

    /**
     * @param {ProcessorImplementationPayload} payload
     */
    process_HUB(payload) {
        const hubComponent = payload.entity.components.Hub;
        window.assert(hubComponent, "Hub item processor has no hub component");

        for (let i = 0; i < payload.items.length; ++i) {
            const item = /** @type {ShapeItem} */ (payload.items[i].item);
            this.root.hubGoals.handleDefinitionDelivered(item.definition);
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/item_processor_overlays.js":
/*!********************************************************!*\
  !*** ./src/js/game/systems/item_processor_overlays.js ***!
  \********************************************************/
/*! exports provided: ItemProcessorOverlaysSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemProcessorOverlaysSystem", function() { return ItemProcessorOverlaysSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _components_item_processor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/item_processor */ "./src/js/game/components/item_processor.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../game_system */ "./src/js/game/game_system.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");









class ItemProcessorOverlaysSystem extends _game_system__WEBPACK_IMPORTED_MODULE_5__["GameSystem"] {
    constructor(root) {
        super(root);

        this.spriteDisabled = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/misc/processor_disabled.png");
        this.spriteDisconnected = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/misc/processor_disconnected.png");

        this.readerOverlaySprite = _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite("sprites/misc/reader_overlay.png");

        this.drawnUids = new Set();
        this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
    }

    clearDrawnUids() {
        this.drawnUids.clear();
    }

    /**
     *
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const processorComp = entity.components.ItemProcessor;
            const filterComp = entity.components.Filter;

            // Draw processor overlays
            if (processorComp) {
                const requirement = processorComp.processingRequirement;
                if (!requirement && processorComp.type !== _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].reader) {
                    continue;
                }

                if (this.drawnUids.has(entity.uid)) {
                    continue;
                }
                this.drawnUids.add(entity.uid);

                switch (requirement) {
                    case _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorRequirements"].painterQuad: {
                        this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: true });
                        break;
                    }
                }

                if (processorComp.type === _components_item_processor__WEBPACK_IMPORTED_MODULE_3__["enumItemProcessorTypes"].reader) {
                    this.drawReaderOverlays(parameters, entity);
                }
            }

            // Draw filter overlays
            else if (filterComp) {
                if (this.drawnUids.has(entity.uid)) {
                    continue;
                }
                this.drawnUids.add(entity.uid);

                this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: false });
            }
        }
    }

    /**
     *
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {Entity} entity
     */
    drawReaderOverlays(parameters, entity) {
        const staticComp = entity.components.StaticMapEntity;
        const readerComp = entity.components.BeltReader;

        this.readerOverlaySprite.drawCachedCentered(
            parameters,
            (staticComp.origin.x + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            (staticComp.origin.y + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize
        );

        parameters.context.fillStyle = "#333439";
        parameters.context.textAlign = "center";
        parameters.context.font = "bold 10px GameFont";
        parameters.context.fillText(
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["round1DigitLocalized"])(readerComp.lastThroughput),
            (staticComp.origin.x + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            (staticComp.origin.y + 0.62) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize
        );

        parameters.context.textAlign = "left";
    }

    /**
     *
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {Entity} entity
     * @param {object} param0
     * @param {boolean=} param0.drawIfFalse
     */
    drawConnectedSlotRequirement(parameters, entity, { drawIfFalse = true }) {
        const staticComp = entity.components.StaticMapEntity;
        const pinsComp = entity.components.WiredPins;

        let anySlotConnected = false;

        // Check if any slot has a value
        for (let i = 0; i < pinsComp.slots.length; ++i) {
            const slot = pinsComp.slots[i];
            const network = slot.linkedNetwork;
            if (network && network.hasValue()) {
                anySlotConnected = true;

                if (Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_6__["isTruthyItem"])(network.currentValue) || !drawIfFalse) {
                    // No need to draw anything
                    return;
                }
            }
        }

        const pulse = Object(_core_utils__WEBPACK_IMPORTED_MODULE_2__["smoothPulse"])(this.root.time.now());
        parameters.context.globalAlpha = 0.6 + 0.4 * pulse;
        const sprite = anySlotConnected ? this.spriteDisabled : this.spriteDisconnected;
        sprite.drawCachedCentered(
            parameters,
            (staticComp.origin.x + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            (staticComp.origin.y + 0.5) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize * (0.7 + 0.2 * pulse)
        );

        parameters.context.globalAlpha = 1;
    }
}


/***/ }),

/***/ "./src/js/game/systems/item_producer.js":
/*!**********************************************!*\
  !*** ./src/js/game/systems/item_producer.js ***!
  \**********************************************/
/*! exports provided: ItemProducerSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ItemProducerSystem", function() { return ItemProducerSystem; });
/* harmony import */ var _components_item_producer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../components/item_producer */ "./src/js/game/components/item_producer.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");



class ItemProducerSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_1__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_item_producer__WEBPACK_IMPORTED_MODULE_0__["ItemProducerComponent"]]);
    }

    update() {
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const pinsComp = entity.components.WiredPins;
            const pin = pinsComp.slots[0];
            const network = pin.linkedNetwork;

            if (!network || !network.hasValue()) {
                continue;
            }

            const ejectorComp = entity.components.ItemEjector;
            ejectorComp.tryEject(0, network.currentValue);
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/lever.js":
/*!**************************************!*\
  !*** ./src/js/game/systems/lever.js ***!
  \**************************************/
/*! exports provided: LeverSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LeverSystem", function() { return LeverSystem; });
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _components_lever__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/lever */ "./src/js/game/components/lever.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");







class LeverSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_0__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_lever__WEBPACK_IMPORTED_MODULE_1__["LeverComponent"]]);

        this.spriteOn = _core_loader__WEBPACK_IMPORTED_MODULE_5__["Loader"].getSprite("sprites/wires/lever_on.png");
        this.spriteOff = _core_loader__WEBPACK_IMPORTED_MODULE_5__["Loader"].getSprite("sprites/buildings/lever.png");
    }

    update() {
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];

            const leverComp = entity.components.Lever;
            const pinsComp = entity.components.WiredPins;

            // Simply sync the status to the first slot
            pinsComp.slots[0].value = leverComp.toggled ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__["BOOL_TRUE_SINGLETON"] : _items_boolean_item__WEBPACK_IMPORTED_MODULE_2__["BOOL_FALSE_SINGLETON"];
        }
    }

    /**
     * Draws a given chunk
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const leverComp = entity.components.Lever;
            if (leverComp) {
                const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff;
                entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/logic_gate.js":
/*!*******************************************!*\
  !*** ./src/js/game/systems/logic_gate.js ***!
  \*******************************************/
/*! exports provided: LogicGateSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LogicGateSystem", function() { return LogicGateSystem; });
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../colors */ "./src/js/game/colors.js");
/* harmony import */ var _components_logic_gate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../components/logic_gate */ "./src/js/game/components/logic_gate.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _items_color_item__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../items/color_item */ "./src/js/game/items/color_item.js");
/* harmony import */ var _items_shape_item__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../items/shape_item */ "./src/js/game/items/shape_item.js");
/* harmony import */ var _shape_definition__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../shape_definition */ "./src/js/game/shape_definition.js");










class LogicGateSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_4__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["LogicGateComponent"]]);

        this.boundOperations = {
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].and]: this.compute_AND.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].not]: this.compute_NOT.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].xor]: this.compute_XOR.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].or]: this.compute_OR.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].transistor]: this.compute_IF.bind(this),

            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].rotater]: this.compute_ROTATE.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].analyzer]: this.compute_ANALYZE.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].cutter]: this.compute_CUT.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].unstacker]: this.compute_UNSTACK.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].compare]: this.compute_COMPARE.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].stacker]: this.compute_STACKER.bind(this),
            [_components_logic_gate__WEBPACK_IMPORTED_MODULE_2__["enumLogicGateType"].painter]: this.compute_PAINTER.bind(this),
        };
    }

    update() {
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const logicComp = entity.components.LogicGate;
            const slotComp = entity.components.WiredPins;

            const slotValues = [];

            // Store if any conflict was found
            let anyConflict = false;

            // Gather inputs from all connected networks
            for (let i = 0; i < slotComp.slots.length; ++i) {
                const slot = slotComp.slots[i];
                if (slot.type !== _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalAcceptor) {
                    continue;
                }
                const network = slot.linkedNetwork;
                if (network) {
                    if (network.valueConflict) {
                        anyConflict = true;
                        break;
                    }
                    slotValues.push(network.currentValue);
                } else {
                    slotValues.push(null);
                }
            }

            // Handle conflicts
            if (anyConflict) {
                for (let i = 0; i < slotComp.slots.length; ++i) {
                    const slot = slotComp.slots[i];
                    if (slot.type !== _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalEjector) {
                        continue;
                    }
                    slot.value = null;
                }
                continue;
            }

            // Compute actual result
            const result = this.boundOperations[logicComp.type](slotValues);

            if (Array.isArray(result)) {
                let resultIndex = 0;
                for (let i = 0; i < slotComp.slots.length; ++i) {
                    const slot = slotComp.slots[i];
                    if (slot.type !== _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalEjector) {
                        continue;
                    }
                    slot.value = result[resultIndex++];
                }
            } else {
                // @TODO: For now we hardcode the value to always be slot 0
                window.assert(
                    slotValues.length === slotComp.slots.length - 1,
                    "Bad slot config, should have N acceptor slots and 1 ejector"
                );
                window.assert(slotComp.slots[0].type === _components_wired_pins__WEBPACK_IMPORTED_MODULE_3__["enumPinSlotType"].logicalEjector, "Slot 0 should be ejector");
                slotComp.slots[0].value = result;
            }
        }
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_AND(parameters) {
        window.assert(parameters.length === 2, "bad parameter count for AND");
        return Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[0]) && Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[1])
            ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]
            : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_NOT(parameters) {
        return Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[0]) ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"] : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_XOR(parameters) {
        window.assert(parameters.length === 2, "bad parameter count for XOR");
        return Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[0]) !== Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[1])
            ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]
            : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_OR(parameters) {
        window.assert(parameters.length === 2, "bad parameter count for OR");
        return Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[0]) || Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(parameters[1])
            ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]
            : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_IF(parameters) {
        window.assert(parameters.length === 2, "bad parameter count for IF");
        const flag = parameters[0];
        const value = parameters[1];

        // pass through item
        if (Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["isTruthyItem"])(flag)) {
            return value;
        }

        return null;
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_ROTATE(parameters) {
        const item = parameters[0];
        if (!item || item.getItemType() !== "shape") {
            // Not a shape
            return null;
        }

        const definition = /** @type {ShapeItem} */ (item).definition;
        const rotatedDefinitionCW = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
        return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW);
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {[BaseItem, BaseItem]}
     */
    compute_ANALYZE(parameters) {
        const item = parameters[0];
        if (!item || item.getItemType() !== "shape") {
            // Not a shape
            return [null, null];
        }

        const definition = /** @type {ShapeItem} */ (item).definition;
        const lowerLayer = /** @type {import("../shape_definition").ShapeLayer} */ (definition.layers[0]);
        if (!lowerLayer) {
            return [null, null];
        }

        const topRightContent = lowerLayer[0];

        if (!topRightContent || topRightContent.subShape === null) {
            return [null, null];
        }

        const newDefinition = new _shape_definition__WEBPACK_IMPORTED_MODULE_8__["ShapeDefinition"]({
            layers: [
                [
                    { subShape: topRightContent.subShape, color: _colors__WEBPACK_IMPORTED_MODULE_1__["enumColors"].uncolored },
                    { subShape: topRightContent.subShape, color: _colors__WEBPACK_IMPORTED_MODULE_1__["enumColors"].uncolored },
                    { subShape: topRightContent.subShape, color: _colors__WEBPACK_IMPORTED_MODULE_1__["enumColors"].uncolored },
                    { subShape: topRightContent.subShape, color: _colors__WEBPACK_IMPORTED_MODULE_1__["enumColors"].uncolored },
                ],
            ],
        });

        return [
            _items_color_item__WEBPACK_IMPORTED_MODULE_6__["COLOR_ITEM_SINGLETONS"][topRightContent.color],
            this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition),
        ];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {[BaseItem, BaseItem]}
     */
    compute_CUT(parameters) {
        const item = parameters[0];
        if (!item || item.getItemType() !== "shape") {
            // Not a shape
            return [null, null];
        }

        const definition = /** @type {ShapeItem} */ (item).definition;
        const result = this.root.shapeDefinitionMgr.shapeActionCutHalf(definition);
        return [
            result[0].isEntirelyEmpty()
                ? null
                : this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[0]),
            result[1].isEntirelyEmpty()
                ? null
                : this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[1]),
        ];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {[BaseItem, BaseItem]}
     */
    compute_UNSTACK(parameters) {
        const item = parameters[0];
        if (!item || item.getItemType() !== "shape") {
            // Not a shape
            return [null, null];
        }

        const definition = /** @type {ShapeItem} */ (item).definition;
        const layers = /** @type {Array<import("../shape_definition").ShapeLayer>}  */ (definition.layers);

        const upperLayerDefinition = new _shape_definition__WEBPACK_IMPORTED_MODULE_8__["ShapeDefinition"]({
            layers: [layers[layers.length - 1]],
        });

        const lowerLayers = layers.slice(0, layers.length - 1);
        const lowerLayerDefinition =
            lowerLayers.length > 0 ? new _shape_definition__WEBPACK_IMPORTED_MODULE_8__["ShapeDefinition"]({ layers: lowerLayers }) : null;

        return [
            lowerLayerDefinition
                ? this.root.shapeDefinitionMgr.getShapeItemFromDefinition(lowerLayerDefinition)
                : null,
            this.root.shapeDefinitionMgr.getShapeItemFromDefinition(upperLayerDefinition),
        ];
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_STACKER(parameters) {
        const lowerItem = parameters[0];
        const upperItem = parameters[1];

        if (!lowerItem || !upperItem) {
            // Empty
            return null;
        }

        if (lowerItem.getItemType() !== "shape" || upperItem.getItemType() !== "shape") {
            // Bad type
            return null;
        }

        const stackedShape = this.root.shapeDefinitionMgr.shapeActionStack(
            /** @type {ShapeItem} */ (lowerItem).definition,
            /** @type {ShapeItem} */ (upperItem).definition
        );

        return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedShape);
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_PAINTER(parameters) {
        const shape = parameters[0];
        const color = parameters[1];

        if (!shape || !color) {
            // Empty
            return null;
        }

        if (shape.getItemType() !== "shape" || color.getItemType() !== "color") {
            // Bad type
            return null;
        }

        const coloredShape = this.root.shapeDefinitionMgr.shapeActionPaintWith(
            /** @type {ShapeItem} */ (shape).definition,
            /** @type {ColorItem} */ (color).color
        );

        return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(coloredShape);
    }

    /**
     * @param {Array<BaseItem|null>} parameters
     * @returns {BaseItem}
     */
    compute_COMPARE(parameters) {
        const itemA = parameters[0];
        const itemB = parameters[1];

        if (!itemA || !itemB) {
            // Empty
            return null;
        }

        if (itemA.getItemType() !== itemB.getItemType()) {
            // Not the same type
            return _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
        }

        switch (itemA.getItemType()) {
            case "shape": {
                return /** @type {ShapeItem} */ (itemA).definition.getHash() ===
                    /** @type {ShapeItem} */ (itemB).definition.getHash()
                    ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]
                    : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
            }
            case "color": {
                return /** @type {ColorItem} */ (itemA).color === /** @type {ColorItem} */ (itemB).color
                    ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]
                    : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
            }

            case "boolean": {
                return /** @type {BooleanItem} */ (itemA).value === /** @type {BooleanItem} */ (itemB).value
                    ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"]
                    : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
            }

            default: {
                window.assert(false, "Bad item type: " + itemA.getItemType());
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/map_resources.js":
/*!**********************************************!*\
  !*** ./src/js/game/systems/map_resources.js ***!
  \**********************************************/
/*! exports provided: MapResourcesSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MapResourcesSystem", function() { return MapResourcesSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _game_system__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../game_system */ "./src/js/game/game_system.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");
/* harmony import */ var _theme__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../theme */ "./src/js/game/theme.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/draw_utils */ "./src/js/core/draw_utils.js");







class MapResourcesSystem extends _game_system__WEBPACK_IMPORTED_MODULE_2__["GameSystem"] {
    /**
     * Draws the map resources
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const basicChunkBackground = this.root.buffers.getForKey({
            key: "mapresourcebg",
            subKey: chunk.renderKey,
            w: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize,
            h: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize,
            dpi: 1,
            redrawMethod: this.generateChunkBackground.bind(this, chunk),
        });

        parameters.context.imageSmoothingEnabled = false;
        Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_5__["drawSpriteClipped"])({
            parameters,
            sprite: basicChunkBackground,
            x: chunk.tileX * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            y: chunk.tileY * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
            w: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize,
            h: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize,
            originalW: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize,
            originalH: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize,
        });
        parameters.context.imageSmoothingEnabled = true;

        parameters.context.globalAlpha = 0.5;

        if (this.root.app.settings.getAllSettings().lowQualityMapResources) {
            // LOW QUALITY: Draw patch items only
            for (let i = 0; i < chunk.patches.length; ++i) {
                const patch = chunk.patches[i];
                const destX = chunk.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize + patch.pos.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                const destY = chunk.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkWorldSize + patch.pos.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                const diameter = Math.min(80, 40 / parameters.zoomLevel);

                patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
            }
        } else {
            // HIGH QUALITY: Draw all items
            const layer = chunk.lowerLayer;
            const layerEntities = chunk.contents;
            for (let x = 0; x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++x) {
                const row = layer[x];
                const rowEntities = layerEntities[x];
                const worldX = (chunk.tileX + x) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;
                for (let y = 0; y < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++y) {
                    const lowerItem = row[y];

                    const entity = rowEntities[y];
                    if (entity) {
                        // Don't draw if there is an entity above
                        continue;
                    }

                    if (lowerItem) {
                        const worldY = (chunk.tileY + y) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize;

                        const destX = worldX + _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;
                        const destY = worldY + _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize;

                        lowerItem.drawItemCenteredClipped(
                            destX,
                            destY,
                            parameters,
                            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter
                        );
                    }
                }
            }
        }
        parameters.context.globalAlpha = 1;
    }

    /**
     *
     * @param {MapChunkView} chunk
     * @param {HTMLCanvasElement} canvas
     * @param {CanvasRenderingContext2D} context
     * @param {number} w
     * @param {number} h
     * @param {number} dpi
     */
    generateChunkBackground(chunk, canvas, context, w, h, dpi) {
        if (this.root.app.settings.getAllSettings().disableTileGrid) {
            // The map doesn't draw a background, so we have to
            context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_4__["THEME"].map.background;
            context.fillRect(0, 0, w, h);
        } else {
            context.clearRect(0, 0, w, h);
        }

        context.globalAlpha = 0.5;
        const layer = chunk.lowerLayer;
        for (let x = 0; x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++x) {
            const row = layer[x];
            for (let y = 0; y < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++y) {
                const item = row[y];
                if (item) {
                    context.fillStyle = item.getBackgroundColorAsResource();
                    context.fillRect(x, y, 1, 1);
                }
            }
        }

        if (this.root.app.settings.getAllSettings().displayChunkBorders) {
            context.fillStyle = _theme__WEBPACK_IMPORTED_MODULE_4__["THEME"].map.chunkBorders;
            context.fillRect(0, 0, w, 1);
            context.fillRect(0, 1, 1, h);
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/miner.js":
/*!**************************************!*\
  !*** ./src/js/game/systems/miner.js ***!
  \**************************************/
/*! exports provided: MinerSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MinerSystem", function() { return MinerSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _components_miner__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../components/miner */ "./src/js/game/components/miner.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");









class MinerSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_6__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_miner__WEBPACK_IMPORTED_MODULE_4__["MinerComponent"]]);

        this.needsRecompute = true;

        this.root.signals.entityAdded.add(this.onEntityChanged, this);
        this.root.signals.entityChanged.add(this.onEntityChanged, this);
        this.root.signals.entityDestroyed.add(this.onEntityChanged, this);
    }

    /**
     * Called whenever an entity got changed
     * @param {Entity} entity
     */
    onEntityChanged(entity) {
        const minerComp = entity.components.Miner;
        if (minerComp && minerComp.chainable) {
            // Miner component, need to recompute
            this.needsRecompute = true;
        }
    }

    update() {
        let miningSpeed = this.root.hubGoals.getMinerBaseSpeed();
        if (false) {}

        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const minerComp = entity.components.Miner;

            // Reset everything on recompute
            if (this.needsRecompute) {
                minerComp.cachedChainedMiner = null;
            }

            // Check if miner is above an actual tile
            if (!minerComp.cachedMinedItem) {
                const staticComp = entity.components.StaticMapEntity;
                const tileBelow = this.root.map.getLowerLayerContentXY(
                    staticComp.origin.x,
                    staticComp.origin.y
                );
                if (!tileBelow) {
                    continue;
                }
                minerComp.cachedMinedItem = tileBelow;
            }

            // First, try to get rid of chained items
            if (minerComp.itemChainBuffer.length > 0) {
                if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) {
                    minerComp.itemChainBuffer.shift();
                    continue;
                }
            }

            const mineDuration = 1 / miningSpeed;
            const timeSinceMine = this.root.time.now() - minerComp.lastMiningTime;
            if (timeSinceMine > mineDuration) {
                // Store how much we overflowed
                const buffer = Math.min(timeSinceMine - mineDuration, this.root.dynamicTickrate.deltaSeconds);

                if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) {
                    // Analytics hook
                    this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem);
                    // Store mining time
                    minerComp.lastMiningTime = this.root.time.now() - buffer;
                }
            }
        }

        // After this frame we are done
        this.needsRecompute = false;
    }

    /**
     * Finds the target chained miner for a given entity
     * @param {Entity} entity
     * @returns {Entity|false} The chained entity or null if not found
     */
    findChainedMiner(entity) {
        const ejectComp = entity.components.ItemEjector;
        const staticComp = entity.components.StaticMapEntity;
        const contentsBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y);
        if (!contentsBelow) {
            // This miner has no contents
            return null;
        }

        const ejectingSlot = ejectComp.slots[0];
        const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos);
        const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction);

        const targetTile = ejectingPos.add(_core_vector__WEBPACK_IMPORTED_MODULE_2__["enumDirectionToVector"][ejectingDirection]);
        const targetContents = this.root.map.getTileContent(targetTile, "regular");

        // Check if we are connected to another miner and thus do not eject directly
        if (targetContents) {
            const targetMinerComp = targetContents.components.Miner;
            if (targetMinerComp && targetMinerComp.chainable) {
                const targetLowerLayer = this.root.map.getLowerLayerContentXY(targetTile.x, targetTile.y);
                if (targetLowerLayer) {
                    return targetContents;
                }
            }
        }

        return false;
    }

    /**
     *
     * @param {Entity} entity
     * @param {BaseItem} item
     */
    tryPerformMinerEject(entity, item) {
        const minerComp = entity.components.Miner;
        const ejectComp = entity.components.ItemEjector;

        // Check if we are a chained miner
        if (minerComp.chainable) {
            const targetEntity = minerComp.cachedChainedMiner;

            // Check if the cache has to get recomputed
            if (targetEntity === null) {
                minerComp.cachedChainedMiner = this.findChainedMiner(entity);
            }

            // Check if we now have a target
            if (targetEntity) {
                const targetMinerComp = targetEntity.components.Miner;
                if (targetMinerComp.tryAcceptChainedItem(item)) {
                    return true;
                } else {
                    return false;
                }
            }
        }

        // Seems we are a regular miner or at the end of a row, try actually ejecting
        if (ejectComp.tryEject(0, item)) {
            return true;
        }

        return false;
    }

    /**
     *
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntitiesByLayer.regular;

        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const minerComp = entity.components.Miner;
            if (!minerComp) {
                continue;
            }

            const staticComp = entity.components.StaticMapEntity;
            if (!minerComp.cachedMinedItem) {
                continue;
            }

            // Draw the item background - this is to hide the ejected item animation from
            // the item ejector

            const padding = 3;
            const destX = staticComp.origin.x * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + padding;
            const destY = staticComp.origin.y * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + padding;
            const dimensions = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize - 2 * padding;

            if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) {
                parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource();
                parameters.context.fillRect(destX, destY, dimensions, dimensions);
            }

            minerComp.cachedMinedItem.drawItemCenteredClipped(
                (0.5 + staticComp.origin.x) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                (0.5 + staticComp.origin.y) * _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize,
                parameters,
                _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].defaultItemDiameter
            );
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/static_map_entity.js":
/*!**************************************************!*\
  !*** ./src/js/game/systems/static_map_entity.js ***!
  \**************************************************/
/*! exports provided: StaticMapEntitySystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StaticMapEntitySystem", function() { return StaticMapEntitySystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _game_system__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../game_system */ "./src/js/game/game_system.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");





class StaticMapEntitySystem extends _game_system__WEBPACK_IMPORTED_MODULE_2__["GameSystem"] {
    constructor(root) {
        super(root);

        /** @type {Set<number>} */
        this.drawnUids = new Set();

        this.root.signals.gameFrameStarted.add(this.clearUidList, this);
    }

    /**
     * Clears the uid list when a new frame started
     */
    clearUidList() {
        this.drawnUids.clear();
    }

    /**
     * Draws the static entities
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        if (false) {}

        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];

            const staticComp = entity.components.StaticMapEntity;
            const sprite = staticComp.getSprite();
            if (sprite) {
                // Avoid drawing an entity twice which has been drawn for
                // another chunk already
                if (this.drawnUids.has(entity.uid)) {
                    continue;
                }

                this.drawnUids.add(entity.uid);
                staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2);
            }
        }
    }

    /**
     * Draws the static wire entities
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawWiresChunk(parameters, chunk) {
        if (false) {}

        const drawnUids = new Set();
        const contents = chunk.wireContents;
        for (let y = 0; y < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++y) {
            for (let x = 0; x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++x) {
                const entity = contents[x][y];
                if (entity) {
                    if (drawnUids.has(entity.uid)) {
                        continue;
                    }
                    drawnUids.add(entity.uid);
                    const staticComp = entity.components.StaticMapEntity;

                    const sprite = staticComp.getSprite();
                    if (sprite) {
                        staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2);
                    }
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/storage.js":
/*!****************************************!*\
  !*** ./src/js/game/systems/storage.js ***!
  \****************************************/
/*! exports provided: StorageSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StorageSystem", function() { return StorageSystem; });
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _components_storage__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../components/storage */ "./src/js/game/components/storage.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");








class StorageSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_0__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_storage__WEBPACK_IMPORTED_MODULE_1__["StorageComponent"]]);

        this.storageOverlaySprite = _core_loader__WEBPACK_IMPORTED_MODULE_4__["Loader"].getSprite("sprites/misc/storage_overlay.png");

        /**
         * Stores which uids were already drawn to avoid drawing entities twice
         * @type {Set<number>}
         */
        this.drawnUids = new Set();

        this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
    }

    clearDrawnUids() {
        this.drawnUids.clear();
    }

    update() {
        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const storageComp = entity.components.Storage;
            const pinsComp = entity.components.WiredPins;

            // Eject from storage
            if (storageComp.storedItem && storageComp.storedCount > 0) {
                const ejectorComp = entity.components.ItemEjector;

                const nextSlot = ejectorComp.getFirstFreeSlot();
                if (nextSlot !== null) {
                    if (ejectorComp.tryEject(nextSlot, storageComp.storedItem)) {
                        storageComp.storedCount--;

                        if (storageComp.storedCount === 0) {
                            storageComp.storedItem = null;
                        }
                    }
                }
            }

            let targetAlpha = storageComp.storedCount > 0 ? 1 : 0;
            storageComp.overlayOpacity = Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["lerp"])(storageComp.overlayOpacity, targetAlpha, 0.05);

            pinsComp.slots[0].value = storageComp.storedItem;
            pinsComp.slots[1].value = storageComp.getIsFull() ? _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_TRUE_SINGLETON"] : _items_boolean_item__WEBPACK_IMPORTED_MODULE_5__["BOOL_FALSE_SINGLETON"];
        }
    }

    /**
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const storageComp = entity.components.Storage;
            if (!storageComp) {
                continue;
            }

            const storedItem = storageComp.storedItem;
            if (!storedItem) {
                continue;
            }

            if (this.drawnUids.has(entity.uid)) {
                continue;
            }

            this.drawnUids.add(entity.uid);

            const staticComp = entity.components.StaticMapEntity;

            const context = parameters.context;
            context.globalAlpha = storageComp.overlayOpacity;
            const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
            storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30);

            this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);

            if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) {
                context.font = "bold 10px GameFont";
                context.textAlign = "center";
                context.fillStyle = "#64666e";
                context.fillText(Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["formatBigNumber"])(storageComp.storedCount), center.x, center.y + 25.5);
                context.textAlign = "left";
            }
            context.globalAlpha = 1;
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/underground_belt.js":
/*!*************************************************!*\
  !*** ./src/js/game/systems/underground_belt.js ***!
  \*************************************************/
/*! exports provided: UndergroundBeltSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UndergroundBeltSystem", function() { return UndergroundBeltSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/stale_area_detector */ "./src/js/core/stale_area_detector.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../components/underground_belt */ "./src/js/game/components/underground_belt.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");











const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("tunnels");

class UndergroundBeltSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_9__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["UndergroundBeltComponent"]]);

        this.beltSprites = {
            [_components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].sender]: _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite(
                "sprites/buildings/underground_belt_entry.png"
            ),
            [_components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].receiver]: _core_loader__WEBPACK_IMPORTED_MODULE_1__["Loader"].getSprite(
                "sprites/buildings/underground_belt_exit.png"
            ),
        };

        this.staleAreaWatcher = new _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_4__["StaleAreaDetector"]({
            root: this.root,
            name: "underground-belt",
            recomputeMethod: this.recomputeArea.bind(this),
        });

        this.root.signals.entityManuallyPlaced.add(this.onEntityManuallyPlaced, this);

        // NOTICE: Once we remove a tunnel, we need to update the whole area to
        // clear outdated handles
        this.staleAreaWatcher.recomputeOnComponentsChanged(
            [_components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["UndergroundBeltComponent"]],
            _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].undergroundBeltMaxTilesByTier[_core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].undergroundBeltMaxTilesByTier.length - 1]
        );
    }

    /**
     * Callback when an entity got placed, used to remove belts between underground belts
     * @param {Entity} entity
     */
    onEntityManuallyPlaced(entity) {
        if (!this.root.app.settings.getAllSettings().enableTunnelSmartplace) {
            // Smart-place disabled
            return;
        }

        const undergroundComp = entity.components.UndergroundBelt;
        if (undergroundComp && undergroundComp.mode === _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].receiver) {
            const staticComp = entity.components.StaticMapEntity;
            const tile = staticComp.origin;

            const direction = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumAngleToDirection"][staticComp.rotation];
            const inverseDirection = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumInvertedDirections"][direction];
            const offset = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToVector"][inverseDirection];

            let currentPos = tile.copy();

            const tier = undergroundComp.tier;
            const range = _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].undergroundBeltMaxTilesByTier[tier];

            // FIND ENTRANCE
            // Search for the entrance which is farthest apart (this is why we can't reuse logic here)
            let matchingEntrance = null;
            for (let i = 0; i < range; ++i) {
                currentPos.addInplace(offset);
                const contents = this.root.map.getTileContent(currentPos, entity.layer);
                if (!contents) {
                    continue;
                }

                const contentsUndergroundComp = contents.components.UndergroundBelt;
                const contentsStaticComp = contents.components.StaticMapEntity;
                if (
                    contentsUndergroundComp &&
                    contentsUndergroundComp.tier === undergroundComp.tier &&
                    contentsUndergroundComp.mode === _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].sender &&
                    _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumAngleToDirection"][contentsStaticComp.rotation] === direction
                ) {
                    matchingEntrance = {
                        entity: contents,
                        range: i,
                    };
                }
            }

            if (!matchingEntrance) {
                // Nothing found
                return;
            }

            // DETECT OBSOLETE BELTS BETWEEN
            // Remove any belts between entrance and exit which have the same direction,
            // but only if they *all* have the right direction
            currentPos = tile.copy();
            let allBeltsMatch = true;
            for (let i = 0; i < matchingEntrance.range; ++i) {
                currentPos.addInplace(offset);

                const contents = this.root.map.getTileContent(currentPos, entity.layer);
                if (!contents) {
                    allBeltsMatch = false;
                    break;
                }

                const contentsStaticComp = contents.components.StaticMapEntity;
                const contentsBeltComp = contents.components.Belt;
                if (!contentsBeltComp) {
                    allBeltsMatch = false;
                    break;
                }

                // It's a belt
                if (
                    contentsBeltComp.direction !== _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirection"].top ||
                    _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumAngleToDirection"][contentsStaticComp.rotation] !== direction
                ) {
                    allBeltsMatch = false;
                    break;
                }
            }

            currentPos = tile.copy();
            if (allBeltsMatch) {
                // All belts between this are obsolete, so drop them
                for (let i = 0; i < matchingEntrance.range; ++i) {
                    currentPos.addInplace(offset);
                    const contents = this.root.map.getTileContent(currentPos, entity.layer);
                    window.assert(contents, "Invalid smart underground belt logic");
                    this.root.logic.tryDeleteBuilding(contents);
                }
            }

            // REMOVE OBSOLETE TUNNELS
            // Remove any double tunnels, by checking the tile plus the tile above
            currentPos = tile.copy().add(offset);
            for (let i = 0; i < matchingEntrance.range - 1; ++i) {
                const posBefore = currentPos.copy();
                currentPos.addInplace(offset);

                const entityBefore = this.root.map.getTileContent(posBefore, entity.layer);
                const entityAfter = this.root.map.getTileContent(currentPos, entity.layer);

                if (!entityBefore || !entityAfter) {
                    continue;
                }

                const undergroundBefore = entityBefore.components.UndergroundBelt;
                const undergroundAfter = entityAfter.components.UndergroundBelt;

                if (!undergroundBefore || !undergroundAfter) {
                    // Not an underground belt
                    continue;
                }

                if (
                    // Both same tier
                    undergroundBefore.tier !== undergroundAfter.tier ||
                    // And same tier as our original entity
                    undergroundBefore.tier !== undergroundComp.tier
                ) {
                    // Mismatching tier
                    continue;
                }

                if (
                    undergroundBefore.mode !== _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].sender ||
                    undergroundAfter.mode !== _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].receiver
                ) {
                    // Not the right mode
                    continue;
                }

                // Check rotations
                const staticBefore = entityBefore.components.StaticMapEntity;
                const staticAfter = entityAfter.components.StaticMapEntity;

                if (
                    _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumAngleToDirection"][staticBefore.rotation] !== direction ||
                    _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumAngleToDirection"][staticAfter.rotation] !== direction
                ) {
                    // Wrong rotation
                    continue;
                }

                // All good, can remove
                this.root.logic.tryDeleteBuilding(entityBefore);
                this.root.logic.tryDeleteBuilding(entityAfter);
            }
        }
    }

    /**
     * Recomputes the cache in the given area, invalidating all entries there
     * @param {Rectangle} area
     */
    recomputeArea(area) {
        for (let x = area.x; x < area.right(); ++x) {
            for (let y = area.y; y < area.bottom(); ++y) {
                const entities = this.root.map.getLayersContentsMultipleXY(x, y);
                for (let i = 0; i < entities.length; ++i) {
                    const entity = entities[i];
                    const undergroundComp = entity.components.UndergroundBelt;
                    if (!undergroundComp) {
                        continue;
                    }
                    undergroundComp.cachedLinkedEntity = null;
                }
            }
        }
    }

    update() {
        this.staleAreaWatcher.update();

        for (let i = 0; i < this.allEntities.length; ++i) {
            const entity = this.allEntities[i];
            const undergroundComp = entity.components.UndergroundBelt;
            if (undergroundComp.mode === _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].sender) {
                this.handleSender(entity);
            } else {
                this.handleReceiver(entity);
            }
        }
    }

    /**
     * Finds the receiver for a given sender
     * @param {Entity} entity
     * @returns {import("../components/underground_belt").LinkedUndergroundBelt}
     */
    findRecieverForSender(entity) {
        const staticComp = entity.components.StaticMapEntity;
        const undergroundComp = entity.components.UndergroundBelt;
        const searchDirection = staticComp.localDirectionToWorld(_core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirection"].top);
        const searchVector = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToVector"][searchDirection];
        const targetRotation = _core_vector__WEBPACK_IMPORTED_MODULE_6__["enumDirectionToAngle"][searchDirection];
        let currentTile = staticComp.origin;

        // Search in the direction of the tunnel
        for (
            let searchOffset = 0;
            searchOffset < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].undergroundBeltMaxTilesByTier[undergroundComp.tier];
            ++searchOffset
        ) {
            currentTile = currentTile.add(searchVector);

            const potentialReceiver = this.root.map.getTileContent(currentTile, "regular");
            if (!potentialReceiver) {
                // Empty tile
                continue;
            }
            const receiverUndergroundComp = potentialReceiver.components.UndergroundBelt;
            if (!receiverUndergroundComp || receiverUndergroundComp.tier !== undergroundComp.tier) {
                // Not a tunnel, or not on the same tier
                continue;
            }

            const receiverStaticComp = potentialReceiver.components.StaticMapEntity;
            if (receiverStaticComp.rotation !== targetRotation) {
                // Wrong rotation
                continue;
            }

            if (receiverUndergroundComp.mode !== _components_underground_belt__WEBPACK_IMPORTED_MODULE_7__["enumUndergroundBeltMode"].receiver) {
                // Not a receiver, but a sender -> Abort to make sure we don't deliver double
                break;
            }

            return { entity: potentialReceiver, distance: searchOffset };
        }

        // None found
        return { entity: null, distance: 0 };
    }

    /**
     *
     * @param {Entity} entity
     */
    handleSender(entity) {
        const undergroundComp = entity.components.UndergroundBelt;

        // Find the current receiver
        let cacheEntry = undergroundComp.cachedLinkedEntity;
        if (!cacheEntry) {
            // Need to recompute cache
            cacheEntry = undergroundComp.cachedLinkedEntity = this.findRecieverForSender(entity);
        }

        if (!cacheEntry.entity) {
            // If there is no connection to a receiver, ignore this one
            return;
        }

        // Check if we have any items to eject
        const nextItemAndDuration = undergroundComp.pendingItems[0];
        if (nextItemAndDuration) {
            window.assert(undergroundComp.pendingItems.length === 1, "more than 1 pending");

            // Check if the receiver can accept it
            if (
                cacheEntry.entity.components.UndergroundBelt.tryAcceptTunneledItem(
                    nextItemAndDuration[0],
                    cacheEntry.distance,
                    this.root.hubGoals.getUndergroundBeltBaseSpeed(),
                    this.root.time.now()
                )
            ) {
                // Drop this item
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_5__["fastArrayDelete"])(undergroundComp.pendingItems, 0);
            }
        }
    }

    /**
     *
     * @param {Entity} entity
     */
    handleReceiver(entity) {
        const undergroundComp = entity.components.UndergroundBelt;

        // Try to eject items, we only check the first one because it is sorted by remaining time
        const nextItemAndDuration = undergroundComp.pendingItems[0];
        if (nextItemAndDuration) {
            if (this.root.time.now() > nextItemAndDuration[1]) {
                const ejectorComp = entity.components.ItemEjector;

                const nextSlotIndex = ejectorComp.getFirstFreeSlot();
                if (nextSlotIndex !== null) {
                    if (ejectorComp.tryEject(nextSlotIndex, nextItemAndDuration[0])) {
                        undergroundComp.pendingItems.shift();
                    }
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/wire.js":
/*!*************************************!*\
  !*** ./src/js/game/systems/wire.js ***!
  \*************************************/
/*! exports provided: WireNetwork, WireSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WireNetwork", function() { return WireNetwork; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WireSystem", function() { return WireSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_rectangle__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/rectangle */ "./src/js/core/rectangle.js");
/* harmony import */ var _core_sprites__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/sprites */ "./src/js/core/sprites.js");
/* harmony import */ var _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/stale_area_detector */ "./src/js/core/stale_area_detector.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _base_item__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../base_item */ "./src/js/game/base_item.js");
/* harmony import */ var _buildings_wire__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../buildings/wire */ "./src/js/game/buildings/wire.js");
/* harmony import */ var _building_codes__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _components_wire__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../components/wire */ "./src/js/game/components/wire.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _components_wire_tunnel__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../components/wire_tunnel */ "./src/js/game/components/wire_tunnel.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _items_boolean_item__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../items/boolean_item */ "./src/js/game/items/boolean_item.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");




















const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_3__["createLogger"])("wires");

let networkUidCounter = 0;

const VERBOSE_WIRES =  false && false;

class WireNetwork {
    constructor() {
        /**
         * Who contributes to this network
         * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} */
        this.providers = [];

        /**
         * Who takes values from this network
         * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} */
        this.receivers = [];

        /**
         * All connected slots
         * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>}
         */
        this.allSlots = [];

        /**
         * All connected tunnels
         * @type {Array<Entity>}
         */
        this.tunnels = [];

        /**
         * Which wires are in this network
         * @type {Array<Entity>}
         */
        this.wires = [];

        /**
         * The current value of this network
         * @type {BaseItem}
         */
        this.currentValue = null;

        /**
         * Whether this network has a value conflict, that is, more than one
         * sender has sent a value
         * @type {boolean}
         */
        this.valueConflict = false;

        /**
         * Unique network identifier
         * @type {number}
         */
        this.uid = ++networkUidCounter;
    }

    /**
     * Returns whether this network currently has a value
     * @returns {boolean}
     */
    hasValue() {
        return !!this.currentValue && !this.valueConflict;
    }
}

class WireSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_16__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_wire__WEBPACK_IMPORTED_MODULE_12__["WireComponent"]]);

        /**
         * @type {Object<enumWireVariant, Object<enumWireType, AtlasSprite>>}
         */
        this.wireSprites = {};

        const variants = ["conflict", ...Object.keys(_components_wire__WEBPACK_IMPORTED_MODULE_12__["enumWireVariant"])];
        for (let i = 0; i < variants.length; ++i) {
            const wireVariant = variants[i];
            const sprites = {};
            for (const wireType in _components_wire__WEBPACK_IMPORTED_MODULE_12__["enumWireType"]) {
                sprites[wireType] = _core_loader__WEBPACK_IMPORTED_MODULE_2__["Loader"].getSprite(
                    "sprites/wires/sets/" + wireVariant + "_" + wireType + ".png"
                );
            }
            this.wireSprites[wireVariant] = sprites;
        }

        this.root.signals.entityDestroyed.add(this.queuePlacementUpdate, this);
        this.root.signals.entityAdded.add(this.queuePlacementUpdate, this);

        this.root.signals.entityDestroyed.add(this.queueRecomputeIfWire, this);
        this.root.signals.entityChanged.add(this.queueRecomputeIfWire, this);
        this.root.signals.entityAdded.add(this.queueRecomputeIfWire, this);

        this.needsRecompute = true;
        this.isFirstRecompute = true;

        this.staleArea = new _core_stale_area_detector__WEBPACK_IMPORTED_MODULE_6__["StaleAreaDetector"]({
            root: this.root,
            name: "wires",
            recomputeMethod: this.updateSurroundingWirePlacement.bind(this),
        });

        /**
         * @type {Array<WireNetwork>}
         */
        this.networks = [];
    }

    /**
     * Invalidates the wires network if the given entity is relevant for it
     * @param {Entity} entity
     */
    queueRecomputeIfWire(entity) {
        if (!this.root.gameInitialized) {
            return;
        }

        if (this.isEntityRelevantForWires(entity)) {
            this.needsRecompute = true;
            this.networks = [];
        }
    }

    /**
     * Recomputes the whole wires network
     */
    recomputeWiresNetwork() {
        this.needsRecompute = false;
        logger.log("Recomputing wires network");

        this.networks = [];

        const wireEntities = this.root.entityMgr.getAllWithComponent(_components_wire__WEBPACK_IMPORTED_MODULE_12__["WireComponent"]);
        const tunnelEntities = this.root.entityMgr.getAllWithComponent(_components_wire_tunnel__WEBPACK_IMPORTED_MODULE_14__["WireTunnelComponent"]);
        const pinEntities = this.root.entityMgr.getAllWithComponent(_components_wired_pins__WEBPACK_IMPORTED_MODULE_13__["WiredPinsComponent"]);

        // Clear all network references, but not on the first update since that's the deserializing one
        if (!this.isFirstRecompute) {
            for (let i = 0; i < wireEntities.length; ++i) {
                wireEntities[i].components.Wire.linkedNetwork = null;
            }
            for (let i = 0; i < tunnelEntities.length; ++i) {
                tunnelEntities[i].components.WireTunnel.linkedNetworks = [];
            }

            for (let i = 0; i < pinEntities.length; ++i) {
                const slots = pinEntities[i].components.WiredPins.slots;
                for (let k = 0; k < slots.length; ++k) {
                    slots[k].linkedNetwork = null;
                }
            }
        } else {
            logger.log("Recomputing wires first time");
            this.isFirstRecompute = false;
        }

        VERBOSE_WIRES && logger.log("Recomputing slots");

        // Iterate over all ejector slots
        for (let i = 0; i < pinEntities.length; ++i) {
            const entity = pinEntities[i];
            const slots = entity.components.WiredPins.slots;
            for (let k = 0; k < slots.length; ++k) {
                const slot = slots[k];

                // Ejectors are computed directly, acceptors are just set
                if (slot.type === _components_wired_pins__WEBPACK_IMPORTED_MODULE_13__["enumPinSlotType"].logicalEjector && !slot.linkedNetwork) {
                    this.findNetworkForEjector(entity, slot);
                }
            }
        }
    }

    /**
     * Finds the network for the given slot
     * @param {Entity} initialEntity
     * @param {import("../components/wired_pins").WirePinSlot} slot
     */
    findNetworkForEjector(initialEntity, slot) {
        let currentNetwork = new WireNetwork();
        VERBOSE_WIRES &&
            logger.log(
                "Finding network for entity",
                initialEntity.uid,
                initialEntity.components.StaticMapEntity.origin.toString(),
                "(nw-id:",
                currentNetwork.uid,
                ")"
            );
        const entitiesToVisit = [
            {
                entity: initialEntity,
                slot,
            },
        ];

        /**
         * Once we occur a wire, we store its variant so we don't connect to
         * mismatching ones
         * @type {enumWireVariant}
         */
        let variantMask = null;

        while (entitiesToVisit.length > 0) {
            const nextData = entitiesToVisit.pop();
            const nextEntity = nextData.entity;

            const wireComp = nextEntity.components.Wire;
            const staticComp = nextEntity.components.StaticMapEntity;

            VERBOSE_WIRES && logger.log("Visiting", staticComp.origin.toString(), "(", nextEntity.uid, ")");

            // Where to search for neighbours
            let newSearchDirections = [];
            let newSearchTile = null;

            //// WIRE
            if (wireComp) {
                // Sanity check
                window.assert(
                    !wireComp.linkedNetwork || wireComp.linkedNetwork === currentNetwork,
                    "Mismatching wire network on wire entity " +
                        (wireComp.linkedNetwork ? wireComp.linkedNetwork.uid : "<empty>") +
                        " vs " +
                        currentNetwork.uid +
                        " @ " +
                        staticComp.origin.toString()
                );

                if (!wireComp.linkedNetwork) {
                    if (variantMask && wireComp.variant !== variantMask) {
                        // Mismatching variant
                    } else {
                        // This one is new! :D
                        VERBOSE_WIRES && logger.log("  Visited new wire:", staticComp.origin.toString());
                        wireComp.linkedNetwork = currentNetwork;
                        currentNetwork.wires.push(nextEntity);

                        newSearchDirections = _core_vector__WEBPACK_IMPORTED_MODULE_8__["arrayAllDirections"];
                        newSearchTile = nextEntity.components.StaticMapEntity.origin;
                        variantMask = wireComp.variant;
                    }
                }
            }

            //// PINS
            const pinsComp = nextEntity.components.WiredPins;
            if (pinsComp) {
                const slot = nextData.slot;
                window.assert(slot, "No slot set for next entity");

                if (slot.type === _components_wired_pins__WEBPACK_IMPORTED_MODULE_13__["enumPinSlotType"].logicalEjector) {
                    VERBOSE_WIRES &&
                        logger.log("  Visiting ejector slot", staticComp.origin.toString(), "->", slot.type);
                } else if (slot.type === _components_wired_pins__WEBPACK_IMPORTED_MODULE_13__["enumPinSlotType"].logicalAcceptor) {
                    VERBOSE_WIRES &&
                        logger.log("  Visiting acceptor slot", staticComp.origin.toString(), "->", slot.type);
                } else {
                    window.assert(false, "Bad slot type: " + slot.type);
                }

                // Sanity check
                window.assert(
                    !slot.linkedNetwork || slot.linkedNetwork === currentNetwork,
                    "Mismatching wire network on pin slot entity " +
                        (slot.linkedNetwork ? slot.linkedNetwork.uid : "<empty>") +
                        " vs " +
                        currentNetwork.uid
                );
                if (!slot.linkedNetwork) {
                    // This one is new
                    VERBOSE_WIRES && logger.log("  Visited new slot:", staticComp.origin.toString());

                    // Add to the right list
                    if (slot.type === _components_wired_pins__WEBPACK_IMPORTED_MODULE_13__["enumPinSlotType"].logicalEjector) {
                        currentNetwork.providers.push({ entity: nextEntity, slot });
                    } else if (slot.type === _components_wired_pins__WEBPACK_IMPORTED_MODULE_13__["enumPinSlotType"].logicalAcceptor) {
                        currentNetwork.receivers.push({ entity: nextEntity, slot });
                    } else {
                        window.assert(false, "unknown slot type:" + slot.type);
                    }

                    // Register on the network
                    currentNetwork.allSlots.push({ entity: nextEntity, slot });
                    slot.linkedNetwork = currentNetwork;

                    // Specify where to search next
                    newSearchDirections = [staticComp.localDirectionToWorld(slot.direction)];
                    newSearchTile = staticComp.localTileToWorld(slot.pos);
                }
            }

            if (newSearchTile) {
                // Find new surrounding wire targets
                const newTargets = this.findSurroundingWireTargets(
                    newSearchTile,
                    newSearchDirections,
                    currentNetwork,
                    variantMask
                );

                VERBOSE_WIRES && logger.log("   Found", newTargets, "new targets to visit!");
                for (let i = 0; i < newTargets.length; ++i) {
                    entitiesToVisit.push(newTargets[i]);
                }
            }
        }

        if (
            currentNetwork.providers.length > 0 &&
            (currentNetwork.wires.length > 0 ||
                currentNetwork.receivers.length > 0 ||
                currentNetwork.tunnels.length > 0)
        ) {
            this.networks.push(currentNetwork);
            VERBOSE_WIRES && logger.log("Attached new network with uid", currentNetwork);
        } else {
            // Unregister network again
            for (let i = 0; i < currentNetwork.wires.length; ++i) {
                currentNetwork.wires[i].components.Wire.linkedNetwork = null;
            }

            for (let i = 0; i < currentNetwork.tunnels.length; ++i) {
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_7__["fastArrayDeleteValueIfContained"])(
                    currentNetwork.tunnels[i].components.WireTunnel.linkedNetworks,
                    currentNetwork
                );
            }

            for (let i = 0; i < currentNetwork.allSlots.length; ++i) {
                currentNetwork.allSlots[i].slot.linkedNetwork = null;
            }
        }
    }

    /**
     * Finds surrounding entities which are not yet assigned to a network
     * @param {Vector} initialTile
     * @param {Array<enumDirection>} directions
     * @param {WireNetwork} network
     * @param {enumWireVariant=} variantMask Only accept connections to this mask
     * @returns {Array<any>}
     */
    findSurroundingWireTargets(initialTile, directions, network, variantMask = null) {
        let result = [];

        VERBOSE_WIRES &&
            logger.log(
                "    Searching for new targets at",
                initialTile.toString(),
                "and d=",
                directions,
                "with mask=",
                variantMask
            );

        // Go over all directions we should search for
        for (let i = 0; i < directions.length; ++i) {
            const direction = directions[i];
            const offset = _core_vector__WEBPACK_IMPORTED_MODULE_8__["enumDirectionToVector"][direction];
            const initialSearchTile = initialTile.add(offset);

            // Store which tunnels we already visited to avoid infinite loops
            const visitedTunnels = new Set();

            // First, find the initial connected entities
            const initialContents = this.root.map.getLayersContentsMultipleXY(
                initialSearchTile.x,
                initialSearchTile.y
            );

            // Link the initial tile to the initial entities, since it may change
            /** @type {Array<{entity: Entity, tile: Vector}>} */
            const contents = [];
            for (let j = 0; j < initialContents.length; ++j) {
                contents.push({
                    entity: initialContents[j],
                    tile: initialSearchTile,
                });
            }

            for (let k = 0; k < contents.length; ++k) {
                const { entity, tile } = contents[k];
                const wireComp = entity.components.Wire;

                // Check for wire
                if (
                    wireComp &&
                    !wireComp.linkedNetwork &&
                    (!variantMask || wireComp.variant === variantMask)
                ) {
                    // Wires accept connections from everywhere
                    result.push({
                        entity,
                    });
                }

                // Check for connected slots
                const pinComp = entity.components.WiredPins;
                if (pinComp) {
                    const staticComp = entity.components.StaticMapEntity;

                    // Go over all slots and see if they are connected
                    const pinSlots = pinComp.slots;
                    for (let j = 0; j < pinSlots.length; ++j) {
                        const slot = pinSlots[j];

                        // Check if the position matches
                        const pinPos = staticComp.localTileToWorld(slot.pos);
                        if (!pinPos.equals(tile)) {
                            continue;
                        }

                        // Check if the direction (inverted) matches
                        const pinDirection = staticComp.localDirectionToWorld(slot.direction);
                        if (pinDirection !== _core_vector__WEBPACK_IMPORTED_MODULE_8__["enumInvertedDirections"][direction]) {
                            continue;
                        }

                        if (!slot.linkedNetwork) {
                            result.push({
                                entity,
                                slot,
                            });
                        }
                    }

                    // Pin slots mean it can be nothing else
                    continue;
                }

                // Check if it's a tunnel, if so, go to the forwarded item
                const tunnelComp = entity.components.WireTunnel;
                if (tunnelComp) {
                    if (visitedTunnels.has(entity.uid)) {
                        continue;
                    }

                    const staticComp = entity.components.StaticMapEntity;

                    // Compute where this tunnel connects to
                    const forwardedTile = staticComp.origin.add(offset);
                    VERBOSE_WIRES &&
                        logger.log(
                            "   Found tunnel",
                            entity.uid,
                            "at",
                            tile,
                            "-> forwarding to",
                            forwardedTile
                        );

                    // Figure out which entities are connected
                    const connectedContents = this.root.map.getLayersContentsMultipleXY(
                        forwardedTile.x,
                        forwardedTile.y
                    );

                    // Attach the entities and the tile we search at, because it may change
                    for (let h = 0; h < connectedContents.length; ++h) {
                        contents.push({
                            entity: connectedContents[h],
                            tile: forwardedTile,
                        });
                    }

                    // Add the tunnel to the network
                    if (tunnelComp.linkedNetworks.indexOf(network) < 0) {
                        tunnelComp.linkedNetworks.push(network);
                    }
                    if (network.tunnels.indexOf(entity) < 0) {
                        network.tunnels.push(entity);
                    }

                    // Remember this tunnel
                    visitedTunnels.add(entity.uid);
                }
            }
        }

        VERBOSE_WIRES && logger.log("     -> Found", result.length);

        return result;
    }

    /**
     * Updates the wires network
     */
    update() {
        this.staleArea.update();

        if (this.needsRecompute) {
            this.recomputeWiresNetwork();
        }

        // Re-compute values of all networks
        for (let i = 0; i < this.networks.length; ++i) {
            const network = this.networks[i];

            // Reset conflicts
            network.valueConflict = false;

            // Aggregate values of all senders
            const senders = network.providers;
            let value = null;
            for (let k = 0; k < senders.length; ++k) {
                const senderSlot = senders[k];
                const slotValue = senderSlot.slot.value;

                // The first sender can just put in his value
                if (!value) {
                    value = slotValue;
                    continue;
                }

                // If the slot is empty itself, just skip it
                if (!slotValue) {
                    continue;
                }

                // If there is already an value, compare if it matches ->
                // otherwise there is a conflict
                if (value.equals(slotValue)) {
                    // All good
                    continue;
                }

                // There is a conflict, this means the value will be null anyways
                network.valueConflict = true;
                break;
            }

            // Assign value
            if (network.valueConflict) {
                network.currentValue = null;
            } else {
                network.currentValue = value;
            }
        }
    }

    /**
     * Returns the given tileset and opacity
     * @param {WireComponent} wireComp
     * @returns {{ spriteSet: Object<enumWireType, import("../../core/draw_utils").AtlasSprite>, opacity: number}}
     */
    getSpriteSetAndOpacityForWire(wireComp) {
        if (!wireComp.linkedNetwork) {
            // There is no network, it's empty
            return {
                spriteSet: this.wireSprites[wireComp.variant],
                opacity: 0.5,
            };
        }

        const network = wireComp.linkedNetwork;
        if (network.valueConflict) {
            // There is a conflict
            return {
                spriteSet: this.wireSprites.conflict,
                opacity: 1,
            };
        }

        return {
            spriteSet: this.wireSprites[wireComp.variant],
            opacity: Object(_items_boolean_item__WEBPACK_IMPORTED_MODULE_17__["isTruthyItem"])(network.currentValue) ? 1 : 0.5,
        };
    }

    /**
     * Draws a given chunk
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.wireContents;
        for (let y = 0; y < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++y) {
            for (let x = 0; x < _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].mapChunkSize; ++x) {
                const entity = contents[x][y];
                if (entity && entity.components.Wire) {
                    const wireComp = entity.components.Wire;
                    const wireType = wireComp.type;

                    const { opacity, spriteSet } = this.getSpriteSetAndOpacityForWire(wireComp);

                    const sprite = spriteSet[wireType];

                    window.assert(sprite, "Unknown wire type: " + wireType);
                    const staticComp = entity.components.StaticMapEntity;
                    parameters.context.globalAlpha = opacity;
                    staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 0);

                    // DEBUG Rendering
                    if (false) {}
                }

                // DEBUG Rendering
                if (false) {}
            }
        }

        parameters.context.globalAlpha = 1;
    }

    /**
     * Returns whether this entity is relevant for the wires network
     * @param {Entity} entity
     */
    isEntityRelevantForWires(entity) {
        return entity.components.Wire || entity.components.WiredPins || entity.components.WireTunnel;
    }

    /**
     *
     * @param {Entity} entity
     */
    queuePlacementUpdate(entity) {
        if (!this.root.gameInitialized) {
            return;
        }

        if (!this.isEntityRelevantForWires(entity)) {
            return;
        }

        const staticComp = entity.components.StaticMapEntity;
        if (!staticComp) {
            return;
        }

        // Invalidate affected area
        const originalRect = staticComp.getTileSpaceBounds();
        const affectedArea = originalRect.expandedInAllDirections(1);
        this.staleArea.invalidate(affectedArea);
    }

    /**
     * Updates the wire placement after an entity has been added / deleted
     * @param {Rectangle} affectedArea
     */
    updateSurroundingWirePlacement(affectedArea) {
        const metaWire = _core_global_registries__WEBPACK_IMPORTED_MODULE_1__["gMetaBuildingRegistry"].findByClass(_buildings_wire__WEBPACK_IMPORTED_MODULE_10__["MetaWireBuilding"]);

        for (let x = affectedArea.x; x < affectedArea.right(); ++x) {
            for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) {
                const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y);
                for (let i = 0; i < targetEntities.length; ++i) {
                    const targetEntity = targetEntities[i];

                    const targetWireComp = targetEntity.components.Wire;
                    const targetStaticComp = targetEntity.components.StaticMapEntity;

                    if (!targetWireComp) {
                        // Not a wire
                        continue;
                    }

                    const variant = targetStaticComp.getVariant();

                    const {
                        rotation,
                        rotationVariant,
                    } = metaWire.computeOptimalDirectionAndRotationVariantAtTile({
                        root: this.root,
                        tile: new _core_vector__WEBPACK_IMPORTED_MODULE_8__["Vector"](x, y),
                        rotation: targetStaticComp.originalRotation,
                        variant,
                        layer: targetEntity.layer,
                    });

                    // Compute delta to see if anything changed
                    const newType = _buildings_wire__WEBPACK_IMPORTED_MODULE_10__["arrayWireRotationVariantToType"][rotationVariant];

                    if (targetStaticComp.rotation !== rotation || newType !== targetWireComp.type) {
                        // Change stuff
                        targetStaticComp.rotation = rotation;
                        metaWire.updateVariants(targetEntity, rotationVariant, variant);

                        // Update code as well
                        targetStaticComp.code = Object(_building_codes__WEBPACK_IMPORTED_MODULE_11__["getCodeFromBuildingData"])(metaWire, variant, rotationVariant);

                        // Make sure the chunks know about the update
                        this.root.signals.entityChanged.dispatch(targetEntity);
                    }
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/systems/wired_pins.js":
/*!*******************************************!*\
  !*** ./src/js/game/systems/wired_pins.js ***!
  \*******************************************/
/*! exports provided: WiredPinsSystem */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WiredPinsSystem", function() { return WiredPinsSystem; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_draw_parameters__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/draw_parameters */ "./src/js/core/draw_parameters.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/draw_utils */ "./src/js/core/draw_utils.js");
/* harmony import */ var _core_loader__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/loader */ "./src/js/core/loader.js");
/* harmony import */ var _core_signal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/signal */ "./src/js/core/signal.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _components_wired_pins__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../components/wired_pins */ "./src/js/game/components/wired_pins.js");
/* harmony import */ var _entity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_system_with_filter__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../game_system_with_filter */ "./src/js/game/game_system_with_filter.js");
/* harmony import */ var _map_chunk_view__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../map_chunk_view */ "./src/js/game/map_chunk_view.js");











/** @type {Object<ItemType, number>} */
const enumTypeToSize = {
    boolean: 9,
    shape: 9,
    color: 14,
};

class WiredPinsSystem extends _game_system_with_filter__WEBPACK_IMPORTED_MODULE_8__["GameSystemWithFilter"] {
    constructor(root) {
        super(root, [_components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["WiredPinsComponent"]]);

        this.pinSprites = {
            [_components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["enumPinSlotType"].logicalEjector]: _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/wires/logical_ejector.png"),
            [_components_wired_pins__WEBPACK_IMPORTED_MODULE_6__["enumPinSlotType"].logicalAcceptor]: _core_loader__WEBPACK_IMPORTED_MODULE_3__["Loader"].getSprite("sprites/wires/logical_acceptor.png"),
        };

        this.root.signals.prePlacementCheck.add(this.prePlacementCheck, this);
        this.root.signals.freeEntityAreaBeforeBuild.add(this.freeEntityAreaBeforeBuild, this);
    }

    /**
     * Performs pre-placement checks
     * @param {Entity} entity
     * @param {Vector} offset
     */
    prePlacementCheck(entity, offset) {
        // Compute area of the building
        const rect = entity.components.StaticMapEntity.getTileSpaceBounds();
        if (offset) {
            rect.x += offset.x;
            rect.y += offset.y;
        }

        // If this entity is placed on the wires layer, make sure we don't
        // place it above a pin
        if (entity.layer === "wires") {
            for (let x = rect.x; x < rect.x + rect.w; ++x) {
                for (let y = rect.y; y < rect.y + rect.h; ++y) {
                    // Find which entities are in same tiles of both layers
                    const entities = this.root.map.getLayersContentsMultipleXY(x, y);
                    for (let i = 0; i < entities.length; ++i) {
                        const otherEntity = entities[i];

                        // Check if entity has a wired component
                        const pinComponent = otherEntity.components.WiredPins;
                        const staticComp = otherEntity.components.StaticMapEntity;
                        if (!pinComponent) {
                            continue;
                        }

                        if (staticComp.getMetaBuilding().getIsReplaceable()) {
                            // Don't mind here, even if there would be a collision we
                            // could replace it
                            continue;
                        }

                        // Go over all pins and check if they are blocking
                        const pins = pinComponent.slots;
                        for (let pinSlot = 0; pinSlot < pins.length; ++pinSlot) {
                            const pos = staticComp.localTileToWorld(pins[pinSlot].pos);
                            // Occupied by a pin
                            if (pos.x === x && pos.y === y) {
                                return _core_signal__WEBPACK_IMPORTED_MODULE_4__["STOP_PROPAGATION"];
                            }
                        }
                    }
                }
            }
        }

        // Check for collisions on the wires layer
        if (this.checkEntityPinsCollide(entity, offset)) {
            return _core_signal__WEBPACK_IMPORTED_MODULE_4__["STOP_PROPAGATION"];
        }
    }

    /**
     * Checks if the pins of the given entity collide on the wires layer
     * @param {Entity} entity
     * @param {Vector=} offset Optional, move the entity by the given offset first
     * @returns {boolean} True if the pins collide
     */
    checkEntityPinsCollide(entity, offset) {
        const pinsComp = entity.components.WiredPins;
        if (!pinsComp) {
            return false;
        }

        // Go over all slots
        for (let slotIndex = 0; slotIndex < pinsComp.slots.length; ++slotIndex) {
            const slot = pinsComp.slots[slotIndex];

            // Figure out which tile this slot is on
            const worldPos = entity.components.StaticMapEntity.localTileToWorld(slot.pos);
            if (offset) {
                worldPos.x += offset.x;
                worldPos.y += offset.y;
            }

            // Check if there is any entity on that tile (Wired pins are always on the wires layer)
            const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, "wires");

            // If there's an entity, and it can't get removed -> That's a collision
            if (collidingEntity) {
                if (!collidingEntity.components.StaticMapEntity.getMetaBuilding().getIsReplaceable()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Called to free space for the given entity
     * @param {Entity} entity
     */
    freeEntityAreaBeforeBuild(entity) {
        const pinsComp = entity.components.WiredPins;
        if (!pinsComp) {
            // Entity has no pins
            return;
        }

        // Remove any stuff which collides with the pins
        for (let i = 0; i < pinsComp.slots.length; ++i) {
            const slot = pinsComp.slots[i];
            const worldPos = entity.components.StaticMapEntity.localTileToWorld(slot.pos);
            const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, "wires");
            if (collidingEntity) {
                window.assert(
                    collidingEntity.components.StaticMapEntity.getMetaBuilding().getIsReplaceable(),
                    "Tried to replace non-repleaceable entity for pins"
                );
                if (!this.root.logic.tryDeleteBuilding(collidingEntity)) {
                    window.assert(false, "Tried to replace non-repleaceable entity for pins #2");
                }
            }
        }
    }

    /**
     * Draws a given entity
     * @param {DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntities;

        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const pinsComp = entity.components.WiredPins;
            if (!pinsComp) {
                continue;
            }

            const staticComp = entity.components.StaticMapEntity;
            const slots = pinsComp.slots;

            for (let j = 0; j < slots.length; ++j) {
                const slot = slots[j];
                const tile = staticComp.localTileToWorld(slot.pos);

                if (!chunk.tileSpaceRectangle.containsPoint(tile.x, tile.y)) {
                    // Doesn't belong to this chunk
                    continue;
                }
                const worldPos = tile.toWorldSpaceCenterOfTile();

                // Culling
                if (
                    !parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].halfTileSize)
                ) {
                    continue;
                }

                const effectiveRotation = Math.radians(
                    staticComp.rotation + _core_vector__WEBPACK_IMPORTED_MODULE_5__["enumDirectionToAngle"][slot.direction]
                );

                if (staticComp.getMetaBuilding().getRenderPins()) {
                    Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_2__["drawRotatedSprite"])({
                        parameters,
                        sprite: this.pinSprites[slot.type],
                        x: worldPos.x,
                        y: worldPos.y,
                        angle: effectiveRotation,
                        size: _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].tileSize + 2,
                        offsetX: 0,
                        offsetY: 0,
                    });
                }

                // Draw contained item to visualize whats emitted
                const value = slot.value;
                if (value) {
                    const offset = new _core_vector__WEBPACK_IMPORTED_MODULE_5__["Vector"](0, -9.1).rotated(effectiveRotation);

                    value.drawItemCenteredClipped(
                        worldPos.x + offset.x,
                        worldPos.y + offset.y,
                        parameters,
                        enumTypeToSize[value.getItemType()]
                    );
                }

                // Debug view
                if (false) {}
            }
        }
    }
}


/***/ }),

/***/ "./src/js/game/theme.js":
/*!******************************!*\
  !*** ./src/js/game/theme.js ***!
  \******************************/
/*! exports provided: THEMES, THEME, applyGameTheme */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "THEMES", function() { return THEMES; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "THEME", function() { return THEME; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "applyGameTheme", function() { return applyGameTheme; });
const THEMES = {
    dark: __webpack_require__(/*! ./themes/dark.json */ "./src/js/game/themes/dark.json"),
    light: __webpack_require__(/*! ./themes/light.json */ "./src/js/game/themes/light.json"),
};

let THEME = THEMES.light;

function applyGameTheme(id) {
    THEME = THEMES[id];
}


/***/ }),

/***/ "./src/js/game/themes/dark.json":
/*!**************************************!*\
  !*** ./src/js/game/themes/dark.json ***!
  \**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4WAUABFEEQK4EsDKAXAngGwKYwFywBMBDAJwGsYAacaWAWyIAc8JRJboYAjIgYzIDmJAPZwAdgRYwAxAGYssgGYAWAOxUaHKDCEJJ+GCQE8AFACYArBcoRL121ZsAGAHROzASg3stOknoAZBDEsAHU9FAALFldrcE1fAGcsbF4UBGExAHkANywSDCI0KSNTVWUbAEYANlkbMzM6iFdVL2ofDhhk1PTMrLgUDGCcA1KiE3Kq2vrG5xcLNoTO7qw0jLEAIT5BEXF9WDGJiogapoam109vJdoYXkjxMg3hEgJ8xJLjccqzVSqATic9UczTcykWYBunAICBIq16YgCwn4LDYWl8cIEcEKJFRUPRnF4wgwL0+XCO9VkfxOsnB3gJBO42yEogkZPGk1sVKqtLmV3xHAAvu0GZwAO6wrAffBo0WdIkk3GjYwUrnUyq0iFyzo8fgsvbs1WNdW80H8joEwUC4XxC2E4kvDZDCQABQQ-HyABUENhDXZgfZ-aCFtc7do4YlRCReFK8WHlpEmCNYNILAQLIpqkR6XLDFh9jIABwWZSKCzVHOivxYLBiKTSarVVT-arKSsM7gYODJmQWLCqXhENvWkXou4PMRkXL5HIILBiuPamBYOiMdD15Sb4sV0dVxQ+7AFhvKapcVSSXcdrgpFAAYQdSpTgJbRFkMBHtsZEoji9zwjyBRFPeiqGv86rVJUVQWGYcyqCGl6MowcKzvOwGksq5JgVMkEnNBczwQKtyRAgAiREMpF3g+hpAs0Ab4V4H6QvGdyZCEaT5gAssMJDSqwhGcP++SFMUGHjMoNEWDR1Q0bEWq5igWAAB6USBBjSIoGntoyCnKWhJC3kwjD5vWWBlk4qhmFpY66jsrIFoc4k2JJNjSXMhYMfGVodDaTG3AgCl0Lxsq+KIgzDPWlSRcolSFlZ2ihc6YQRNE+AtHEzG8LCvDYFseq7GyonmDRsg0Y5oKyB5tBeV5QA"));
    })()

/***/ }),

/***/ "./src/js/game/themes/light.json":
/*!***************************************!*\
  !*** ./src/js/game/themes/light.json ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4WAUABFEEQK4EsDKAXAngGwKYwFywwQHMALFGAGnGlgFsBDABzwlEhuhgCN6BjAayIAnAPZwAdgBMWMAMQAzRZWocoMYQmn458+rt3L2q9UM0AZBOKwB1TShIsAjFTArjAZyzZeKBCPEA8gBuWEIY9GgyQkQ8ABQA7AAsFBCOAGwAzCkATNlZEAAMAHTxAJSGqmqe3r7+AXAohFZRMfQJyamZOXkpxQCs5S6VsNVYPn7iAEJ8gqISWrDRcUkp6fm5+cXZg+BuHDC8JBL8kyJCkqHuLXEFKRm3EH0PxQUZO65G+5IIQmO14mYRAIWGxhjQYL8iHBwkIQXswZxeCIMGdru0chl4qsMol3gjjDwBMIxFJrm0VhA8ljUjjekVtjB4ZUAL5DfEwADuPywV3woPx4KRKNh2iW6MpmOxuIqAs4hNmJIWENa4qpUrpDKZqmZWogrN2n0FyLOkyakgACggBKEACoIbBk2JPFLOx7PIrSly6iE8sRCXg8uGGyowdwkJg4bSyLD0LBcMYyhE+pUKeRceS8RyJsEmLBYcQyWS8dMZxLZ4bcDBwSOwWTpyR6eSM4NQfUfCuHY7BUJBBBYDlBgUwLC0RjoQv0NJ6Lhccsh+T27Ap3h9Xi8SRpOcErwoADCxpFtfix+bwzb3q5v15rF14JEITCEX3wsdAE5qelnKk+tk6fEBlu+yML8vb9s+qKijEsTvqsaRfo4P50p6t6cCQxAkIQpB7gejrxL+NLUtkfR9Ks5S6ueLawEi4hWD4WCSAAspYlyDuy96hOEkSQXEiQPK6aTugBbJJigWAAB7YS+UaKE2wk5qJEngUIu5MIw9GFlg8hPHhgHgvKxLzI6vEug8Al0gAHGRlE6p8FHgggom0Ne-LGGIjTMYWxF9P+9CATAblNDYdgOPgxT-nJgo-Lw2DTEScyktxbTGW6KRpCRhRFI4Vk0DZNlAA"));
    })()

/***/ }),

/***/ "./src/js/game/time/base_game_speed.js":
/*!*********************************************!*\
  !*** ./src/js/game/time/base_game_speed.js ***!
  \*********************************************/
/*! exports provided: BaseGameSpeed */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseGameSpeed", function() { return BaseGameSpeed; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");



class BaseGameSpeed extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["BasicSerializableObject"] {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        super();
        this.root = root;
        this.initializeAfterDeserialize(root);
    }

    /** @returns {string} */
    static getId() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "unknown-speed";
    }

    getId() {
        // @ts-ignore
        return this.constructor.getId();
    }

    static getSchema() {
        return {};
    }

    initializeAfterDeserialize(root) {
        this.root = root;
    }

    /**
     * Returns the time multiplier
     */
    getTimeMultiplier() {
        return 1;
    }

    /**
     * Returns how many logic steps there may be queued
     */
    getMaxLogicStepsInQueue() {
        return 3;
    }

    // Internals
    /** @returns {BaseGameSpeed} */
    newSpeed(instance) {
        return new instance(this.root);
    }
}


/***/ }),

/***/ "./src/js/game/time/game_time.js":
/*!***************************************!*\
  !*** ./src/js/game/time/game_time.js ***!
  \***************************************/
/*! exports provided: GameTime */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameTime", function() { return GameTime; });
/* harmony import */ var _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../savegame/serialization */ "./src/js/savegame/serialization.js");
/* harmony import */ var _regular_game_speed__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./regular_game_speed */ "./src/js/game/time/regular_game_speed.js");
/* harmony import */ var _base_game_speed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./base_game_speed */ "./src/js/game/time/base_game_speed.js");
/* harmony import */ var _paused_game_speed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./paused_game_speed */ "./src/js/game/time/paused_game_speed.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");










const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_6__["createLogger"])("game_time");

class GameTime extends _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["BasicSerializableObject"] {
    /**
     * @param {GameRoot} root
     */
    constructor(root) {
        super();
        this.root = root;

        // Current ingame time seconds, not incremented while paused
        this.timeSeconds = 0;

        // Current "realtime", a timer which always is incremented no matter whether the game is paused or no
        this.realtimeSeconds = 0;

        // The adjustment, used when loading savegames so we can continue where we were
        this.realtimeAdjust = 0;

        /** @type {BaseGameSpeed} */
        this.speed = new _regular_game_speed__WEBPACK_IMPORTED_MODULE_1__["RegularGameSpeed"](this.root);

        // Store how much time we have in bucket
        this.logicTimeBudget = 0;
    }

    static getId() {
        return "GameTime";
    }

    static getSchema() {
        return {
            timeSeconds: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].float,
            speed: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].obj(_core_global_registries__WEBPACK_IMPORTED_MODULE_4__["gGameSpeedRegistry"]),
            realtimeSeconds: _savegame_serialization__WEBPACK_IMPORTED_MODULE_0__["types"].float,
        };
    }

    /**
     * Fetches the new "real" time, called from the core once per frame, since performance now() is kinda slow
     */
    updateRealtimeNow() {
        this.realtimeSeconds = performance.now() / 1000.0 + this.realtimeAdjust;
    }

    /**
     * Returns the ingame time in milliseconds
     */
    getTimeMs() {
        return this.timeSeconds * 1000.0;
    }

    /**
     * Returns how many seconds we are in the grace period
     * @returns {number}
     */
    getRemainingGracePeriodSeconds() {
        return 0;
    }

    /**
     * Returns if we are currently in the grace period
     * @returns {boolean}
     */
    getIsWithinGracePeriod() {
        return this.getRemainingGracePeriodSeconds() > 0;
    }

    /**
     * Internal method to generate new logic time budget
     * @param {number} deltaMs
     */
    internalAddDeltaToBudget(deltaMs) {
        // Only update if game is supposed to update
        if (this.root.hud.shouldPauseGame()) {
            this.logicTimeBudget = 0;
        } else {
            const multiplier = this.getSpeed().getTimeMultiplier();
            this.logicTimeBudget += deltaMs * multiplier;
        }

        // Check for too big pile of updates -> reduce it to 1
        let maxLogicSteps = Math.max(
            3,
            (this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60
        );
        if (false) {}

        if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) {
            this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps;
        }
    }

    /**
     * Performs update ticks based on the queued logic budget
     * @param {number} deltaMs
     * @param {function():boolean} updateMethod
     */
    performTicks(deltaMs, updateMethod) {
        this.internalAddDeltaToBudget(deltaMs);

        const speedAtStart = this.root.time.getSpeed();

        let effectiveDelta = this.root.dynamicTickrate.deltaMs;
        if (false) {}

        // Update physics & logic
        while (this.logicTimeBudget >= effectiveDelta) {
            this.logicTimeBudget -= effectiveDelta;

            if (!updateMethod()) {
                // Gameover happened or so, do not update anymore
                return;
            }

            // Step game time
            this.timeSeconds += this.root.dynamicTickrate.deltaSeconds;

            // Game time speed changed, need to abort since our logic steps are no longer valid
            if (speedAtStart.getId() !== this.speed.getId()) {
                logger.warn(
                    "Skipping update because speed changed from",
                    speedAtStart.getId(),
                    "to",
                    this.speed.getId()
                );
                break;
            }
        }
    }

    /**
     * Returns ingame time in seconds
     * @returns {number} seconds
     */
    now() {
        return this.timeSeconds;
    }

    /**
     * Returns "real" time in seconds
     * @returns {number} seconds
     */
    realtimeNow() {
        return this.realtimeSeconds;
    }

    /**
     * Returns "real" time in seconds
     * @returns {number} seconds
     */
    systemNow() {
        return (this.realtimeSeconds - this.realtimeAdjust) * 1000.0;
    }

    getIsPaused() {
        return this.speed.getId() === _paused_game_speed__WEBPACK_IMPORTED_MODULE_3__["PausedGameSpeed"].getId();
    }

    getSpeed() {
        return this.speed;
    }

    setSpeed(speed) {
        window.assert(speed instanceof _base_game_speed__WEBPACK_IMPORTED_MODULE_2__["BaseGameSpeed"], "Not a valid game speed");
        if (this.speed.getId() === speed.getId()) {
            logger.warn("Same speed set than current one:", speed.constructor.getId());
        }
        this.speed = speed;
    }

    deserialize(data) {
        const errorCode = super.deserialize(data);
        if (errorCode) {
            return errorCode;
        }

        // Adjust realtime now difference so they match
        this.realtimeAdjust = this.realtimeSeconds - performance.now() / 1000.0;
        this.updateRealtimeNow();

        this.speed.initializeAfterDeserialize(this.root);
    }
}


/***/ }),

/***/ "./src/js/game/time/paused_game_speed.js":
/*!***********************************************!*\
  !*** ./src/js/game/time/paused_game_speed.js ***!
  \***********************************************/
/*! exports provided: PausedGameSpeed */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PausedGameSpeed", function() { return PausedGameSpeed; });
/* harmony import */ var _base_game_speed__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./base_game_speed */ "./src/js/game/time/base_game_speed.js");


class PausedGameSpeed extends _base_game_speed__WEBPACK_IMPORTED_MODULE_0__["BaseGameSpeed"] {
    static getId() {
        return "paused";
    }

    getTimeMultiplier() {
        return 0;
    }

    getMaxLogicStepsInQueue() {
        return 0;
    }
}


/***/ }),

/***/ "./src/js/game/time/regular_game_speed.js":
/*!************************************************!*\
  !*** ./src/js/game/time/regular_game_speed.js ***!
  \************************************************/
/*! exports provided: RegularGameSpeed */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RegularGameSpeed", function() { return RegularGameSpeed; });
/* harmony import */ var _base_game_speed__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./base_game_speed */ "./src/js/game/time/base_game_speed.js");


class RegularGameSpeed extends _base_game_speed__WEBPACK_IMPORTED_MODULE_0__["BaseGameSpeed"] {
    static getId() {
        return "regular";
    }

    getTimeMultiplier() {
        return 1;
    }
}


/***/ }),

/***/ "./src/js/game/tutorial_goals.js":
/*!***************************************!*\
  !*** ./src/js/game/tutorial_goals.js ***!
  \***************************************/
/*! exports provided: enumHubGoalRewards */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumHubGoalRewards", function() { return enumHubGoalRewards; });
/**
 * Don't forget to also update tutorial_goals_mappings.js as well as the translations!
 * @enum {string}
 */
const enumHubGoalRewards = {
    reward_cutter_and_trash: "reward_cutter_and_trash",
    reward_rotater: "reward_rotater",
    reward_painter: "reward_painter",
    reward_mixer: "reward_mixer",
    reward_stacker: "reward_stacker",
    reward_balancer: "reward_balancer",
    reward_tunnel: "reward_tunnel",

    reward_rotater_ccw: "reward_rotater_ccw",
    reward_rotater_180: "reward_rotater_180",
    reward_miner_chainable: "reward_miner_chainable",
    reward_underground_belt_tier_2: "reward_underground_belt_tier_2",
    reward_belt_reader: "reward_belt_reader",
    reward_splitter: "reward_splitter",
    reward_cutter_quad: "reward_cutter_quad",
    reward_painter_double: "reward_painter_double",
    reward_storage: "reward_storage",
    reward_merger: "reward_merger",
    reward_wires_painter_and_levers: "reward_wires_painter_and_levers",
    reward_display: "reward_display",
    reward_constant_signal: "reward_constant_signal",
    reward_logic_gates: "reward_logic_gates",
    reward_virtual_processing: "reward_virtual_processing",
    reward_filter: "reward_filter",

    reward_demo_end: "reward_demo_end",

    reward_blueprints: "reward_blueprints",
    reward_freeplay: "reward_freeplay",

    no_reward: "no_reward",
    no_reward_freeplay: "no_reward_freeplay",
};


/***/ }),

/***/ "./src/js/game/tutorial_goals_mappings.js":
/*!************************************************!*\
  !*** ./src/js/game/tutorial_goals_mappings.js ***!
  \************************************************/
/*! exports provided: enumHubGoalRewardsToContentUnlocked */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumHubGoalRewardsToContentUnlocked", function() { return enumHubGoalRewardsToContentUnlocked; });
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");
/* harmony import */ var _buildings_balancer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./buildings/balancer */ "./src/js/game/buildings/balancer.js");
/* harmony import */ var _buildings_constant_signal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./buildings/constant_signal */ "./src/js/game/buildings/constant_signal.js");
/* harmony import */ var _buildings_cutter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./buildings/cutter */ "./src/js/game/buildings/cutter.js");
/* harmony import */ var _buildings_display__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./buildings/display */ "./src/js/game/buildings/display.js");
/* harmony import */ var _buildings_filter__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./buildings/filter */ "./src/js/game/buildings/filter.js");
/* harmony import */ var _buildings_logic_gate__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./buildings/logic_gate */ "./src/js/game/buildings/logic_gate.js");
/* harmony import */ var _buildings_miner__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./buildings/miner */ "./src/js/game/buildings/miner.js");
/* harmony import */ var _buildings_mixer__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./buildings/mixer */ "./src/js/game/buildings/mixer.js");
/* harmony import */ var _buildings_painter__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./buildings/painter */ "./src/js/game/buildings/painter.js");
/* harmony import */ var _buildings_reader__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./buildings/reader */ "./src/js/game/buildings/reader.js");
/* harmony import */ var _buildings_rotater__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./buildings/rotater */ "./src/js/game/buildings/rotater.js");
/* harmony import */ var _buildings_stacker__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./buildings/stacker */ "./src/js/game/buildings/stacker.js");
/* harmony import */ var _buildings_storage__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./buildings/storage */ "./src/js/game/buildings/storage.js");
/* harmony import */ var _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./buildings/underground_belt */ "./src/js/game/buildings/underground_belt.js");
/* harmony import */ var _meta_building__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./meta_building */ "./src/js/game/meta_building.js");
/* harmony import */ var _tutorial_goals__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./tutorial_goals */ "./src/js/game/tutorial_goals.js");
















/** @typedef {Array<[typeof MetaBuilding, string]>} TutorialGoalReward */


/**
 * Helper method for proper types
 *  @returns {TutorialGoalReward}
 */
const typed = x => x;

/**
 * Stores which reward unlocks what
 * @enum {TutorialGoalReward?}
 */
const enumHubGoalRewardsToContentUnlocked = {
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_cutter_and_trash]: typed([[_buildings_cutter__WEBPACK_IMPORTED_MODULE_3__["MetaCutterBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_rotater]: typed([[_buildings_rotater__WEBPACK_IMPORTED_MODULE_11__["MetaRotaterBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_painter]: typed([[_buildings_painter__WEBPACK_IMPORTED_MODULE_9__["MetaPainterBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_mixer]: typed([[_buildings_mixer__WEBPACK_IMPORTED_MODULE_8__["MetaMixerBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_stacker]: typed([[_buildings_stacker__WEBPACK_IMPORTED_MODULE_12__["MetaStackerBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_balancer]: typed([[_buildings_balancer__WEBPACK_IMPORTED_MODULE_1__["MetaBalancerBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_tunnel]: typed([[_buildings_underground_belt__WEBPACK_IMPORTED_MODULE_14__["MetaUndergroundBeltBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),

    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_rotater_ccw]: typed([[_buildings_rotater__WEBPACK_IMPORTED_MODULE_11__["MetaRotaterBuilding"], _buildings_rotater__WEBPACK_IMPORTED_MODULE_11__["enumRotaterVariants"].ccw]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_rotater_180]: typed([[_buildings_rotater__WEBPACK_IMPORTED_MODULE_11__["MetaRotaterBuilding"], _buildings_rotater__WEBPACK_IMPORTED_MODULE_11__["enumRotaterVariants"].rotate180]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_miner_chainable]: typed([[_buildings_miner__WEBPACK_IMPORTED_MODULE_7__["MetaMinerBuilding"], _buildings_miner__WEBPACK_IMPORTED_MODULE_7__["enumMinerVariants"].chainable]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_underground_belt_tier_2]: typed([
        [_buildings_underground_belt__WEBPACK_IMPORTED_MODULE_14__["MetaUndergroundBeltBuilding"], _buildings_underground_belt__WEBPACK_IMPORTED_MODULE_14__["enumUndergroundBeltVariants"].tier2],
    ]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_splitter]: typed([[_buildings_balancer__WEBPACK_IMPORTED_MODULE_1__["MetaBalancerBuilding"], _buildings_balancer__WEBPACK_IMPORTED_MODULE_1__["enumBalancerVariants"].splitter]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_merger]: typed([[_buildings_balancer__WEBPACK_IMPORTED_MODULE_1__["MetaBalancerBuilding"], _buildings_balancer__WEBPACK_IMPORTED_MODULE_1__["enumBalancerVariants"].merger]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_cutter_quad]: typed([[_buildings_cutter__WEBPACK_IMPORTED_MODULE_3__["MetaCutterBuilding"], _buildings_cutter__WEBPACK_IMPORTED_MODULE_3__["enumCutterVariants"].quad]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_painter_double]: typed([[_buildings_painter__WEBPACK_IMPORTED_MODULE_9__["MetaPainterBuilding"], _buildings_painter__WEBPACK_IMPORTED_MODULE_9__["enumPainterVariants"].double]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_storage]: typed([[_buildings_storage__WEBPACK_IMPORTED_MODULE_13__["MetaStorageBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),

    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_belt_reader]: typed([[_buildings_reader__WEBPACK_IMPORTED_MODULE_10__["MetaReaderBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_display]: typed([[_buildings_display__WEBPACK_IMPORTED_MODULE_4__["MetaDisplayBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_constant_signal]: typed([
        [_buildings_constant_signal__WEBPACK_IMPORTED_MODULE_2__["MetaConstantSignalBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]],
    ]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_logic_gates]: typed([[_buildings_logic_gate__WEBPACK_IMPORTED_MODULE_6__["MetaLogicGateBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_filter]: typed([[_buildings_filter__WEBPACK_IMPORTED_MODULE_5__["MetaFilterBuilding"], _meta_building__WEBPACK_IMPORTED_MODULE_15__["defaultBuildingVariant"]]]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_virtual_processing]: null,

    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_wires_painter_and_levers]: typed([
        [_buildings_painter__WEBPACK_IMPORTED_MODULE_9__["MetaPainterBuilding"], _buildings_painter__WEBPACK_IMPORTED_MODULE_9__["enumPainterVariants"].quad],
    ]),
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_freeplay]: null,
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_blueprints]: null,
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].no_reward]: null,
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].no_reward_freeplay]: null,
    [_tutorial_goals__WEBPACK_IMPORTED_MODULE_16__["enumHubGoalRewards"].reward_demo_end]: null,
};

if (false) {}


/***/ }),

/***/ "./src/js/languages.js":
/*!*****************************!*\
  !*** ./src/js/languages.js ***!
  \*****************************/
/*! exports provided: LANGUAGES */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LANGUAGES", function() { return LANGUAGES; });
/**
 * @type {Object<string, {name: string, data: any, code: string, region: string}>}
 */
const LANGUAGES = {
    "en": {
        name: "English",
        data: null,
        code: "en",
        region: "",
    },
    "de": {
        name: "Deutsch",
        data: __webpack_require__(/*! ./built-temp/base-de.json */ "./src/js/built-temp/base-de.json"),
        code: "de",
        region: "",
    },
    "fr": {
        name: "Français",
        data: __webpack_require__(/*! ./built-temp/base-fr.json */ "./src/js/built-temp/base-fr.json"),
        code: "fr",
        region: "",
    },
    "ja": {
        name: "日本語",
        data: __webpack_require__(/*! ./built-temp/base-ja.json */ "./src/js/built-temp/base-ja.json"),
        code: "ja",
        region: "",
    },
    "pt-PT": {
        name: "Português (Portugal)",
        data: __webpack_require__(/*! ./built-temp/base-pt-PT.json */ "./src/js/built-temp/base-pt-PT.json"),
        code: "pt",
        region: "PT",
    },
    "pt-BR": {
        name: "Português (Brasil)",
        data: __webpack_require__(/*! ./built-temp/base-pt-BR.json */ "./src/js/built-temp/base-pt-BR.json"),
        code: "pt",
        region: "BR",
    },
    "ru": {
        name: "Русский",
        data: __webpack_require__(/*! ./built-temp/base-ru.json */ "./src/js/built-temp/base-ru.json"),
        code: "ru",
        region: "",
    },
    "cs": {
        name: "Čeština",
        data: __webpack_require__(/*! ./built-temp/base-cz.json */ "./src/js/built-temp/base-cz.json"),
        code: "cs",
        region: "",
    },
    "es-419": {
        name: "Español",
        data: __webpack_require__(/*! ./built-temp/base-es.json */ "./src/js/built-temp/base-es.json"),
        code: "es",
        region: "",
    },
    "pl": {
        name: "Polski",
        data: __webpack_require__(/*! ./built-temp/base-pl.json */ "./src/js/built-temp/base-pl.json"),
        code: "pl",
        region: "",
    },
    "kor": {
        name: "한국어",
        data: __webpack_require__(/*! ./built-temp/base-kor.json */ "./src/js/built-temp/base-kor.json"),
        code: "kor",
        region: "",
    },
    "nl": {
        name: "Nederlands",
        data: __webpack_require__(/*! ./built-temp/base-nl.json */ "./src/js/built-temp/base-nl.json"),
        code: "nl",
        region: "",
    },
    "no": {
        name: "Norsk",
        data: __webpack_require__(/*! ./built-temp/base-no.json */ "./src/js/built-temp/base-no.json"),
        code: "no",
        region: "",
    },

    "tr": {
        name: "Türkçe",
        data: __webpack_require__(/*! ./built-temp/base-tr.json */ "./src/js/built-temp/base-tr.json"),
        code: "tr",
        region: "",
    },

    "zh-CN": {
        // simplified
        name: "中文简体",
        data: __webpack_require__(/*! ./built-temp/base-zh-CN.json */ "./src/js/built-temp/base-zh-CN.json"),
        code: "zh",
        region: "CN",
    },

    "zh-TW": {
        // traditional
        name: "中文繁體",
        data: __webpack_require__(/*! ./built-temp/base-zh-TW.json */ "./src/js/built-temp/base-zh-TW.json"),
        code: "zh",
        region: "TW",
    },

    "sv": {
        name: "Svenska",
        data: __webpack_require__(/*! ./built-temp/base-sv.json */ "./src/js/built-temp/base-sv.json"),
        code: "sv",
        region: "",
    },

    "da": {
        name: "Dansk",
        data: __webpack_require__(/*! ./built-temp/base-da.json */ "./src/js/built-temp/base-da.json"),
        code: "da",
        region: "",
    },

    "hu": {
        name: "Magyar",
        data: __webpack_require__(/*! ./built-temp/base-hu.json */ "./src/js/built-temp/base-hu.json"),
        code: "hu",
        region: "",
    },
};


/***/ }),

/***/ "./src/js/main.js":
/*!************************!*\
  !*** ./src/js/main.js ***!
  \************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _core_polyfills__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./core/polyfills */ "./src/js/core/polyfills.js");
/* harmony import */ var _core_assert__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./core/assert */ "./src/js/core/assert.js");
/* harmony import */ var _core_error_handler__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./core/error_handler */ "./src/js/core/error_handler.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _application__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./application */ "./src/js/application.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./core/config */ "./src/js/core/config.js");
/* harmony import */ var _game_component_registry__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./game/component_registry */ "./src/js/game/component_registry.js");
/* harmony import */ var _core_draw_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./core/draw_utils */ "./src/js/core/draw_utils.js");
/* harmony import */ var _game_item_registry__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./game/item_registry */ "./src/js/game/item_registry.js");
/* harmony import */ var _game_meta_building_registry__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./game/meta_building_registry */ "./src/js/game/meta_building_registry.js");
/* harmony import */ var _game_game_speed_registry__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./game/game_speed_registry */ "./src/js/game/game_speed_registry.js");













const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_3__["createLogger"])("main");

if (window.coreThreadLoadedCb) {
    logger.log("Javascript parsed, calling html thread");
    window.coreThreadLoadedCb();
}

// Logrocket
// if (!G_IS_DEV && !G_IS_STANDALONE) {
//     const monthlyUsers = 300; // thousand
//     const logrocketLimit = 10; // thousand
//     const percentageOfUsers = logrocketLimit / monthlyUsers;

//     if (Math.random() <= percentageOfUsers) {
//         logger.log("Analyzing this session with logrocket");
//         const logrocket = require("logrocket");
//         logrocket.init("p1x9zh/shapezio");

//         try {
//             logrocket.getSessionURL(function (sessionURL) {
//                 logger.log("Connected lockrocket to GA");
//                 // @ts-ignore
//                 try {
//                     window.ga("send", {
//                         hitType: "event",
//                         eventCategory: "LogRocket",
//                         eventAction: sessionURL,
//                     });
//                 } catch (ex) {
//                     logger.warn("Logrocket connection to analytics failed:", ex);
//                 }
//             });
//         } catch (ex) {
//             logger.warn("Logrocket connection to analytics failed:", ex);
//         }
//     }
// }

console.log(
    `%cshapez.io ️%c\n© 2020 Tobias Springer IT Solutions\nCommit %c${"b5a96a91"}%c on %c${new Date(
        1612259262750
    ).toLocaleString()}\n`,
    "font-size: 35px; font-family: Arial;font-weight: bold; padding: 10px 0;",
    "color: #aaa",
    "color: #7f7",
    "color: #aaa",
    "color: #7f7"
);

console.log("Environment: %c" + "dev", "color: #fff");

if (false) {}


/* dev:start */
console.log("%cDEVCODE BUILT IN", "color: #f77");
/* dev:end */

Object(_core_logging__WEBPACK_IMPORTED_MODULE_3__["logSection"])("Boot Process", "#f9a825");

Object(_core_draw_utils__WEBPACK_IMPORTED_MODULE_7__["initDrawUtils"])();
Object(_game_component_registry__WEBPACK_IMPORTED_MODULE_6__["initComponentRegistry"])();
Object(_game_item_registry__WEBPACK_IMPORTED_MODULE_8__["initItemRegistry"])();
Object(_game_meta_building_registry__WEBPACK_IMPORTED_MODULE_9__["initMetaBuildingRegistry"])();
Object(_game_game_speed_registry__WEBPACK_IMPORTED_MODULE_10__["initGameSpeedRegistry"])();

let app = null;

function bootApp() {
    logger.log("Page Loaded");
    app = new _application__WEBPACK_IMPORTED_MODULE_4__["Application"]();
    app.boot();
}

window.addEventListener("load", bootApp);


/***/ }),

/***/ "./src/js/platform/ad_provider.js":
/*!****************************************!*\
  !*** ./src/js/platform/ad_provider.js ***!
  \****************************************/
/*! exports provided: AdProviderInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdProviderInterface", function() { return AdProviderInterface; });

class AdProviderInterface {
    /** @param {Application} app */
    constructor(app) {
        this.app = app;
    }

    /**
     * Initializes the storage
     * @returns {Promise<void>}
     */
    initialize() {
        return Promise.resolve();
    }

    /**
     * Returns if this provider serves ads at all
     * @returns {boolean}
     */
    getHasAds() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }

    /**
     * Returns if it would be possible to show a video ad *now*. This can be false if for
     * example the last video ad is
     * @returns {boolean}
     */
    getCanShowVideoAd() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }

    /**
     * Shows an video ad
     * @returns {Promise<void>}
     */
    showVideoAd() {
        return Promise.resolve();
    }
}


/***/ }),

/***/ "./src/js/platform/ad_providers/gamedistribution.js":
/*!**********************************************************!*\
  !*** ./src/js/platform/ad_providers/gamedistribution.js ***!
  \**********************************************************/
/*! exports provided: GamedistributionAdProvider */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GamedistributionAdProvider", function() { return GamedistributionAdProvider; });
/* harmony import */ var _ad_provider__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../ad_provider */ "./src/js/platform/ad_provider.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");




const minimumTimeBetweenVideoAdsMs =  false ? undefined : 5 * 60 * 1000;

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("gamedistribution");

class GamedistributionAdProvider extends _ad_provider__WEBPACK_IMPORTED_MODULE_0__["AdProviderInterface"] {
    /**
     *
     * @param {Application} app
     */
    constructor(app) {
        super(app);

        /**
         * The resolve function to finish the current video ad. Null if none is currently running
         * @type {Function}
         */
        this.videoAdResolveFunction = null;

        /**
         * The current timer which will timeout the resolve
         */
        this.videoAdResolveTimer = null;

        /**
         * When we showed the last video ad
         */
        this.lastVideoAdShowTime = -1e20;

        console.error("X");
    }

    getHasAds() {
        return true;
    }

    getCanShowVideoAd() {
        return (
            this.getHasAds() &&
            !this.videoAdResolveFunction &&
            performance.now() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs
        );
    }

    initialize() {
        // No point to initialize everything if ads are not supported
        if (!this.getHasAds()) {
            return Promise.resolve();
        }

        logger.log("🎬 Initializing gamedistribution ads");

        try {
            parent.postMessage("shapezio://gd.game_loaded", "*");
        } catch (ex) {
            return Promise.reject("Frame communication not allowed");
        }

        window.addEventListener(
            "message",
            event => {
                if (event.data === "shapezio://gd.ad_started") {
                    console.log("🎬 Got ad started callback");
                } else if (event.data === "shapezio://gd.ad_finished") {
                    console.log("🎬 Got ad finished callback");
                    if (this.videoAdResolveFunction) {
                        this.videoAdResolveFunction();
                    }
                }
            },
            false
        );

        return Promise.resolve();
    }

    showVideoAd() {
        window.assert(this.getHasAds(), "Called showVideoAd but ads are not supported!");
        window.assert(!this.videoAdResolveFunction, "Video ad still running, can not show again!");
        this.lastVideoAdShowTime = performance.now();

        console.log("🎬 Gamedistribution: Start ad");
        try {
            parent.postMessage("shapezio://gd.show_ad", "*");
        } catch (ex) {
            logger.warn("🎬 Failed to send message for gd ad:", ex);
            return Promise.resolve();
        }

        document.body.classList.add("externalAdOpen");

        return new Promise(resolve => {
            // So, wait for the remove call but also remove after N seconds
            this.videoAdResolveFunction = () => {
                this.videoAdResolveFunction = null;
                clearTimeout(this.videoAdResolveTimer);
                this.videoAdResolveTimer = null;

                // When the ad closed, also set the time
                this.lastVideoAdShowTime = performance.now();
                resolve();
            };

            this.videoAdResolveTimer = setTimeout(() => {
                logger.warn("Automatically closing ad after not receiving callback");
                if (this.videoAdResolveFunction) {
                    this.videoAdResolveFunction();
                }
            }, 35000);
        })
            .catch(err => {
                logger.error(this, "Error while resolving video ad:", err);
            })
            .then(() => {
                document.body.classList.remove("externalAdOpen");
            });
    }
}


/***/ }),

/***/ "./src/js/platform/ad_providers/no_ad_provider.js":
/*!********************************************************!*\
  !*** ./src/js/platform/ad_providers/no_ad_provider.js ***!
  \********************************************************/
/*! exports provided: NoAdProvider */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NoAdProvider", function() { return NoAdProvider; });
/* harmony import */ var _ad_provider__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../ad_provider */ "./src/js/platform/ad_provider.js");


class NoAdProvider extends _ad_provider__WEBPACK_IMPORTED_MODULE_0__["AdProviderInterface"] {
    getHasAds() {
        return false;
    }

    getCanShowVideoAd() {
        return false;
    }
}


/***/ }),

/***/ "./src/js/platform/analytics.js":
/*!**************************************!*\
  !*** ./src/js/platform/analytics.js ***!
  \**************************************/
/*! exports provided: AnalyticsInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AnalyticsInterface", function() { return AnalyticsInterface; });

class AnalyticsInterface {
    constructor(app) {
        /** @type {Application} */
        this.app = app;
    }

    /**
     * Initializes the analytics
     * @returns {Promise<void>}
     */
    initialize() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.reject();
    }

    /**
     * Sets the player name for analytics
     * @param {string} userName
     */
    setUserContext(userName) {}

    /**
     * Tracks a click no an ui element
     * @param {string} elementName
     */
    trackUiClick(elementName) {}

    /**
     * Tracks when a new state is entered
     * @param {string} stateId
     */
    trackStateEnter(stateId) {}

    /**
     * Tracks a new user decision
     * @param {string} name
     */
    trackDecision(name) {}
}


/***/ }),

/***/ "./src/js/platform/browser/game_analytics.js":
/*!***************************************************!*\
  !*** ./src/js/platform/browser/game_analytics.js ***!
  \***************************************************/
/*! exports provided: ShapezGameAnalytics */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ShapezGameAnalytics", function() { return ShapezGameAnalytics; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_query_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/query_parameters */ "./src/js/core/query_parameters.js");
/* harmony import */ var _game_components_belt__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../game/components/belt */ "./src/js/game/components/belt.js");
/* harmony import */ var _game_components_static_map_entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../game/components/static_map_entity */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _game_root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../game/root */ "./src/js/game/root.js");
/* harmony import */ var _states_ingame__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../states/ingame */ "./src/js/states/ingame.js");
/* harmony import */ var _game_analytics__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../game_analytics */ "./src/js/platform/game_analytics.js");
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../storage */ "./src/js/platform/storage.js");










const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("game_analytics");

const analyticsUrl = "";

// Be sure to increment the ID whenever it changes to make sure all
// users are tracked
const analyticsLocalFile = "shapez_token_123.bin";

class ShapezGameAnalytics extends _game_analytics__WEBPACK_IMPORTED_MODULE_7__["GameAnalyticsInterface"] {
    get environment() {
        if (false) {}

        if (false) {}

        if (true) {
            return "prod";
        }

        if (window.location.host.indexOf("alpha") >= 0) {
            if (_core_query_parameters__WEBPACK_IMPORTED_MODULE_2__["queryParamOptions"].sandboxMode) {
                return "alpha-sandbox";
            }
            return "alpha";
        } else {
            if (_core_query_parameters__WEBPACK_IMPORTED_MODULE_2__["queryParamOptions"].sandboxMode) {
                return "beta-sandbox";
            }
            return "beta";
        }
    }

    /**
     * @returns {Promise<void>}
     */
    initialize() {
        this.syncKey = null;

        setInterval(() => this.sendTimePoints(), 60 * 1000);

        // Retrieve sync key from player
        return this.app.storage.readFileAsync(analyticsLocalFile).then(
            syncKey => {
                this.syncKey = syncKey;
                logger.log("Player sync key read:", this.syncKey);
            },
            error => {
                // File was not found, retrieve new key
                if (error === _storage__WEBPACK_IMPORTED_MODULE_8__["FILE_NOT_FOUND"]) {
                    logger.log("Retrieving new player key");

                    // Perform call to get a new key from the API
                    this.sendToApi("/v1/register", {
                        environment: this.environment,
                    })
                        .then(res => {
                            // Try to read and parse the key from the api
                            if (res.key && typeof res.key === "string" && res.key.length === 40) {
                                this.syncKey = res.key;
                                logger.log("Key retrieved:", this.syncKey);
                                this.app.storage.writeFileAsync(analyticsLocalFile, res.key);
                            } else {
                                throw new Error("Bad response from analytics server: " + res);
                            }
                        })
                        .catch(err => {
                            logger.error("Failed to register on analytics api:", err);
                        });
                } else {
                    logger.error("Failed to read ga key:", error);
                }
                return;
            }
        );
    }

    /**
     * Sends a request to the api
     * @param {string} endpoint Endpoint without base url
     * @param {object} data payload
     * @returns {Promise<any>}
     */
    sendToApi(endpoint, data) {
        return new Promise((resolve, reject) => {
            const timeout = setTimeout(() => reject("Request to " + endpoint + " timed out"), 20000);

            fetch(analyticsUrl + endpoint, {
                method: "POST",
                mode: "cors",
                cache: "no-cache",
                referrer: "no-referrer",
                credentials: "omit",
                headers: {
                    "Content-Type": "application/json",
                    "Accept": "application/json",
                    "x-api-key": _core_config__WEBPACK_IMPORTED_MODULE_0__["globalConfig"].info.analyticsApiKey,
                },
                body: JSON.stringify(data),
            })
                .then(res => {
                    clearTimeout(timeout);
                    if (!res.ok || res.status !== 200) {
                        reject("Fetch error: Bad status " + res.status);
                    } else {
                        return res.json();
                    }
                })
                .then(resolve)
                .catch(reason => {
                    clearTimeout(timeout);
                    reject(reason);
                });
        });
    }

    /**
     * Sends a game event to the analytics
     * @param {string} category
     * @param {string} value
     */
    sendGameEvent(category, value) {
        if (!this.syncKey) {
            logger.warn("Can not send event due to missing sync key");
            return;
        }

        const gameState = this.app.stateMgr.currentState;
        if (!(gameState instanceof _states_ingame__WEBPACK_IMPORTED_MODULE_6__["InGameState"])) {
            logger.warn("Trying to send analytics event outside of ingame state");
            return;
        }

        const savegame = gameState.savegame;
        if (!savegame) {
            logger.warn("Ingame state has empty savegame");
            return;
        }

        const savegameId = savegame.internalId;
        if (!gameState.core) {
            logger.warn("Game state has no core");
            return;
        }
        const root = gameState.core.root;
        if (!root) {
            logger.warn("Root is not initialized");
            return;
        }

        logger.log("Sending event", category, value);

        this.sendToApi("/v1/game-event", {
            playerKey: this.syncKey,
            gameKey: savegameId,
            ingameTime: root.time.now(),
            environment: this.environment,
            category,
            value,
            version: "1.2.2",
            level: root.hubGoals.level,
            gameDump: this.generateGameDump(root),
        });
    }

    sendTimePoints() {
        const gameState = this.app.stateMgr.currentState;
        if (gameState instanceof _states_ingame__WEBPACK_IMPORTED_MODULE_6__["InGameState"]) {
            logger.log("Syncing analytics");
            this.sendGameEvent("sync", "");
        }
    }

    /**
     * Returns true if the shape is interesting
     * @param {GameRoot} root
     * @param {string} key
     */
    isInterestingShape(root, key) {
        if (key === root.gameMode.getBlueprintShapeKey()) {
            return true;
        }

        // Check if its a story goal
        const levels = root.gameMode.getLevelDefinitions();
        for (let i = 0; i < levels.length; ++i) {
            if (key === levels[i].shape) {
                return true;
            }
        }

        // Check if its required to unlock an upgrade
        const upgrades = root.gameMode.getUpgrades();
        for (const upgradeKey in upgrades) {
            const upgradeTiers = upgrades[upgradeKey];
            for (let i = 0; i < upgradeTiers.length; ++i) {
                const tier = upgradeTiers[i];
                const required = tier.required;
                for (let k = 0; k < required.length; ++k) {
                    if (required[k].shape === key) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Generates a game dump
     * @param {GameRoot} root
     */
    generateGameDump(root) {
        const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(key =>
            this.isInterestingShape(root, key)
        );
        let shapes = {};
        for (let i = 0; i < shapeIds.length; ++i) {
            shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]];
        }
        return {
            shapes,
            upgrades: root.hubGoals.upgradeLevels,
            belts: root.entityMgr.getAllWithComponent(_game_components_belt__WEBPACK_IMPORTED_MODULE_3__["BeltComponent"]).length,
            buildings:
                root.entityMgr.getAllWithComponent(_game_components_static_map_entity__WEBPACK_IMPORTED_MODULE_4__["StaticMapEntityComponent"]).length -
                root.entityMgr.getAllWithComponent(_game_components_belt__WEBPACK_IMPORTED_MODULE_3__["BeltComponent"]).length,
        };
    }

    /**
     */
    handleGameStarted() {
        this.sendGameEvent("game_start", "");
    }

    /**
     */
    handleGameResumed() {
        this.sendTimePoints();
    }

    /**
     * Handles the given level completed
     * @param {number} level
     */
    handleLevelCompleted(level) {
        logger.log("Complete level", level);
        this.sendGameEvent("level_complete", "" + level);
    }

    /**
     * Handles the given upgrade completed
     * @param {string} id
     * @param {number} level
     */
    handleUpgradeUnlocked(id, level) {
        logger.log("Unlock upgrade", id, level);
        this.sendGameEvent("upgrade_unlock", id + "@" + level);
    }
}


/***/ }),

/***/ "./src/js/platform/browser/google_analytics.js":
/*!*****************************************************!*\
  !*** ./src/js/platform/browser/google_analytics.js ***!
  \*****************************************************/
/*! exports provided: GoogleAnalyticsImpl */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GoogleAnalyticsImpl", function() { return GoogleAnalyticsImpl; });
/* harmony import */ var _analytics__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../analytics */ "./src/js/platform/analytics.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");



const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("ga");

class GoogleAnalyticsImpl extends _analytics__WEBPACK_IMPORTED_MODULE_0__["AnalyticsInterface"] {
    initialize() {
        this.lastUiClickTracked = -1000;

        setInterval(() => this.internalTrackAfkEvent(), 120 * 1000);

        // Analytics is already loaded in the html
        return Promise.resolve();
    }

    setUserContext(userName) {
        try {
            if (window.gtag) {
                logger.log("📊 Setting user context:", userName);
                window.gtag("set", {
                    player: userName,
                });
            }
        } catch (ex) {
            logger.warn("📊 Failed to set user context:", ex);
        }
    }

    trackStateEnter(stateId) {
        const nonInteractionStates = [
            "LoginState",
            "MainMenuState",
            "PreloadState",
            "RegisterState",
            "WatchAdState",
        ];

        try {
            if (window.gtag) {
                logger.log("📊 Tracking state enter:", stateId);
                window.gtag("event", "enter_state", {
                    event_category: "ui",
                    event_label: stateId,
                    non_interaction: nonInteractionStates.indexOf(stateId) >= 0,
                });
            }
        } catch (ex) {
            logger.warn("📊 Failed to track state analytcis:", ex);
        }
    }

    trackDecision(decisionName) {
        try {
            if (window.gtag) {
                logger.log("📊 Tracking decision:", decisionName);
                window.gtag("event", "decision", {
                    event_category: "ui",
                    event_label: decisionName,
                    non_interaction: true,
                });
            }
        } catch (ex) {
            logger.warn("📊 Failed to track state analytcis:", ex);
        }
    }

    trackUiClick(elementName) {
        const stateKey = this.app.stateMgr.getCurrentState().key;
        const fullSelector = stateKey + ">" + elementName;

        try {
            if (window.gtag) {
                logger.log("📊 Tracking click on:", fullSelector);
                window.gtag("event", "click", {
                    event_category: "ui",
                    event_label: fullSelector,
                });
            }
        } catch (ex) {
            logger.warn("📊 Failed to track ui click:", ex);
        }
    }

    /**
     * Tracks an event so GA keeps track of the user
     */
    internalTrackAfkEvent() {
        if (window.gtag) {
            window.gtag("event", "afk", {
                event_category: "ping",
                event_label: "timed",
            });
        }
    }
}


/***/ }),

/***/ "./src/js/platform/browser/sound.js":
/*!******************************************!*\
  !*** ./src/js/platform/browser/sound.js ***!
  \******************************************/
/*! exports provided: SoundImplBrowser */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SoundImplBrowser", function() { return SoundImplBrowser; });
/* harmony import */ var _sound__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../sound */ "./src/js/platform/sound.js");
/* harmony import */ var _core_cachebust__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/cachebust */ "./src/js/core/cachebust.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");





const { Howl, Howler } = __webpack_require__(/*! howler */ "./node_modules/howler/dist/howler.js");

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("sound/browser");

// @ts-ignore
const sprites = __webpack_require__(/*! ../../built-temp/sfx.json */ "./src/js/built-temp/sfx.json");

class SoundSpritesContainer {
    constructor() {
        this.howl = null;

        this.loadingPromise = null;
    }

    load() {
        if (this.loadingPromise) {
            return this.loadingPromise;
        }
        return (this.loadingPromise = Promise.race([
            new Promise((resolve, reject) => {
                setTimeout(reject,  false ? undefined : 5000);
            }),
            new Promise(resolve => {
                this.howl = new Howl({
                    src: Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_1__["cachebust"])("res/sounds/sfx.mp3"),
                    sprite: sprites.sprite,
                    autoplay: false,
                    loop: false,
                    volume: 0,
                    preload: true,
                    pool: 20,
                    onload: () => {
                        resolve();
                    },
                    onloaderror: (id, err) => {
                        logger.warn("SFX failed to load:", id, err);
                        this.howl = null;
                        resolve();
                    },
                    onplayerror: (id, err) => {
                        logger.warn("SFX failed to play:", id, err);
                    },
                });
            }),
        ]));
    }

    play(volume, key) {
        if (this.howl) {
            const instance = this.howl.play(key);
            this.howl.volume(volume, instance);
        }
    }

    deinitialize() {
        if (this.howl) {
            this.howl.unload();
            this.howl = null;
        }
    }
}

class WrappedSoundInstance extends _sound__WEBPACK_IMPORTED_MODULE_0__["SoundInstanceInterface"] {
    /**
     *
     * @param {SoundSpritesContainer} spriteContainer
     * @param {string} key
     */
    constructor(spriteContainer, key) {
        super(key, "sfx.mp3");
        this.spriteContainer = spriteContainer;
    }

    /** @returns {Promise<void>} */
    load() {
        return this.spriteContainer.load();
    }

    play(volume) {
        this.spriteContainer.play(volume, this.key);
    }

    deinitialize() {
        return this.spriteContainer.deinitialize();
    }
}

class MusicInstance extends _sound__WEBPACK_IMPORTED_MODULE_0__["MusicInstanceInterface"] {
    constructor(key, url) {
        super(key, url);
        this.howl = null;
        this.instance = null;
        this.playing = false;
    }
    load() {
        return Promise.race([
            new Promise((resolve, reject) => {
                setTimeout(reject,  false ? undefined : 5000);
            }),
            new Promise((resolve, reject) => {
                this.howl = new Howl({
                    src: Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_1__["cachebust"])("res/sounds/music/" + this.url + ".mp3"),
                    autoplay: false,
                    loop: true,
                    html5: true,
                    volume: 1,
                    preload: true,
                    pool: 2,

                    onunlock: () => {
                        if (this.playing) {
                            logger.log("Playing music after manual unlock");
                            this.play();
                        }
                    },

                    onload: () => {
                        resolve();
                    },
                    onloaderror: (id, err) => {
                        logger.warn(this, "Music", this.url, "failed to load:", id, err);
                        this.howl = null;
                        resolve();
                    },
                    onplayerror: (id, err) => {
                        logger.warn(this, "Music", this.url, "failed to play:", id, err);
                    },
                });
            }),
        ]);
    }

    stop() {
        if (this.howl && this.instance) {
            this.playing = false;
            this.howl.pause(this.instance);
        }
    }

    isPlaying() {
        return this.playing;
    }

    play(volume) {
        if (this.howl) {
            this.playing = true;
            this.howl.volume(volume);
            if (this.instance) {
                this.howl.play(this.instance);
            } else {
                this.instance = this.howl.play();
            }
        }
    }

    setVolume(volume) {
        if (this.howl) {
            this.howl.volume(volume);
        }
    }

    deinitialize() {
        if (this.howl) {
            this.howl.unload();
            this.howl = null;
            this.instance = null;
        }
    }
}

class SoundImplBrowser extends _sound__WEBPACK_IMPORTED_MODULE_0__["SoundInterface"] {
    constructor(app) {
        Howler.mobileAutoEnable = true;
        Howler.autoUnlock = true;
        Howler.autoSuspend = false;
        Howler.html5PoolSize = 20;
        Howler.pos(0, 0, 0);

        super(app, WrappedSoundInstance, MusicInstance);
    }

    initialize() {
        // NOTICE: We override the initialize() method here with custom logic because
        // we have a sound sprites instance

        this.sfxHandle = new SoundSpritesContainer();

        // @ts-ignore
        const keys = Object.values(_sound__WEBPACK_IMPORTED_MODULE_0__["SOUNDS"]);
        keys.forEach(key => {
            this.sounds[key] = new WrappedSoundInstance(this.sfxHandle, key);
        });
        for (const musicKey in _sound__WEBPACK_IMPORTED_MODULE_0__["MUSIC"]) {
            const musicPath = _sound__WEBPACK_IMPORTED_MODULE_0__["MUSIC"][musicKey];
            const music = new this.musicClass(musicKey, musicPath);
            this.music[musicPath] = music;
        }

        this.musicVolume = this.app.settings.getAllSettings().musicVolume;
        this.soundVolume = this.app.settings.getAllSettings().soundVolume;

        if (false) {}

        return Promise.resolve();
    }

    deinitialize() {
        return super.deinitialize().then(() => Howler.unload());
    }
}


/***/ }),

/***/ "./src/js/platform/browser/storage.js":
/*!********************************************!*\
  !*** ./src/js/platform/browser/storage.js ***!
  \********************************************/
/*! exports provided: StorageImplBrowser */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StorageImplBrowser", function() { return StorageImplBrowser; });
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../storage */ "./src/js/platform/storage.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");



const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("storage/browser");

const LOCAL_STORAGE_UNAVAILABLE = "local-storage-unavailable";
const LOCAL_STORAGE_NO_WRITE_PERMISSION = "local-storage-no-write-permission";

let randomDelay = () => 0;

if (false) {}

class StorageImplBrowser extends _storage__WEBPACK_IMPORTED_MODULE_0__["StorageInterface"] {
    constructor(app) {
        super(app);
        this.currentBusyFilename = false;
    }

    initialize() {
        logger.error("Using localStorage, please update to a newer browser");
        return new Promise((resolve, reject) => {
            // Check for local storage availability in general
            if (!window.localStorage) {
                alert("Local storage is not available! Please upgrade to a newer browser!");
                reject(LOCAL_STORAGE_UNAVAILABLE);
            }

            // Check if we can set and remove items
            try {
                window.localStorage.setItem("storage_availability_test", "1");
                window.localStorage.removeItem("storage_availability_test");
            } catch (e) {
                alert(
                    "It seems we don't have permission to write to local storage! Please update your browsers settings or use a different browser!"
                );
                reject(LOCAL_STORAGE_NO_WRITE_PERMISSION);
                return;
            }
            setTimeout(resolve, 0);
        });
    }

    writeFileAsync(filename, contents) {
        if (this.currentBusyFilename === filename) {
            logger.warn("Attempt to write", filename, "while write process is not finished!");
        }

        this.currentBusyFilename = filename;
        window.localStorage.setItem(filename, contents);
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                this.currentBusyFilename = false;
                resolve();
            }, 0);
        });
    }

    writeFileSyncIfSupported(filename, contents) {
        window.localStorage.setItem(filename, contents);
        return true;
    }

    readFileAsync(filename) {
        if (this.currentBusyFilename === filename) {
            logger.warn("Attempt to read", filename, "while write progress on it is ongoing!");
        }

        return new Promise((resolve, reject) => {
            const contents = window.localStorage.getItem(filename);
            if (!contents) {
                // File not found
                setTimeout(() => reject(_storage__WEBPACK_IMPORTED_MODULE_0__["FILE_NOT_FOUND"]), randomDelay());
                return;
            }

            // File read, simulate delay
            setTimeout(() => resolve(contents), 0);
        });
    }

    deleteFileAsync(filename) {
        if (this.currentBusyFilename === filename) {
            logger.warn("Attempt to delete", filename, "while write progres on it is ongoing!");
        }

        this.currentBusyFilename = filename;
        return new Promise((resolve, reject) => {
            window.localStorage.removeItem(filename);
            setTimeout(() => {
                this.currentBusyFilename = false;
                resolve();
            }, 0);
        });
    }
}


/***/ }),

/***/ "./src/js/platform/browser/storage_indexed_db.js":
/*!*******************************************************!*\
  !*** ./src/js/platform/browser/storage_indexed_db.js ***!
  \*******************************************************/
/*! exports provided: StorageImplBrowserIndexedDB */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StorageImplBrowserIndexedDB", function() { return StorageImplBrowserIndexedDB; });
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../storage */ "./src/js/platform/storage.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");



const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("storage/browserIDB");

const LOCAL_STORAGE_UNAVAILABLE = "local-storage-unavailable";
const LOCAL_STORAGE_NO_WRITE_PERMISSION = "local-storage-no-write-permission";

let randomDelay = () => 0;

if (false) {}

class StorageImplBrowserIndexedDB extends _storage__WEBPACK_IMPORTED_MODULE_0__["StorageInterface"] {
    constructor(app) {
        super(app);
        this.currentBusyFilename = false;

        /** @type {IDBDatabase} */
        this.database = null;
    }

    initialize() {
        logger.log("Using indexed DB storage");
        return new Promise((resolve, reject) => {
            const request = window.indexedDB.open("app_storage", 10);
            request.onerror = event => {
                logger.error("IDB error:", event);
                alert(
                    "Sorry, it seems your browser has blocked the access to the storage system. This might be the case if you are browsing in private mode for example. I recommend to use google chrome or disable private browsing."
                );
                reject("Indexed DB access error");
            };

            // @ts-ignore
            request.onsuccess = event => resolve(event.target.result);

            request.onupgradeneeded = /** @type {IDBVersionChangeEvent} */ event => {
                /** @type {IDBDatabase} */
                // @ts-ignore
                const database = event.target.result;

                const objectStore = database.createObjectStore("files", {
                    keyPath: "filename",
                });

                objectStore.createIndex("filename", "filename", { unique: true });

                objectStore.transaction.onerror = event => {
                    logger.error("IDB transaction error:", event);
                    reject("Indexed DB transaction error during migration, check console output.");
                };

                objectStore.transaction.oncomplete = event => {
                    logger.log("Object store completely initialized");
                    resolve(database);
                };
            };
        }).then(database => {
            this.database = database;
        });
    }

    writeFileAsync(filename, contents) {
        if (this.currentBusyFilename === filename) {
            logger.warn("Attempt to write", filename, "while write process is not finished!");
        }
        if (!this.database) {
            return Promise.reject("Storage not ready");
        }

        this.currentBusyFilename = filename;
        const transaction = this.database.transaction(["files"], "readwrite");

        return new Promise((resolve, reject) => {
            transaction.oncomplete = () => {
                this.currentBusyFilename = null;
                resolve();
            };

            transaction.onerror = error => {
                this.currentBusyFilename = null;
                logger.error("Error while writing", filename, ":", error);
                reject(error);
            };

            const store = transaction.objectStore("files");
            store.put({
                filename,
                contents,
            });
        });
    }

    writeFileSyncIfSupported(filename, contents) {
        // Not supported
        this.writeFileAsync(filename, contents);
        return true;
    }

    readFileAsync(filename) {
        if (!this.database) {
            return Promise.reject("Storage not ready");
        }

        this.currentBusyFilename = filename;
        const transaction = this.database.transaction(["files"], "readonly");

        return new Promise((resolve, reject) => {
            const store = transaction.objectStore("files");
            const request = store.get(filename);

            request.onsuccess = event => {
                this.currentBusyFilename = null;
                if (!request.result) {
                    reject(_storage__WEBPACK_IMPORTED_MODULE_0__["FILE_NOT_FOUND"]);
                    return;
                }
                resolve(request.result.contents);
            };

            request.onerror = error => {
                this.currentBusyFilename = null;
                logger.error("Error while reading", filename, ":", error);
                reject(error);
            };
        });
    }

    deleteFileAsync(filename) {
        if (this.currentBusyFilename === filename) {
            logger.warn("Attempt to delete", filename, "while write progres on it is ongoing!");
        }

        if (!this.database) {
            return Promise.reject("Storage not ready");
        }

        this.currentBusyFilename = filename;
        const transaction = this.database.transaction(["files"], "readwrite");

        return new Promise((resolve, reject) => {
            transaction.oncomplete = () => {
                this.currentBusyFilename = null;
                resolve();
            };

            transaction.onerror = error => {
                this.currentBusyFilename = null;
                logger.error("Error while deleting", filename, ":", error);
                reject(error);
            };

            const store = transaction.objectStore("files");
            store.delete(filename);
        });
    }
}


/***/ }),

/***/ "./src/js/platform/browser/wrapper.js":
/*!********************************************!*\
  !*** ./src/js/platform/browser/wrapper.js ***!
  \********************************************/
/*! exports provided: PlatformWrapperImplBrowser */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlatformWrapperImplBrowser", function() { return PlatformWrapperImplBrowser; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_query_parameters__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/query_parameters */ "./src/js/core/query_parameters.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _ad_providers_gamedistribution__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../ad_providers/gamedistribution */ "./src/js/platform/ad_providers/gamedistribution.js");
/* harmony import */ var _ad_providers_no_ad_provider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../ad_providers/no_ad_provider */ "./src/js/platform/ad_providers/no_ad_provider.js");
/* harmony import */ var _wrapper__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../wrapper */ "./src/js/platform/wrapper.js");
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./storage */ "./src/js/platform/browser/storage.js");
/* harmony import */ var _storage_indexed_db__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./storage_indexed_db */ "./src/js/platform/browser/storage_indexed_db.js");










const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("platform/browser");

class PlatformWrapperImplBrowser extends _wrapper__WEBPACK_IMPORTED_MODULE_6__["PlatformWrapperInterface"] {
    initialize() {
        this.recaptchaTokenCallback = null;

        this.embedProvider = {
            id: "shapezio-website",
            adProvider: _ad_providers_no_ad_provider__WEBPACK_IMPORTED_MODULE_5__["NoAdProvider"],
            iframed: false,
            externalLinks: true,
            iogLink: true,
        };

        if ( true && _core_query_parameters__WEBPACK_IMPORTED_MODULE_2__["queryParamOptions"].embedProvider) {
            const providerId = _core_query_parameters__WEBPACK_IMPORTED_MODULE_2__["queryParamOptions"].embedProvider;
            this.embedProvider.iframed = true;
            this.embedProvider.iogLink = false;

            switch (providerId) {
                case "armorgames": {
                    this.embedProvider.id = "armorgames";
                    break;
                }

                case "iogames.space": {
                    this.embedProvider.id = "iogames.space";
                    this.embedProvider.iogLink = true;
                    break;
                }

                case "miniclip": {
                    this.embedProvider.id = "miniclip";
                    break;
                }

                case "gamedistribution": {
                    this.embedProvider.id = "gamedistribution";
                    this.embedProvider.externalLinks = false;
                    this.embedProvider.adProvider = _ad_providers_gamedistribution__WEBPACK_IMPORTED_MODULE_4__["GamedistributionAdProvider"];
                    break;
                }

                case "kongregate": {
                    this.embedProvider.id = "kongregate";
                    break;
                }

                case "crazygames": {
                    this.embedProvider.id = "crazygames";
                    break;
                }

                default: {
                    logger.error("Got unsupported embed provider:", providerId);
                }
            }
        }

        logger.log("Embed provider:", this.embedProvider.id);

        return this.detectStorageImplementation()
            .then(() => this.initializeAdProvider())
            .then(() => super.initialize());
    }

    detectStorageImplementation() {
        return new Promise(resolve => {
            logger.log("Detecting storage");

            if (!window.indexedDB) {
                logger.log("Indexed DB not supported");
                this.app.storage = new _storage__WEBPACK_IMPORTED_MODULE_7__["StorageImplBrowser"](this.app);
                resolve();
                return;
            }

            // Try accessing the indexedb
            let request;
            try {
                request = window.indexedDB.open("indexeddb_feature_detection", 1);
            } catch (ex) {
                logger.warn("Error while opening indexed db:", ex);
                this.app.storage = new _storage__WEBPACK_IMPORTED_MODULE_7__["StorageImplBrowser"](this.app);
                resolve();
                return;
            }
            request.onerror = err => {
                logger.log("Indexed DB can *not* be accessed: ", err);
                logger.log("Using fallback to local storage");
                this.app.storage = new _storage__WEBPACK_IMPORTED_MODULE_7__["StorageImplBrowser"](this.app);
                resolve();
            };
            request.onsuccess = () => {
                logger.log("Indexed DB *can* be accessed");
                this.app.storage = new _storage_indexed_db__WEBPACK_IMPORTED_MODULE_8__["StorageImplBrowserIndexedDB"](this.app);
                resolve();
            };
        });
    }

    getId() {
        return "browser@" + this.embedProvider.id;
    }

    getUiScale() {
        if (_core_config__WEBPACK_IMPORTED_MODULE_0__["IS_MOBILE"]) {
            return 1;
        }

        const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight);
        return Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["clamp"])((avgDims / 1000.0) * 1.9, 0.1, 10);
    }

    getSupportsRestart() {
        return true;
    }

    getTouchPanStrength() {
        return _core_config__WEBPACK_IMPORTED_MODULE_0__["IS_MOBILE"] ? 1 : 0.5;
    }

    openExternalLink(url, force = false) {
        logger.log("Opening external:", url);
        if (force || this.embedProvider.externalLinks) {
            window.open(url);
        } else {
            // Do nothing
            alert(
                "This platform does not allow opening external links. You can play on https://shapez.io directly to open them.\n\nClicked Link: " +
                    url
            );
        }
    }

    performRestart() {
        logger.log("Performing restart");
        window.location.reload(true);
    }

    /**
     * Detects if there is an adblocker installed
     * @returns {Promise<boolean>}
     */
    detectAdblock() {
        return Promise.race([
            new Promise(resolve => {
                // If the request wasn't blocked within a very short period of time, this means
                // the adblocker is not active and the request was actually made -> ignore it then
                setTimeout(() => resolve(false), 30);
            }),
            new Promise(resolve => {
                fetch("https://googleads.g.doubleclick.net/pagead/id", {
                    method: "HEAD",
                    mode: "no-cors",
                })
                    .then(res => {
                        resolve(false);
                    })
                    .catch(err => {
                        resolve(true);
                    });
            }),
        ]);
    }

    initializeAdProvider() {
        if (false) {}

        // First, detect adblocker
        return this.detectAdblock().then(hasAdblocker => {
            if (hasAdblocker) {
                logger.log("Adblock detected");
                return;
            }

            const adProvider = this.embedProvider.adProvider;
            this.app.adProvider = new adProvider(this.app);
            return this.app.adProvider.initialize().catch(err => {
                logger.error("Failed to initialize ad provider, disabling ads:", err);
                this.app.adProvider = new _ad_providers_no_ad_provider__WEBPACK_IMPORTED_MODULE_5__["NoAdProvider"](this.app);
            });
        });
    }

    exitApp() {
        // Can not exit app
    }
}


/***/ }),

/***/ "./src/js/platform/electron/storage.js":
/*!*********************************************!*\
  !*** ./src/js/platform/electron/storage.js ***!
  \*********************************************/
/*! exports provided: StorageImplElectron */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StorageImplElectron", function() { return StorageImplElectron; });
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../storage */ "./src/js/platform/storage.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");




const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("electron-storage");

class StorageImplElectron extends _storage__WEBPACK_IMPORTED_MODULE_0__["StorageInterface"] {
    constructor(app) {
        super(app);

        /** @type {Object.<number, {resolve:Function, reject: Function}>} */
        this.jobs = {};
        this.jobId = 0;

        Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().on("fs-response", (event, arg) => {
            const id = arg.id;
            if (!this.jobs[id]) {
                logger.warn("Got unhandled FS response, job not known:", id);
                return;
            }
            const { resolve, reject } = this.jobs[id];
            if (arg.result.success) {
                resolve(arg.result.data);
            } else {
                reject(arg.result.error);
            }
        });
    }

    initialize() {
        return Promise.resolve();
    }

    writeFileAsync(filename, contents) {
        return new Promise((resolve, reject) => {
            // ipcMain
            const jobId = ++this.jobId;
            this.jobs[jobId] = { resolve, reject };

            Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().send("fs-job", {
                type: "write",
                filename,
                contents,
                id: jobId,
            });
        });
    }

    writeFileSyncIfSupported(filename, contents) {
        return Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().sendSync("fs-sync-job", {
            type: "write",
            filename,
            contents,
        });
    }

    readFileAsync(filename) {
        return new Promise((resolve, reject) => {
            // ipcMain
            const jobId = ++this.jobId;
            this.jobs[jobId] = { resolve, reject };

            Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().send("fs-job", {
                type: "read",
                filename,
                id: jobId,
            });
        });
    }

    deleteFileAsync(filename) {
        return new Promise((resolve, reject) => {
            // ipcMain
            const jobId = ++this.jobId;
            this.jobs[jobId] = { resolve, reject };
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().send("fs-job", {
                type: "delete",
                filename,
                id: jobId,
            });
        });
    }
}


/***/ }),

/***/ "./src/js/platform/electron/wrapper.js":
/*!*********************************************!*\
  !*** ./src/js/platform/electron/wrapper.js ***!
  \*********************************************/
/*! exports provided: PlatformWrapperImplElectron */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlatformWrapperImplElectron", function() { return PlatformWrapperImplElectron; });
/* harmony import */ var _browser_wrapper__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../browser/wrapper */ "./src/js/platform/browser/wrapper.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./storage */ "./src/js/platform/electron/storage.js");
/* harmony import */ var _wrapper__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../wrapper */ "./src/js/platform/wrapper.js");






const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("electron-wrapper");

class PlatformWrapperImplElectron extends _browser_wrapper__WEBPACK_IMPORTED_MODULE_0__["PlatformWrapperImplBrowser"] {
    initialize() {
        this.app.storage = new _storage__WEBPACK_IMPORTED_MODULE_3__["StorageImplElectron"](this);
        return _wrapper__WEBPACK_IMPORTED_MODULE_4__["PlatformWrapperInterface"].prototype.initialize.call(this);
    }

    getId() {
        return "electron";
    }

    getSupportsRestart() {
        return true;
    }

    openExternalLink(url) {
        logger.log(this, "Opening external:", url);
        window.open(url, "about:blank");
    }

    getSupportsAds() {
        return false;
    }

    performRestart() {
        logger.log(this, "Performing restart");
        window.location.reload(true);
    }

    initializeAdProvider() {
        return Promise.resolve();
    }

    getSupportsFullscreen() {
        return true;
    }

    setFullscreen(flag) {
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().send("set-fullscreen", flag);
    }

    getSupportsAppExit() {
        return true;
    }

    exitApp() {
        logger.log(this, "Sending app exit signal");
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["getIPCRenderer"])().send("exit-app");
    }
}


/***/ }),

/***/ "./src/js/platform/game_analytics.js":
/*!*******************************************!*\
  !*** ./src/js/platform/game_analytics.js ***!
  \*******************************************/
/*! exports provided: GameAnalyticsInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameAnalyticsInterface", function() { return GameAnalyticsInterface; });
/**
 * @typedef {import("../application").Application} Application
 */

class GameAnalyticsInterface {
    constructor(app) {
        /** @type {Application} */
        this.app = app;
    }

    /**
     * Initializes the analytics
     * @returns {Promise<void>}
     */
    initialize() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.reject();
    }

    /**
     * Handles a new game which was started
     */
    handleGameStarted() {}

    /**
     * Handles a resumed game
     */
    handleGameResumed() {}

    /**
     * Handles the given level completed
     * @param {number} level
     */
    handleLevelCompleted(level) {}

    /**
     * Handles the given upgrade completed
     * @param {string} id
     * @param {number} level
     */
    handleUpgradeUnlocked(id, level) {}
}


/***/ }),

/***/ "./src/js/platform/sound.js":
/*!**********************************!*\
  !*** ./src/js/platform/sound.js ***!
  \**********************************/
/*! exports provided: SOUNDS, MUSIC, SoundInstanceInterface, MusicInstanceInterface, SoundInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SOUNDS", function() { return SOUNDS; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MUSIC", function() { return MUSIC; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SoundInstanceInterface", function() { return SoundInstanceInterface; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MusicInstanceInterface", function() { return MusicInstanceInterface; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SoundInterface", function() { return SoundInterface; });
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");





const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("sound");

const SOUNDS = {
    // Menu and such
    uiClick: "ui_click",
    uiError: "ui_error",
    dialogError: "dialog_error",
    dialogOk: "dialog_ok",
    swishHide: "ui_swish_hide",
    swishShow: "ui_swish_show",
    badgeNotification: "badge_notification",

    levelComplete: "level_complete",

    destroyBuilding: "destroy_building",
    placeBuilding: "place_building",
    placeBelt: "place_belt",
    copy: "copy",
};

const MUSIC = {
    // The theme always depends on the standalone only, even if running the full
    // version in the browser
    theme:  false ? undefined : "theme-short",
    menu: "menu",
};

class SoundInstanceInterface {
    constructor(key, url) {
        this.key = key;
        this.url = url;
    }

    /** @returns {Promise<void>} */
    load() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.resolve();
    }

    play(volume) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    deinitialize() {}
}

class MusicInstanceInterface {
    constructor(key, url) {
        this.key = key;
        this.url = url;
    }

    stop() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    play(volume) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    setVolume(volume) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /** @returns {Promise<void>} */
    load() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.resolve();
    }

    /** @returns {boolean} */
    isPlaying() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }

    deinitialize() {}
}

class SoundInterface {
    constructor(app, soundClass, musicClass) {
        /** @type {Application} */
        this.app = app;

        this.soundClass = soundClass;
        this.musicClass = musicClass;

        /** @type {Object<string, SoundInstanceInterface>} */
        this.sounds = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["newEmptyMap"])();

        /** @type {Object<string, MusicInstanceInterface>} */
        this.music = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["newEmptyMap"])();

        /** @type {MusicInstanceInterface} */
        this.currentMusic = null;

        this.pageIsVisible = true;

        this.musicVolume = 1.0;
        this.soundVolume = 1.0;
    }

    /**
     * Initializes the sound
     * @returns {Promise<any>}
     */
    initialize() {
        for (const soundKey in SOUNDS) {
            const soundPath = SOUNDS[soundKey];
            const sound = new this.soundClass(soundKey, soundPath);
            this.sounds[soundPath] = sound;
        }

        for (const musicKey in MUSIC) {
            const musicPath = MUSIC[musicKey];
            const music = new this.musicClass(musicKey, musicPath);
            this.music[musicPath] = music;
        }

        this.musicVolume = this.app.settings.getAllSettings().musicVolume;
        this.soundVolume = this.app.settings.getAllSettings().soundVolume;

        if (false) {}

        return Promise.resolve();
    }

    /**
     * Pre-Loads the given sounds
     * @param {string} key
     * @returns {Promise<void>}
     */
    loadSound(key) {
        if (this.sounds[key]) {
            return this.sounds[key].load();
        } else if (this.music[key]) {
            return this.music[key].load();
        } else {
            logger.error("Sound/Music by key not found:", key);
            return Promise.resolve();
        }
    }

    /** Deinits the sound
     * @returns {Promise<void>}
     */
    deinitialize() {
        const promises = [];
        for (const key in this.sounds) {
            promises.push(this.sounds[key].deinitialize());
        }
        for (const key in this.music) {
            promises.push(this.music[key].deinitialize());
        }
        // @ts-ignore
        return Promise.all(...promises);
    }

    /**
     * Returns the music volume
     * @returns {number}
     */
    getMusicVolume() {
        return this.musicVolume;
    }

    /**
     * Returns the sound volume
     * @returns {number}
     */
    getSoundVolume() {
        return this.soundVolume;
    }

    /**
     * Sets the music volume
     * @param {number} volume
     */
    setMusicVolume(volume) {
        this.musicVolume = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["clamp"])(volume, 0, 1);
        if (this.currentMusic) {
            this.currentMusic.setVolume(this.musicVolume);
        }
    }

    /**
     * Sets the sound volume
     * @param {number} volume
     */
    setSoundVolume(volume) {
        this.soundVolume = Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["clamp"])(volume, 0, 1);
    }

    /**
     * Focus change handler, called by the pap
     * @param {boolean} pageIsVisible
     */
    onPageRenderableStateChanged(pageIsVisible) {
        this.pageIsVisible = pageIsVisible;
        if (this.currentMusic) {
            if (pageIsVisible) {
                if (!this.currentMusic.isPlaying()) {
                    this.currentMusic.play(this.musicVolume);
                }
            } else {
                this.currentMusic.stop();
            }
        }
    }

    /**
     * @param {string} key
     */
    playUiSound(key) {
        if (!this.sounds[key]) {
            logger.warn("Sound", key, "not found, probably not loaded yet");
            return;
        }
        this.sounds[key].play(this.soundVolume);
    }

    /**
     *
     * @param {string} key
     * @param {Vector} worldPosition
     * @param {GameRoot} root
     */
    play3DSound(key, worldPosition, root) {
        if (!this.sounds[key]) {
            logger.warn("Music", key, "not found, probably not loaded yet");
            return;
        }
        if (!this.pageIsVisible) {
            return;
        }

        // hack, but works
        if (root.time.getIsPaused()) {
            return;
        }

        let volume = this.soundVolume;
        if (!root.camera.isWorldPointOnScreen(worldPosition)) {
            volume = this.soundVolume / 5; // In the old implementation this value was fixed to 0.2 => 20% of 1.0
        }
        volume *= Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["clamp"])(root.camera.zoomLevel / 3);
        this.sounds[key].play(Object(_core_utils__WEBPACK_IMPORTED_MODULE_0__["clamp"])(volume));
    }

    /**
     * @param {string} key
     */
    playThemeMusic(key) {
        const music = this.music[key];
        if (key !== null && !music) {
            logger.warn("Music", key, "not found");
        }
        if (this.currentMusic !== music) {
            if (this.currentMusic) {
                logger.log("Stopping", this.currentMusic.key);
                this.currentMusic.stop();
            }
            this.currentMusic = music;
            if (music && this.pageIsVisible) {
                logger.log("Starting", this.currentMusic.key);
                music.play(this.musicVolume);
            }
        }
    }
}


/***/ }),

/***/ "./src/js/platform/storage.js":
/*!************************************!*\
  !*** ./src/js/platform/storage.js ***!
  \************************************/
/*! exports provided: FILE_NOT_FOUND, StorageInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FILE_NOT_FOUND", function() { return FILE_NOT_FOUND; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StorageInterface", function() { return StorageInterface; });

const FILE_NOT_FOUND = "file_not_found";

class StorageInterface {
    constructor(app) {
        /** @type {Application} */
        this.app = app;
    }

    /**
     * Initializes the storage
     * @returns {Promise<void>}
     */
    initialize() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.reject();
    }

    /**
     * Writes a string to a file asynchronously
     * @param {string} filename
     * @param {string} contents
     * @returns {Promise<void>}
     */
    writeFileAsync(filename, contents) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.reject();
    }

    /**
     * Tries to write a file synchronously, used in unload handler
     * @param {string} filename
     * @param {string} contents
     */
    writeFileSyncIfSupported(filename, contents) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }

    /**
     * Reads a string asynchronously. Returns Promise<FILE_NOT_FOUND> if file was not found.
     * @param {string} filename
     * @returns {Promise<string>}
     */
    readFileAsync(filename) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return Promise.reject();
    }

    /**
     * Tries to delete a file
     * @param {string} filename
     * @returns {Promise<void>}
     */
    deleteFileAsync(filename) {
        // Default implementation does not allow deleting files
        return Promise.reject();
    }
}


/***/ }),

/***/ "./src/js/platform/wrapper.js":
/*!************************************!*\
  !*** ./src/js/platform/wrapper.js ***!
  \************************************/
/*! exports provided: PlatformWrapperInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlatformWrapperInterface", function() { return PlatformWrapperInterface; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");




class PlatformWrapperInterface {
    constructor(app) {
        /** @type {Application} */
        this.app = app;
    }

    /** @returns {string} */
    getId() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "unknown-platform";
    }

    /**
     * Returns the UI scale, called on every resize
     * @returns {number} */
    getUiScale() {
        return 1;
    }

    /** @returns {boolean} */
    getSupportsRestart() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }

    /**
     * Returns the strength of touch pans with the mouse
     */
    getTouchPanStrength() {
        return 1;
    }

    /** @returns {Promise<void>} */
    initialize() {
        document.documentElement.classList.add("p-" + this.getId());
        return Promise.resolve();
    }

    /**
     * Should initialize the apps ad provider in case supported
     *  @returns {Promise<void>}
     */
    initializeAdProvider() {
        return Promise.resolve();
    }

    /**
     * Should return the minimum supported zoom level
     * @returns {number}
     */
    getMinimumZoom() {
        return 0.1 * this.getScreenScale();
    }

    /**
     * Should return the maximum supported zoom level
     * @returns {number}
     */
    getMaximumZoom() {
        return 3.5 * this.getScreenScale();
    }

    getScreenScale() {
        return Math.min(window.innerWidth, window.innerHeight) / 1024.0;
    }

    /**
     * Should return if this platform supports ads at all
     */
    getSupportsAds() {
        return false;
    }

    /**
     * Attempt to open an external url
     * @param {string} url
     * @param {boolean=} force Whether to always open the url even if not allowed
     */
    openExternalLink(url, force = false) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Attempt to restart the app
     */
    performRestart() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Returns whether this platform supports a toggleable fullscreen
     */
    getSupportsFullscreen() {
        return false;
    }

    /**
     * Should set the apps fullscreen state to the desired state
     * @param {boolean} flag
     */
    setFullscreen(flag) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Returns whether this platform supports quitting the app
     */
    getSupportsAppExit() {
        return false;
    }

    /**
     * Attempts to quit the app
     */
    exitApp() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Whether this platform supports a keyboard
     */
    getSupportsKeyboard() {
        return !_core_config__WEBPACK_IMPORTED_MODULE_0__["IS_MOBILE"];
    }
}


/***/ }),

/***/ "./src/js/profile/application_settings.js":
/*!************************************************!*\
  !*** ./src/js/profile/application_settings.js ***!
  \************************************************/
/*! exports provided: enumCategories, uiScales, scrollWheelSensitivities, movementSpeeds, autosaveIntervals, allApplicationSettings, getApplicationSettingById, ApplicationSettings */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumCategories", function() { return enumCategories; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "uiScales", function() { return uiScales; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scrollWheelSensitivities", function() { return scrollWheelSensitivities; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "movementSpeeds", function() { return movementSpeeds; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "autosaveIntervals", function() { return autosaveIntervals; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "allApplicationSettings", function() { return allApplicationSettings; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getApplicationSettingById", function() { return getApplicationSettingById; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ApplicationSettings", function() { return ApplicationSettings; });
/* harmony import */ var _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/read_write_proxy */ "./src/js/core/read_write_proxy.js");
/* harmony import */ var _setting_types__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./setting_types */ "./src/js/profile/setting_types.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_explained_result__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/explained_result */ "./src/js/core/explained_result.js");
/* harmony import */ var _game_theme__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../game/theme */ "./src/js/game/theme.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");
/* harmony import */ var _languages__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../languages */ "./src/js/languages.js");









const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("application_settings");

/**
 * @enum {string}
 */
const enumCategories = {
    general: "general",
    userInterface: "userInterface",
    performance: "performance",
    advanced: "advanced",
};

const uiScales = [
    {
        id: "super_small",
        size: 0.6,
    },
    {
        id: "small",
        size: 0.8,
    },
    {
        id: "regular",
        size: 1,
    },
    {
        id: "large",
        size: 1.05,
    },
    {
        id: "huge",
        size: 1.1,
    },
];

const scrollWheelSensitivities = [
    {
        id: "super_slow",
        scale: 0.25,
    },
    {
        id: "slow",
        scale: 0.5,
    },
    {
        id: "regular",
        scale: 1,
    },
    {
        id: "fast",
        scale: 2,
    },
    {
        id: "super_fast",
        scale: 4,
    },
];

const movementSpeeds = [
    {
        id: "super_slow",
        multiplier: 0.25,
    },
    {
        id: "slow",
        multiplier: 0.5,
    },
    {
        id: "regular",
        multiplier: 1,
    },
    {
        id: "fast",
        multiplier: 2,
    },
    {
        id: "super_fast",
        multiplier: 4,
    },
    {
        id: "extremely_fast",
        multiplier: 8,
    },
];

const autosaveIntervals = [
    {
        id: "one_minute",
        seconds: 60,
    },
    {
        id: "two_minutes",
        seconds: 120,
    },
    {
        id: "five_minutes",
        seconds: 5 * 60,
    },
    {
        id: "ten_minutes",
        seconds: 10 * 60,
    },
    {
        id: "twenty_minutes",
        seconds: 20 * 60,
    },
    {
        id: "disabled",
        seconds: null,
    },
];

const refreshRateOptions = ["30", "60", "120", "180", "240"];

if (false) {}

/** @type {Array<BaseSetting>} */
const allApplicationSettings = [
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("language", {
        options: Object.keys(_languages__WEBPACK_IMPORTED_MODULE_6__["LANGUAGES"]),
        valueGetter: key => key,
        textGetter: key => _languages__WEBPACK_IMPORTED_MODULE_6__["LANGUAGES"][key].name,
        category: enumCategories.general,
        restartRequired: true,
        changeCb: (app, id) => null,
        magicValue: "auto-detect",
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("uiScale", {
        options: uiScales.sort((a, b) => a.size - b.size),
        valueGetter: scale => scale.id,
        textGetter: scale => _translations__WEBPACK_IMPORTED_MODULE_5__["T"].settings.labels.uiScale.scales[scale.id],
        category: enumCategories.userInterface,
        restartRequired: false,
        changeCb:
            /**
             * @param {Application} app
             */
            (app, id) => app.updateAfterUiScaleChanged(),
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["RangeSetting"](
        "soundVolume",
        enumCategories.general,
        /**
         * @param {Application} app
         */
        (app, value) => app.sound.setSoundVolume(value)
    ),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["RangeSetting"](
        "musicVolume",
        enumCategories.general,
        /**
         * @param {Application} app
         */
        (app, value) => app.sound.setMusicVolume(value)
    ),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"](
        "fullscreen",
        enumCategories.general,
        /**
         * @param {Application} app
         */
        (app, value) => {
            if (app.platformWrapper.getSupportsFullscreen()) {
                app.platformWrapper.setFullscreen(value);
            }
        },
        /**
         * @param {Application} app
         */ app => app.restrictionMgr.getHasExtendedSettings()
    ),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"](
        "enableColorBlindHelper",
        enumCategories.general,
        /**
         * @param {Application} app
         */
        (app, value) => null
    ),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("offerHints", enumCategories.userInterface, (app, value) => {}),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("theme", {
        options: Object.keys(_game_theme__WEBPACK_IMPORTED_MODULE_4__["THEMES"]),
        valueGetter: theme => theme,
        textGetter: theme => _translations__WEBPACK_IMPORTED_MODULE_5__["T"].settings.labels.theme.themes[theme],
        category: enumCategories.userInterface,
        restartRequired: false,
        changeCb:
            /**
             * @param {Application} app
             */
            (app, id) => {
                Object(_game_theme__WEBPACK_IMPORTED_MODULE_4__["applyGameTheme"])(id);
                document.documentElement.setAttribute("data-theme", id);
            },
        enabledCb: /**
         * @param {Application} app
         */ app => app.restrictionMgr.getHasExtendedSettings(),
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("autosaveInterval", {
        options: autosaveIntervals,
        valueGetter: interval => interval.id,
        textGetter: interval => _translations__WEBPACK_IMPORTED_MODULE_5__["T"].settings.labels.autosaveInterval.intervals[interval.id],
        category: enumCategories.advanced,
        restartRequired: false,
        changeCb:
            /**
             * @param {Application} app
             */
            (app, id) => null,
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("scrollWheelSensitivity", {
        options: scrollWheelSensitivities.sort((a, b) => a.scale - b.scale),
        valueGetter: scale => scale.id,
        textGetter: scale => _translations__WEBPACK_IMPORTED_MODULE_5__["T"].settings.labels.scrollWheelSensitivity.sensitivity[scale.id],
        category: enumCategories.advanced,
        restartRequired: false,
        changeCb:
            /**
             * @param {Application} app
             */
            (app, id) => app.updateAfterUiScaleChanged(),
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("movementSpeed", {
        options: movementSpeeds.sort((a, b) => a.multiplier - b.multiplier),
        valueGetter: multiplier => multiplier.id,
        textGetter: multiplier => _translations__WEBPACK_IMPORTED_MODULE_5__["T"].settings.labels.movementSpeed.speeds[multiplier.id],
        category: enumCategories.advanced,
        restartRequired: false,
        changeCb: (app, id) => {},
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("enableMousePan", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("alwaysMultiplace", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("zoomToCursor", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("enableTunnelSmartplace", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("vignette", enumCategories.userInterface, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("compactBuildingInfo", enumCategories.userInterface, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("disableCutDeleteWarnings", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("rotationByBuilding", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("displayChunkBorders", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("pickMinerOnPatch", enumCategories.advanced, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["RangeSetting"]("mapResourcesScale", enumCategories.advanced, () => null),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["EnumSetting"]("refreshRate", {
        options: refreshRateOptions,
        valueGetter: rate => rate,
        textGetter: rate => rate + " Hz",
        category: enumCategories.performance,
        restartRequired: false,
        changeCb: (app, id) => {},
        enabledCb: /**
         * @param {Application} app
         */ app => app.restrictionMgr.getHasExtendedSettings(),
    }),

    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("lowQualityMapResources", enumCategories.performance, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("disableTileGrid", enumCategories.performance, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("lowQualityTextures", enumCategories.performance, (app, value) => {}),
    new _setting_types__WEBPACK_IMPORTED_MODULE_1__["BoolSetting"]("simplifiedBelts", enumCategories.performance, (app, value) => {}),
];

function getApplicationSettingById(id) {
    return allApplicationSettings.find(setting => setting.id === id);
}

class SettingsStorage {
    constructor() {
        this.uiScale = "regular";
        this.fullscreen = true;

        this.soundVolume = 1.0;
        this.musicVolume = 1.0;

        this.theme = "light";
        this.refreshRate = "30";
        this.scrollWheelSensitivity = "regular";
        this.movementSpeed = "regular";
        this.language = "auto-detect";
        this.autosaveInterval = "two_minutes";

        this.alwaysMultiplace = false;
        this.offerHints = true;
        this.enableTunnelSmartplace = true;
        this.vignette = true;
        this.compactBuildingInfo = false;
        this.disableCutDeleteWarnings = false;
        this.rotationByBuilding = true;
        this.clearCursorOnDeleteWhilePlacing = true;
        this.displayChunkBorders = false;
        this.pickMinerOnPatch = true;
        this.enableMousePan = true;

        this.enableColorBlindHelper = false;

        this.lowQualityMapResources = false;
        this.disableTileGrid = false;
        this.lowQualityTextures = false;
        this.simplifiedBelts = false;
        this.zoomToCursor = true;
        this.mapResourcesScale = 0.5;

        /**
         * @type {Object.<string, number>}
         */
        this.keybindingOverrides = {};
    }
}

class ApplicationSettings extends _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_0__["ReadWriteProxy"] {
    constructor(app) {
        super(app, "app_settings.bin");
    }

    initialize() {
        // Read and directly write latest data back
        return this.readAsync()
            .then(() => {
                // Apply default setting callbacks
                const settings = this.getAllSettings();
                for (let i = 0; i < allApplicationSettings.length; ++i) {
                    const handle = allApplicationSettings[i];
                    handle.apply(this.app, settings[handle.id]);
                }
            })

            .then(() => this.writeAsync());
    }

    save() {
        return this.writeAsync();
    }

    // Getters

    /**
     * @returns {SettingsStorage}
     */
    getAllSettings() {
        return this.currentData.settings;
    }

    /**
     * @param {string} key
     */
    getSetting(key) {
        window.assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key);
        return this.getAllSettings()[key];
    }

    getInterfaceScaleId() {
        if (!this.currentData) {
            // Not initialized yet
            return "regular";
        }
        return this.getAllSettings().uiScale;
    }

    getDesiredFps() {
        return parseInt(this.getAllSettings().refreshRate);
    }

    getInterfaceScaleValue() {
        const id = this.getInterfaceScaleId();
        for (let i = 0; i < uiScales.length; ++i) {
            if (uiScales[i].id === id) {
                return uiScales[i].size;
            }
        }
        logger.error("Unknown ui scale id:", id);
        return 1;
    }

    getScrollWheelSensitivity() {
        const id = this.getAllSettings().scrollWheelSensitivity;
        for (let i = 0; i < scrollWheelSensitivities.length; ++i) {
            if (scrollWheelSensitivities[i].id === id) {
                return scrollWheelSensitivities[i].scale;
            }
        }
        logger.error("Unknown scroll wheel sensitivity id:", id);
        return 1;
    }

    getMovementSpeed() {
        const id = this.getAllSettings().movementSpeed;
        for (let i = 0; i < movementSpeeds.length; ++i) {
            if (movementSpeeds[i].id === id) {
                return movementSpeeds[i].multiplier;
            }
        }
        logger.error("Unknown movement speed id:", id);
        return 1;
    }

    getAutosaveIntervalSeconds() {
        const id = this.getAllSettings().autosaveInterval;
        for (let i = 0; i < autosaveIntervals.length; ++i) {
            if (autosaveIntervals[i].id === id) {
                return autosaveIntervals[i].seconds;
            }
        }
        logger.error("Unknown autosave interval id:", id);
        return 120;
    }

    getIsFullScreen() {
        return this.getAllSettings().fullscreen;
    }

    getKeybindingOverrides() {
        return this.getAllSettings().keybindingOverrides;
    }

    getLanguage() {
        return this.getAllSettings().language;
    }

    // Setters

    updateLanguage(id) {
        window.assert(_languages__WEBPACK_IMPORTED_MODULE_6__["LANGUAGES"][id], "Language not known: " + id);
        return this.updateSetting("language", id);
    }

    /**
     * @param {string} key
     * @param {string|boolean|number} value
     */
    updateSetting(key, value) {
        for (let i = 0; i < allApplicationSettings.length; ++i) {
            const setting = allApplicationSettings[i];
            if (setting.id === key) {
                if (!setting.validate(value)) {
                    window.assert(false, "Bad setting value: " + key);
                }
                this.getAllSettings()[key] = value;
                if (setting.changeCb) {
                    setting.changeCb(this.app, value);
                }
                return this.writeAsync();
            }
        }
        window.assert(false, "Unknown setting: " + key);
    }

    /**
     * Sets a new keybinding override
     * @param {string} keybindingId
     * @param {number} keyCode
     */
    updateKeybindingOverride(keybindingId, keyCode) {
        window.assert(Number.isInteger(keyCode), "Not a valid key code: " + keyCode);
        this.getAllSettings().keybindingOverrides[keybindingId] = keyCode;
        return this.writeAsync();
    }

    /**
     * Resets a given keybinding override
     * @param {string} id
     */
    resetKeybindingOverride(id) {
        delete this.getAllSettings().keybindingOverrides[id];
        return this.writeAsync();
    }
    /**
     * Resets all keybinding overrides
     */
    resetKeybindingOverrides() {
        this.getAllSettings().keybindingOverrides = {};
        return this.writeAsync();
    }

    // RW Proxy impl
    verify(data) {
        if (!data.settings) {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_3__["ExplainedResult"].bad("missing key 'settings'");
        }
        if (typeof data.settings !== "object") {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_3__["ExplainedResult"].bad("Bad settings object");
        }

        const settings = data.settings;
        for (let i = 0; i < allApplicationSettings.length; ++i) {
            const setting = allApplicationSettings[i];
            const storedValue = settings[setting.id];
            if (!setting.validate(storedValue)) {
                return _core_explained_result__WEBPACK_IMPORTED_MODULE_3__["ExplainedResult"].bad(
                    "Bad setting value for " +
                        setting.id +
                        ": " +
                        storedValue +
                        " @ settings version " +
                        data.version +
                        " (latest is " +
                        this.getCurrentVersion() +
                        ")"
                );
            }
        }
        return _core_explained_result__WEBPACK_IMPORTED_MODULE_3__["ExplainedResult"].good();
    }

    getDefaultData() {
        return {
            version: this.getCurrentVersion(),
            settings: new SettingsStorage(),
        };
    }

    getCurrentVersion() {
        return 30;
    }

    /** @param {{settings: SettingsStorage, version: number}} data */
    migrate(data) {
        // Simply reset before
        if (data.version < 5) {
            data.settings = new SettingsStorage();
            data.version = this.getCurrentVersion();
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_3__["ExplainedResult"].good();
        }

        if (data.version < 6) {
            data.settings.alwaysMultiplace = false;
            data.version = 6;
        }

        if (data.version < 7) {
            data.settings.offerHints = true;
            data.version = 7;
        }

        if (data.version < 8) {
            data.settings.scrollWheelSensitivity = "regular";
            data.version = 8;
        }

        if (data.version < 9) {
            data.settings.language = "auto-detect";
            data.version = 9;
        }

        if (data.version < 10) {
            data.settings.movementSpeed = "regular";
            data.version = 10;
        }

        if (data.version < 11) {
            data.settings.enableTunnelSmartplace = true;
            data.version = 11;
        }

        if (data.version < 12) {
            data.settings.vignette = true;
            data.version = 12;
        }

        if (data.version < 13) {
            data.settings.compactBuildingInfo = false;
            data.version = 13;
        }

        if (data.version < 14) {
            data.settings.disableCutDeleteWarnings = false;
            data.version = 14;
        }

        if (data.version < 15) {
            data.settings.autosaveInterval = "two_minutes";
            data.version = 15;
        }

        if (data.version < 16) {
            // RE-ENABLE this setting, it already existed
            data.settings.enableTunnelSmartplace = true;
            data.version = 16;
        }

        if (data.version < 17) {
            data.settings.enableColorBlindHelper = false;
            data.version = 17;
        }

        if (data.version < 18) {
            data.settings.rotationByBuilding = true;
            data.version = 18;
        }

        if (data.version < 19) {
            data.settings.lowQualityMapResources = false;
            data.version = 19;
        }

        if (data.version < 20) {
            data.settings.disableTileGrid = false;
            data.version = 20;
        }

        if (data.version < 21) {
            data.settings.lowQualityTextures = false;
            data.version = 21;
        }

        if (data.version < 22) {
            data.settings.clearCursorOnDeleteWhilePlacing = true;
            data.version = 22;
        }

        if (data.version < 23) {
            data.settings.displayChunkBorders = false;
            data.version = 23;
        }

        if (data.version < 24) {
            data.settings.refreshRate = "60";
        }

        if (data.version < 25) {
            data.settings.musicVolume = 0.5;
            data.settings.soundVolume = 0.5;

            // @ts-ignore
            delete data.settings.musicMuted;
            // @ts-ignore
            delete data.settings.soundsMuted;
            data.version = 25;
        }

        if (data.version < 26) {
            data.settings.pickMinerOnPatch = true;
            data.version = 26;
        }

        if (data.version < 27) {
            data.settings.simplifiedBelts = false;
            data.version = 27;
        }

        if (data.version < 28) {
            data.settings.enableMousePan = true;
            data.version = 28;
        }

        if (data.version < 29) {
            data.settings.zoomToCursor = true;
            data.version = 29;
        }

        if (data.version < 30) {
            data.settings.mapResourcesScale = 0.5;

            // Re-enable hints as well
            data.settings.offerHints = true;

            data.version = 30;
        }

        return _core_explained_result__WEBPACK_IMPORTED_MODULE_3__["ExplainedResult"].good();
    }
}


/***/ }),

/***/ "./src/js/profile/setting_types.js":
/*!*****************************************!*\
  !*** ./src/js/profile/setting_types.js ***!
  \*****************************************/
/*! exports provided: BaseSetting, EnumSetting, BoolSetting, RangeSetting */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSetting", function() { return BaseSetting; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EnumSetting", function() { return EnumSetting; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BoolSetting", function() { return BoolSetting; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RangeSetting", function() { return RangeSetting; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");




const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("setting_types");

/*
 * ***************************************************
 *
 *  LEGACY CODE WARNING
 *
 *  This is old code from yorg3.io and needs to be refactored
 *  @TODO
 *
 * ***************************************************
 */

class BaseSetting {
    /**
     *
     * @param {string} id
     * @param {string} categoryId
     * @param {function(Application, any):void} changeCb
     * @param {function(Application) : boolean=} enabledCb
     */
    constructor(id, categoryId, changeCb, enabledCb = null) {
        this.id = id;
        this.categoryId = categoryId;
        this.changeCb = changeCb;
        this.enabledCb = enabledCb;

        /** @type {Application} */
        this.app = null;

        this.element = null;
        this.dialogs = null;
    }

    /**
     * @param {Application} app
     * @param {any} value
     */
    apply(app, value) {
        if (this.changeCb) {
            this.changeCb(app, value);
        }
    }

    /**
     * Binds all parameters
     * @param {Application} app
     * @param {HTMLElement} element
     * @param {any} dialogs
     */
    bind(app, element, dialogs) {
        this.app = app;
        this.element = element;
        this.dialogs = dialogs;
    }

    /**
     * Returns the HTML for this setting
     * @param {Application} app
     */
    getHtml(app) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "";
    }

    /**
     * Returns whether this setting is enabled and available
     * @param {Application} app
     */
    getIsAvailable(app) {
        return this.enabledCb ? this.enabledCb(app) : true;
    }

    syncValueToElement() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Attempts to modify the setting
     */
    modify() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Shows the dialog that a restart is required
     */
    showRestartRequiredDialog() {
        const { restart } = this.dialogs.showInfo(
            _translations__WEBPACK_IMPORTED_MODULE_1__["T"].dialogs.restartRequired.title,
            _translations__WEBPACK_IMPORTED_MODULE_1__["T"].dialogs.restartRequired.text,
            this.app.platformWrapper.getSupportsRestart() ? ["later:grey", "restart:misc"] : ["ok:good"]
        );
        if (restart) {
            restart.add(() => this.app.platformWrapper.performRestart());
        }
    }

    /**
     * Validates the set value
     * @param {any} value
     * @returns {boolean}
     */
    validate(value) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return false;
    }
}

class EnumSetting extends BaseSetting {
    constructor(
        id,
        {
            options,
            valueGetter,
            textGetter,
            descGetter = null,
            category,
            restartRequired = true,
            iconPrefix = null,
            changeCb = null,
            magicValue = null,
            enabledCb = null,
        }
    ) {
        super(id, category, changeCb, enabledCb);

        this.options = options;
        this.valueGetter = valueGetter;
        this.textGetter = textGetter;
        this.descGetter = descGetter || (() => null);
        this.restartRequired = restartRequired;
        this.iconPrefix = iconPrefix;
        this.magicValue = magicValue;
    }

    /**
     * @param {Application} app
     */
    getHtml(app) {
        const available = this.getIsAvailable(app);
        return `
            <div class="setting cardbox ${available ? "enabled" : "disabled"}">
                ${available ? "" : `<span class="standaloneOnlyHint">${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].demo.settingNotAvailable}</span>`}
                <div class="row">
                    <label>${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].title}</label>
                    <div class="value enum" data-setting="${this.id}"></div>
                </div>
                <div class="desc">
                    ${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].description}
                </div>
            </div>`;
    }

    validate(value) {
        if (value === this.magicValue) {
            return true;
        }

        const availableValues = this.options.map(option => this.valueGetter(option));
        if (availableValues.indexOf(value) < 0) {
            logger.error(
                "Value '" + value + "' is not contained in available values:",
                availableValues,
                "of",
                this.id
            );
            return false;
        }
        return true;
    }

    syncValueToElement() {
        const value = this.app.settings.getSetting(this.id);
        let displayText = "???";
        const matchedInstance = this.options.find(data => this.valueGetter(data) === value);
        if (matchedInstance) {
            displayText = this.textGetter(matchedInstance);
        } else {
            logger.warn("Setting value", value, "not found for", this.id, "!");
        }
        this.element.innerText = displayText;
    }

    modify() {
        const { optionSelected } = this.dialogs.showOptionChooser(_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].title, {
            active: this.app.settings.getSetting(this.id),
            options: this.options.map(option => ({
                value: this.valueGetter(option),
                text: this.textGetter(option),
                desc: this.descGetter(option),
                iconPrefix: this.iconPrefix,
            })),
        });
        optionSelected.add(value => {
            this.app.settings.updateSetting(this.id, value);
            this.syncValueToElement();

            if (this.restartRequired) {
                this.showRestartRequiredDialog();
            }

            if (this.changeCb) {
                this.changeCb(this.app, value);
            }
        }, this);
    }
}

class BoolSetting extends BaseSetting {
    constructor(id, category, changeCb = null, enabledCb = null) {
        super(id, category, changeCb, enabledCb);
    }

    /**
     * @param {Application} app
     */
    getHtml(app) {
        const available = this.getIsAvailable(app);
        return `
        <div class="setting cardbox ${available ? "enabled" : "disabled"}">
            ${available ? "" : `<span class="standaloneOnlyHint">${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].demo.settingNotAvailable}</span>`}

            <div class="row">
                <label>${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].title}</label>
                <div class="value checkbox checked" data-setting="${this.id}">
                <span class="knob"></span>
                </div>
            </div>
            <div class="desc">
                ${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].description}
            </div>
        </div>`;
    }

    syncValueToElement() {
        const value = this.app.settings.getSetting(this.id);
        this.element.classList.toggle("checked", value);
    }

    modify() {
        const newValue = !this.app.settings.getSetting(this.id);
        this.app.settings.updateSetting(this.id, newValue);
        this.syncValueToElement();

        if (this.changeCb) {
            this.changeCb(this.app, newValue);
        }
    }

    validate(value) {
        return typeof value === "boolean";
    }
}

class RangeSetting extends BaseSetting {
    constructor(
        id,
        category,
        changeCb = null,
        defaultValue = 1.0,
        minValue = 0,
        maxValue = 1.0,
        stepSize = 0.0001,
        enabledCb = null
    ) {
        super(id, category, changeCb, enabledCb);

        this.defaultValue = defaultValue;
        this.minValue = minValue;
        this.maxValue = maxValue;
        this.stepSize = stepSize;
    }

    /**
     * @param {Application} app
     */
    getHtml(app) {
        const available = this.getIsAvailable(app);
        return `
        <div class="setting cardbox ${available ? "enabled" : "disabled"}">
            ${available ? "" : `<span class="standaloneOnlyHint">${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].demo.settingNotAvailable}</span>`}

            <div class="row">
                <label>${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].title}</label>
                <div class="value rangeInputContainer noPressEffect" data-setting="${this.id}">
                    <label>${this.defaultValue}</label>
                    <input class="rangeInput" type="range" value="${this.defaultValue}" min="${
            this.minValue
        }" max="${this.maxValue}" step="${this.stepSize}">
                </div>
            </div>
            <div class="desc">
                ${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.labels[this.id].description}
            </div>
        </div>`;
    }

    bind(app, element, dialogs) {
        this.app = app;
        this.element = element;
        this.dialogs = dialogs;

        this.getRangeInputElement().addEventListener("input", () => {
            this.updateLabels();
        });

        this.getRangeInputElement().addEventListener("change", () => {
            this.modify();
        });
    }

    syncValueToElement() {
        const value = this.app.settings.getSetting(this.id);
        this.setElementValue(value);
    }

    /**
     * Sets the elements value to the given value
     * @param {number} value
     */
    setElementValue(value) {
        const rangeInput = this.getRangeInputElement();
        const rangeLabel = this.element.querySelector("label");
        rangeInput.value = String(value);
        rangeLabel.innerHTML = _translations__WEBPACK_IMPORTED_MODULE_1__["T"].settings.rangeSliderPercentage.replace(
            "<amount>",
            String(Math.round(value * 100.0))
        );
    }

    updateLabels() {
        const value = Number(this.getRangeInputElement().value);
        this.setElementValue(value);
    }

    /**
     * @returns {HTMLInputElement}
     */
    getRangeInputElement() {
        return this.element.querySelector("input.rangeInput");
    }

    modify() {
        const rangeInput = this.getRangeInputElement();
        const newValue = Math.round(Number(rangeInput.value) * 100.0) / 100.0;
        this.app.settings.updateSetting(this.id, newValue);
        this.syncValueToElement();
        console.log("SET", newValue);
        if (this.changeCb) {
            this.changeCb(this.app, newValue);
        }
    }

    validate(value) {
        return typeof value === "number" && value >= this.minValue && value <= this.maxValue;
    }
}


/***/ }),

/***/ "./src/js/savegame/savegame.js":
/*!*************************************!*\
  !*** ./src/js/savegame/savegame.js ***!
  \*************************************/
/*! exports provided: Savegame */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Savegame", function() { return Savegame; });
/* harmony import */ var _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/read_write_proxy */ "./src/js/core/read_write_proxy.js");
/* harmony import */ var _core_explained_result__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/explained_result */ "./src/js/core/explained_result.js");
/* harmony import */ var _savegame_serializer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./savegame_serializer */ "./src/js/savegame/savegame_serializer.js");
/* harmony import */ var _savegame_interface__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./savegame_interface */ "./src/js/savegame/savegame_interface.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _savegame_interface_registry__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./savegame_interface_registry */ "./src/js/savegame/savegame_interface_registry.js");
/* harmony import */ var _schemas_1001__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./schemas/1001 */ "./src/js/savegame/schemas/1001.js");
/* harmony import */ var _schemas_1002__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./schemas/1002 */ "./src/js/savegame/schemas/1002.js");
/* harmony import */ var _schemas_1003__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./schemas/1003 */ "./src/js/savegame/schemas/1003.js");
/* harmony import */ var _schemas_1004__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./schemas/1004 */ "./src/js/savegame/schemas/1004.js");
/* harmony import */ var _schemas_1005__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./schemas/1005 */ "./src/js/savegame/schemas/1005.js");
/* harmony import */ var _schemas_1006__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./schemas/1006 */ "./src/js/savegame/schemas/1006.js");
/* harmony import */ var _schemas_1007__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./schemas/1007 */ "./src/js/savegame/schemas/1007.js");















const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_4__["createLogger"])("savegame");

/**
 * @typedef {import("../application").Application} Application
 * @typedef {import("../game/root").GameRoot} GameRoot
 * @typedef {import("./savegame_typedefs").SavegameData} SavegameData
 * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
 * @typedef {import("./savegame_typedefs").SavegameStats} SavegameStats
 * @typedef {import("./savegame_typedefs").SerializedGame} SerializedGame
 */

class Savegame extends _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_0__["ReadWriteProxy"] {
    /**
     *
     * @param {Application} app
     * @param {object} param0
     * @param {string} param0.internalId
     * @param {SavegameMetadata} param0.metaDataRef Handle to the meta data
     */
    constructor(app, { internalId, metaDataRef }) {
        super(app, "savegame-" + internalId + ".bin");
        this.internalId = internalId;
        this.metaDataRef = metaDataRef;

        /** @type {SavegameData} */
        this.currentData = this.getDefaultData();

        window.assert(
            _savegame_interface_registry__WEBPACK_IMPORTED_MODULE_6__["savegameInterfaces"][Savegame.getCurrentVersion()],
            "Savegame interface not defined: " + Savegame.getCurrentVersion()
        );
    }

    //////// RW Proxy Impl //////////

    /**
     * @returns {number}
     */
    static getCurrentVersion() {
        return 1007;
    }

    /**
     * @returns {typeof BaseSavegameInterface}
     */
    static getReaderClass() {
        return _savegame_interface_registry__WEBPACK_IMPORTED_MODULE_6__["savegameInterfaces"][Savegame.getCurrentVersion()];
    }

    /**
     * @returns {number}
     */
    getCurrentVersion() {
        return /** @type {typeof Savegame} */ (this.constructor).getCurrentVersion();
    }

    /**
     * Returns the savegames default data
     * @returns {SavegameData}
     */
    getDefaultData() {
        return {
            version: this.getCurrentVersion(),
            dump: null,
            stats: {},
            lastUpdate: Date.now(),
        };
    }

    /**
     * Migrates the savegames data
     * @param {SavegameData} data
     */
    migrate(data) {
        if (data.version < 1000) {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].bad("Can not migrate savegame, too old");
        }

        if (data.version === 1000) {
            _schemas_1001__WEBPACK_IMPORTED_MODULE_7__["SavegameInterface_V1001"].migrate1000to1001(data);
            data.version = 1001;
        }

        if (data.version === 1001) {
            _schemas_1002__WEBPACK_IMPORTED_MODULE_8__["SavegameInterface_V1002"].migrate1001to1002(data);
            data.version = 1002;
        }

        if (data.version === 1002) {
            _schemas_1003__WEBPACK_IMPORTED_MODULE_9__["SavegameInterface_V1003"].migrate1002to1003(data);
            data.version = 1003;
        }

        if (data.version === 1003) {
            _schemas_1004__WEBPACK_IMPORTED_MODULE_10__["SavegameInterface_V1004"].migrate1003to1004(data);
            data.version = 1004;
        }

        if (data.version === 1004) {
            _schemas_1005__WEBPACK_IMPORTED_MODULE_11__["SavegameInterface_V1005"].migrate1004to1005(data);
            data.version = 1005;
        }

        if (data.version === 1005) {
            _schemas_1006__WEBPACK_IMPORTED_MODULE_12__["SavegameInterface_V1006"].migrate1005to1006(data);
            data.version = 1006;
        }

        if (data.version === 1006) {
            _schemas_1007__WEBPACK_IMPORTED_MODULE_13__["SavegameInterface_V1007"].migrate1006to1007(data);
            data.version = 1007;
        }

        return _core_explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].good();
    }

    /**
     * Verifies the savegames data
     * @param {SavegameData} data
     */
    verify(data) {
        if (!data.dump) {
            // Well, guess that works
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].good();
        }

        if (!this.getDumpReaderForExternalData(data).validate()) {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].bad("dump-reader-failed-validation");
        }
        return _core_explained_result__WEBPACK_IMPORTED_MODULE_1__["ExplainedResult"].good();
    }

    //////// Subclasses interface  ////////

    /**
     * Returns if this game can be saved on disc
     * @returns {boolean}
     */
    isSaveable() {
        return true;
    }
    /**
     * Returns the statistics of the savegame
     * @returns {SavegameStats}
     */
    getStatistics() {
        return this.currentData.stats;
    }

    /**
     * Returns the *real* last update of the savegame, not the one of the metadata
     * which could also be the servers one
     */
    getRealLastUpdate() {
        return this.currentData.lastUpdate;
    }

    /**
     * Returns if this game has a serialized game dump
     */
    hasGameDump() {
        return !!this.currentData.dump && this.currentData.dump.entities.length > 0;
    }

    /**
     * Returns the current game dump
     * @returns {SerializedGame}
     */
    getCurrentDump() {
        return this.currentData.dump;
    }

    /**
     * Returns a reader to access the data
     * @returns {BaseSavegameInterface}
     */
    getDumpReader() {
        if (!this.currentData.dump) {
            logger.warn("Getting reader on null-savegame dump");
        }

        const cls = /** @type {typeof Savegame} */ (this.constructor).getReaderClass();
        return new cls(this.currentData);
    }

    /**
     * Returns a reader to access external data
     * @returns {BaseSavegameInterface}
     */
    getDumpReaderForExternalData(data) {
        window.assert(data.version, "External data contains no version");
        return Object(_savegame_interface_registry__WEBPACK_IMPORTED_MODULE_6__["getSavegameInterface"])(data);
    }

    ///////// Public Interface ///////////

    /**
     * Updates the last update field so we can send the savegame to the server,
     * WITHOUT Saving!
     */
    setLastUpdate(time) {
        this.currentData.lastUpdate = time;
    }

    /**
     *
     * @param {GameRoot} root
     */
    updateData(root) {
        // Construct a new serializer
        const serializer = new _savegame_serializer__WEBPACK_IMPORTED_MODULE_2__["SavegameSerializer"]();

        // let timer = performance.now();
        const dump = serializer.generateDumpFromGameRoot(root);
        if (!dump) {
            return false;
        }

        const shadowData = Object.assign({}, this.currentData);
        shadowData.dump = dump;
        shadowData.lastUpdate = new Date().getTime();
        shadowData.version = this.getCurrentVersion();

        const reader = this.getDumpReaderForExternalData(shadowData);

        // Validate (not in prod though)
        if (false) {}

        // Save data
        this.currentData = shadowData;
    }

    /**
     * Writes the savegame as well as its metadata
     */
    writeSavegameAndMetadata() {
        return this.writeAsync().then(() => this.saveMetadata());
    }

    /**
     * Updates the savegames metadata
     */
    saveMetadata() {
        this.metaDataRef.lastUpdate = new Date().getTime();
        this.metaDataRef.version = this.getCurrentVersion();
        if (!this.hasGameDump()) {
            this.metaDataRef.level = 0;
        } else {
            this.metaDataRef.level = this.currentData.dump.hubGoals.level;
        }

        return this.app.savegameMgr.writeAsync();
    }

    /**
     * @see ReadWriteProxy.writeAsync
     * @returns {Promise<any>}
     */
    writeAsync() {
        if (false) {}
        return super.writeAsync();
    }
}


/***/ }),

/***/ "./src/js/savegame/savegame_compressor.js":
/*!************************************************!*\
  !*** ./src/js/savegame/savegame_compressor.js ***!
  \************************************************/
/*! exports provided: compressObject, decompressObject */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "compressObject", function() { return compressObject; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "decompressObject", function() { return decompressObject; });
const charmap =
    "!#%&'()*+,-./:;<=>?@[]^_`{|}~¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿABCDEFGHIJKLMNOPQRSTUVWXYZ";

let compressionCache = {};
let decompressionCache = {};

/**
 * Compresses an integer into a tight string representation
 * @param {number} i
 * @returns {string}
 */
function compressInt(i) {
    // Zero value breaks
    i += 1;

    if (compressionCache[i]) {
        return compressionCache[i];
    }
    let result = "";
    do {
        result += charmap[i % charmap.length];
        i = Math.floor(i / charmap.length);
    } while (i > 0);
    return (compressionCache[i] = result);
}

/**
 * Decompresses an integer from its tight string representation
 * @param {string} s
 * @returns {number}
 */
function decompressInt(s) {
    if (decompressionCache[s]) {
        return decompressionCache[s];
    }
    s = "" + s;
    let result = 0;
    for (let i = s.length - 1; i >= 0; --i) {
        result = result * charmap.length + charmap.indexOf(s.charAt(i));
    }
    // Fixes zero value break fix from above
    result -= 1;
    return (decompressionCache[s] = result);
}

// Sanity
if (false) {}

/**
 * @param {any} obj
 * @param {Map} keys
 * @param {Map} values
 * @returns {any[]|object|number|string}
 */
function compressObjectInternal(obj, keys, values) {
    if (Array.isArray(obj)) {
        let result = [];
        for (let i = 0; i < obj.length; ++i) {
            result.push(compressObjectInternal(obj[i], keys, values));
        }
        return result;
    } else if (typeof obj === "object" && obj !== null) {
        let result = {};
        for (const key in obj) {
            let index = keys.get(key);
            if (index === undefined) {
                index = keys.size;
                keys.set(key, index);
            }
            const value = obj[key];
            result[compressInt(index)] = compressObjectInternal(value, keys, values);
        }
        return result;
    } else if (typeof obj === "string") {
        let index = values.get(obj);
        if (index === undefined) {
            index = values.size;
            values.set(obj, index);
        }
        return compressInt(index);
    }
    return obj;
}

/**
 * @param {Map} hashMap
 * @returns {Array}
 */
function indexMapToArray(hashMap) {
    const result = new Array(hashMap.size);
    hashMap.forEach((index, key) => {
        result[index] = key;
    });
    return result;
}

/**
 * @param {object} obj
 */
function compressObject(obj) {
    const keys = new Map();
    const values = new Map();
    const data = compressObjectInternal(obj, keys, values);
    return {
        keys: indexMapToArray(keys),
        values: indexMapToArray(values),
        data,
    };
}

/**
 * @param {object} obj
 * @param {string[]} keys
 * @param {any[]} values
 * @returns {object}
 */
function decompressObjectInternal(obj, keys = [], values = []) {
    if (Array.isArray(obj)) {
        let result = [];
        for (let i = 0; i < obj.length; ++i) {
            result.push(decompressObjectInternal(obj[i], keys, values));
        }
        return result;
    } else if (typeof obj === "object" && obj !== null) {
        let result = {};
        for (const key in obj) {
            const realIndex = decompressInt(key);
            const value = obj[key];
            result[keys[realIndex]] = decompressObjectInternal(value, keys, values);
        }
        return result;
    } else if (typeof obj === "string") {
        const realIndex = decompressInt(obj);
        return values[realIndex];
    }
    return obj;
}

/**
 * @param {object} obj
 */
function decompressObject(obj) {
    if (obj.keys && obj.values && obj.data) {
        const keys = obj.keys;
        const values = obj.values;
        const result = decompressObjectInternal(obj.data, keys, values);
        return result;
    }
    return obj;
}


/***/ }),

/***/ "./src/js/savegame/savegame_interface.js":
/*!***********************************************!*\
  !*** ./src/js/savegame/savegame_interface.js ***!
  \***********************************************/
/*! exports provided: BaseSavegameInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSavegameInterface", function() { return BaseSavegameInterface; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");


const Ajv = __webpack_require__(/*! ajv */ "./node_modules/ajv/lib/ajv.js");
const ajv = new Ajv({
    allErrors: false,
    uniqueItems: false,
    unicode: false,
    nullable: false,
});

const validators = {};

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("savegame_interface");

class BaseSavegameInterface {
    /**
     * Returns the interfaces version
     */
    getVersion() {
        throw new Error("Implement get version");
    }

    /**
     * Returns the uncached json schema
     * @returns {object}
     */
    getSchemaUncached() {
        throw new Error("Implement get schema");
    }

    getValidator() {
        const version = this.getVersion();
        if (validators[version]) {
            return validators[version];
        }
        logger.log("Compiling schema for savegame version", version);
        const schema = this.getSchemaUncached();
        try {
            validators[version] = ajv.compile(schema);
        } catch (ex) {
            logger.error("SCHEMA FOR", this.getVersion(), "IS INVALID!");
            logger.error(ex);
            throw new Error("Invalid schema for version " + version);
        }
        return validators[version];
    }

    /**
     * Constructs an new interface for the given savegame
     * @param {any} data
     */
    constructor(data) {
        this.data = data;
    }

    /**
     * Validates the data
     * @returns {boolean}
     */
    validate() {
        const validator = this.getValidator();

        if (!validator(this.data)) {
            logger.error(
                "Savegame failed validation! ErrorText:",
                ajv.errorsText(validator.errors),
                "RawErrors:",
                validator.errors
            );
            return false;
        }

        return true;
    }

    ///// INTERFACE (Override when the schema changes) /////

    /**
     * Returns the time of last update
     * @returns {number}
     */
    readLastUpdate() {
        return this.data.lastUpdate;
    }

    /**
     * Returns the ingame time in seconds
     * @returns {number}
     */
    readIngameTimeSeconds() {
        return this.data.dump.time.timeSeconds;
    }
}


/***/ }),

/***/ "./src/js/savegame/savegame_interface_registry.js":
/*!********************************************************!*\
  !*** ./src/js/savegame/savegame_interface_registry.js ***!
  \********************************************************/
/*! exports provided: savegameInterfaces, getSavegameInterface */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "savegameInterfaces", function() { return savegameInterfaces; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getSavegameInterface", function() { return getSavegameInterface; });
/* harmony import */ var _savegame_interface__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./savegame_interface */ "./src/js/savegame/savegame_interface.js");
/* harmony import */ var _schemas_1000__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./schemas/1000 */ "./src/js/savegame/schemas/1000.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _schemas_1001__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./schemas/1001 */ "./src/js/savegame/schemas/1001.js");
/* harmony import */ var _schemas_1002__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./schemas/1002 */ "./src/js/savegame/schemas/1002.js");
/* harmony import */ var _schemas_1003__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./schemas/1003 */ "./src/js/savegame/schemas/1003.js");
/* harmony import */ var _schemas_1004__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./schemas/1004 */ "./src/js/savegame/schemas/1004.js");
/* harmony import */ var _schemas_1005__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./schemas/1005 */ "./src/js/savegame/schemas/1005.js");
/* harmony import */ var _schemas_1006__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./schemas/1006 */ "./src/js/savegame/schemas/1006.js");
/* harmony import */ var _schemas_1007__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./schemas/1007 */ "./src/js/savegame/schemas/1007.js");











/** @type {Object.<number, typeof BaseSavegameInterface>} */
const savegameInterfaces = {
    1000: _schemas_1000__WEBPACK_IMPORTED_MODULE_1__["SavegameInterface_V1000"],
    1001: _schemas_1001__WEBPACK_IMPORTED_MODULE_3__["SavegameInterface_V1001"],
    1002: _schemas_1002__WEBPACK_IMPORTED_MODULE_4__["SavegameInterface_V1002"],
    1003: _schemas_1003__WEBPACK_IMPORTED_MODULE_5__["SavegameInterface_V1003"],
    1004: _schemas_1004__WEBPACK_IMPORTED_MODULE_6__["SavegameInterface_V1004"],
    1005: _schemas_1005__WEBPACK_IMPORTED_MODULE_7__["SavegameInterface_V1005"],
    1006: _schemas_1006__WEBPACK_IMPORTED_MODULE_8__["SavegameInterface_V1006"],
    1007: _schemas_1007__WEBPACK_IMPORTED_MODULE_9__["SavegameInterface_V1007"],
};

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("savegame_interface_registry");

/**
 * Returns if the given savegame has any supported interface
 * @param {any} savegame
 * @returns {BaseSavegameInterface|null}
 */
function getSavegameInterface(savegame) {
    if (!savegame || !savegame.version) {
        logger.warn("Savegame does not contain a valid version (undefined)");
        return null;
    }
    const version = savegame.version;
    if (!Number.isInteger(version)) {
        logger.warn("Savegame does not contain a valid version (non-integer):", version);
        return null;
    }

    const interfaceClass = savegameInterfaces[version];
    if (!interfaceClass) {
        logger.warn("Version", version, "has no implemented interface!");
        return null;
    }

    return new interfaceClass(savegame);
}


/***/ }),

/***/ "./src/js/savegame/savegame_manager.js":
/*!*********************************************!*\
  !*** ./src/js/savegame/savegame_manager.js ***!
  \*********************************************/
/*! exports provided: enumLocalSavegameStatus, SavegameManager */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enumLocalSavegameStatus", function() { return enumLocalSavegameStatus; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameManager", function() { return SavegameManager; });
/* harmony import */ var _core_explained_result__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/explained_result */ "./src/js/core/explained_result.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/read_write_proxy */ "./src/js/core/read_write_proxy.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _savegame__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./savegame */ "./src/js/savegame/savegame.js");





const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("savegame_manager");

const Rusha = __webpack_require__(/*! rusha */ "./node_modules/rusha/dist/rusha.js");

/**
 * @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData
 * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
 */

/** @enum {string} */
const enumLocalSavegameStatus = {
    offline: "offline",
    synced: "synced",
};

class SavegameManager extends _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_2__["ReadWriteProxy"] {
    constructor(app) {
        super(app, "savegames.bin");

        this.currentData = this.getDefaultData();
    }

    // RW Proxy Impl
    /**
     * @returns {SavegamesData}
     */
    getDefaultData() {
        return {
            version: this.getCurrentVersion(),
            savegames: [],
        };
    }

    getCurrentVersion() {
        return 1002;
    }

    verify(data) {
        // @TODO
        return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].good();
    }

    /**
     *
     * @param {SavegamesData} data
     */
    migrate(data) {
        if (data.version < 1001) {
            data.savegames.forEach(savegame => {
                savegame.level = 0;
            });
            data.version = 1001;
        }

        if (data.version < 1002) {
            data.savegames.forEach(savegame => {
                savegame.name = null;
            });
            data.version = 1002;
        }

        return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].good();
    }

    // End rw proxy

    /**
     * @returns {Array<SavegameMetadata>}
     */
    getSavegamesMetaData() {
        return this.currentData.savegames;
    }

    /**
     *
     * @param {string} internalId
     * @returns {Savegame}
     */
    getSavegameById(internalId) {
        const metadata = this.getGameMetaDataByInternalId(internalId);
        if (!metadata) {
            return null;
        }
        return new _savegame__WEBPACK_IMPORTED_MODULE_4__["Savegame"](this.app, { internalId, metaDataRef: metadata });
    }

    /**
     * Returns if this manager has any savegame of a 1.1.19 version, which
     * enables all levels
     */
    getHasAnyLegacySavegames() {
        return this.currentData.savegames.some(savegame => savegame.version === 1005 || savegame.level > 14);
    }

    /**
     * Deletes a savegame
     * @param {SavegameMetadata} game
     */
    deleteSavegame(game) {
        const handle = new _savegame__WEBPACK_IMPORTED_MODULE_4__["Savegame"](this.app, {
            internalId: game.internalId,
            metaDataRef: game,
        });

        return handle.deleteAsync().then(() => {
            for (let i = 0; i < this.currentData.savegames.length; ++i) {
                const potentialGame = this.currentData.savegames[i];
                if (potentialGame.internalId === handle.internalId) {
                    this.currentData.savegames.splice(i, 1);
                    break;
                }
            }

            return this.writeAsync();
        });
    }

    /**
     * Returns a given games metadata by id
     * @param {string} id
     * @returns {SavegameMetadata}
     */
    getGameMetaDataByInternalId(id) {
        for (let i = 0; i < this.currentData.savegames.length; ++i) {
            const data = this.currentData.savegames[i];
            if (data.internalId === id) {
                return data;
            }
        }
        logger.error("Savegame internal id not found:", id);
        return null;
    }

    /**
     * Creates a new savegame
     * @returns {Savegame}
     */
    createNewSavegame() {
        const id = this.generateInternalId();

        const metaData = /** @type {SavegameMetadata} */ ({
            lastUpdate: Date.now(),
            version: _savegame__WEBPACK_IMPORTED_MODULE_4__["Savegame"].getCurrentVersion(),
            internalId: id,
        });

        this.currentData.savegames.push(metaData);

        // Notice: This is async and happening in the background
        this.updateAfterSavegamesChanged();

        return new _savegame__WEBPACK_IMPORTED_MODULE_4__["Savegame"](this.app, {
            internalId: id,
            metaDataRef: metaData,
        });
    }

    /**
     * Attempts to import a savegame
     * @param {object} data
     */
    importSavegame(data) {
        const savegame = this.createNewSavegame();

        // Track legacy savegames
        const isOldSavegame = data.version < 1006;

        const migrationResult = savegame.migrate(data);
        if (migrationResult.isBad()) {
            return Promise.reject("Failed to migrate: " + migrationResult.reason);
        }

        savegame.currentData = data;
        const verification = savegame.verify(data);
        if (verification.isBad()) {
            return Promise.reject("Verification failed: " + verification.result);
        }

        return savegame
            .writeSavegameAndMetadata()
            .then(() => this.updateAfterSavegamesChanged())
            .then(() => this.app.restrictionMgr.onHasLegacySavegamesChanged(isOldSavegame));
    }

    /**
     * Hook after the savegames got changed
     */
    updateAfterSavegamesChanged() {
        return this.sortSavegames()
            .then(() => this.writeAsync())
            .then(() => this.app.restrictionMgr.onHasLegacySavegamesChanged(this.getHasAnyLegacySavegames()));
    }

    /**
     * Sorts all savegames by their creation time descending
     * @returns {Promise<any>}
     */
    sortSavegames() {
        this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate);
        let promiseChain = Promise.resolve();
        while (this.currentData.savegames.length > 30) {
            const toRemove = this.currentData.savegames.pop();

            // Try to remove the savegame since its no longer available
            const game = new _savegame__WEBPACK_IMPORTED_MODULE_4__["Savegame"](this.app, {
                internalId: toRemove.internalId,
                metaDataRef: toRemove,
            });
            promiseChain = promiseChain
                .then(() => game.deleteAsync())
                .then(
                    () => {},
                    err => {
                        logger.error(this, "Failed to remove old savegame:", toRemove, ":", err);
                    }
                );
        }

        return promiseChain;
    }

    /**
     * Helper method to generate a new internal savegame id
     */
    generateInternalId() {
        return Rusha.createHash()
            .update(Date.now() + "/" + Math.random())
            .digest("hex");
    }

    // End

    initialize() {
        // First read, then directly write to ensure we have the latest data
        // @ts-ignore
        return this.readAsync().then(() => {
            if (false) {}
            return this.updateAfterSavegamesChanged();
        });
    }
}


/***/ }),

/***/ "./src/js/savegame/savegame_serializer.js":
/*!************************************************!*\
  !*** ./src/js/savegame/savegame_serializer.js ***!
  \************************************************/
/*! exports provided: SavegameSerializer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameSerializer", function() { return SavegameSerializer; });
/* harmony import */ var _core_explained_result__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/explained_result */ "./src/js/core/explained_result.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_global_registries__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/global_registries */ "./src/js/core/global_registries.js");
/* harmony import */ var _serializer_internal__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./serializer_internal */ "./src/js/savegame/serializer_internal.js");





/**
 * @typedef {import("../game/component").Component} Component
 * @typedef {import("../game/component").StaticComponent} StaticComponent
 * @typedef {import("../game/entity").Entity} Entity
 * @typedef {import("../game/root").GameRoot} GameRoot
 * @typedef {import("../savegame/savegame_typedefs").SerializedGame} SerializedGame
 */

const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("savegame_serializer");

/**
 * Serializes a savegame
 */
class SavegameSerializer {
    constructor() {
        this.internal = new _serializer_internal__WEBPACK_IMPORTED_MODULE_3__["SerializerInternal"]();
    }

    /**
     * Serializes the game root into a dump
     * @param {GameRoot} root
     * @param {boolean=} sanityChecks Whether to check for validity
     * @returns {object}
     */
    generateDumpFromGameRoot(root, sanityChecks = true) {
        /** @type {SerializedGame} */
        const data = {
            camera: root.camera.serialize(),
            time: root.time.serialize(),
            map: root.map.serialize(),
            entityMgr: root.entityMgr.serialize(),
            hubGoals: root.hubGoals.serialize(),
            pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
            waypoints: root.hud.parts.waypoints.serialize(),
            entities: this.internal.serializeEntityArray(root.entityMgr.entities),
            beltPaths: root.systemMgr.systems.belt.serializePaths(),
        };

        if (false) {}
        return data;
    }

    /**
     * Verifies if there are logical errors in the savegame
     * @param {SerializedGame} savegame
     * @returns {ExplainedResult}
     */
    verifyLogicalErrors(savegame) {
        if (!savegame.entities) {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad("Savegame has no entities");
        }

        const seenUids = new Set();

        // Check for duplicate UIDS
        for (let i = 0; i < savegame.entities.length; ++i) {
            /** @type {Entity} */
            const entity = savegame.entities[i];

            const uid = entity.uid;
            if (!Number.isInteger(uid)) {
                return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad("Entity has invalid uid: " + uid);
            }
            if (seenUids.has(uid)) {
                return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad("Duplicate uid " + uid);
            }
            seenUids.add(uid);

            // Verify components
            if (!entity.components) {
                return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad("Entity is missing key 'components': " + JSON.stringify(entity));
            }

            const components = entity.components;
            for (const componentId in components) {
                const componentClass = _core_global_registries__WEBPACK_IMPORTED_MODULE_2__["gComponentRegistry"].findById(componentId);

                // Check component id is known
                if (!componentClass) {
                    return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad("Unknown component id: " + componentId);
                }

                // Verify component data
                const componentData = components[componentId];
                const componentVerifyError = /** @type {StaticComponent} */ (componentClass).verify(
                    componentData
                );

                // Check component data is ok
                if (componentVerifyError) {
                    return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad(
                        "Component " + componentId + " has invalid data: " + componentVerifyError
                    );
                }
            }
        }

        return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].good();
    }

    /**
     * Tries to load the savegame from a given dump
     * @param {SerializedGame} savegame
     * @param {GameRoot} root
     * @returns {ExplainedResult}
     */
    deserialize(savegame, root) {
        // Sanity
        const verifyResult = this.verifyLogicalErrors(savegame);
        if (!verifyResult.result) {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad(verifyResult.reason);
        }
        let errorReason = null;

        errorReason = errorReason || root.entityMgr.deserialize(savegame.entityMgr);
        errorReason = errorReason || root.time.deserialize(savegame.time);
        errorReason = errorReason || root.camera.deserialize(savegame.camera);
        errorReason = errorReason || root.map.deserialize(savegame.map);
        errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root);
        errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
        errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
        errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
        errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths);

        // Check for errors
        if (errorReason) {
            return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].bad(errorReason);
        }

        return _core_explained_result__WEBPACK_IMPORTED_MODULE_0__["ExplainedResult"].good();
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1000.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1000.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1000 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1000", function() { return SavegameInterface_V1000; });
/* harmony import */ var _savegame_interface_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../savegame_interface.js */ "./src/js/savegame/savegame_interface.js");


const schema = __webpack_require__(/*! ./1000.json */ "./src/js/savegame/schemas/1000.json");

class SavegameInterface_V1000 extends _savegame_interface_js__WEBPACK_IMPORTED_MODULE_0__["BaseSavegameInterface"] {
    getVersion() {
        return 1000;
    }

    getSchemaUncached() {
        return schema;
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1000.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1000.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1001.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1001.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1001 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1001", function() { return SavegameInterface_V1001; });
/* harmony import */ var _1000_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./1000.js */ "./src/js/savegame/schemas/1000.js");
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _translations_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../translations.js */ "./src/js/translations.js");
/* harmony import */ var _serialization_data_types_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../serialization_data_types.js */ "./src/js/savegame/serialization_data_types.js");





const schema = __webpack_require__(/*! ./1001.json */ "./src/js/savegame/schemas/1001.json");

const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("savegame_interface/1001");

class SavegameInterface_V1001 extends _1000_js__WEBPACK_IMPORTED_MODULE_0__["SavegameInterface_V1000"] {
    getVersion() {
        return 1001;
    }

    getSchemaUncached() {
        return schema;
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1000to1001(data) {
        logger.log("Migrating 1000 to 1001");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        dump.pinnedShapes = {
            shapes: [],
        };
        dump.waypoints = {
            waypoints: [
                {
                    label: _translations_js__WEBPACK_IMPORTED_MODULE_2__["T"].ingame.waypoints.hub,
                    center: { x: 0, y: 0 },
                    zoomLevel: 3,
                    deletable: false,
                },
            ],
        };

        const entities = dump.entities;
        for (let i = 0; i < entities.length; ++i) {
            const entity = entities[i];

            /**
             * @typedef {{
             *   origin: TypeVector,
             *   tileSize: TypeVector,
             *   rotation: TypeNumber,
             *   originalRotation: TypeNumber,
             *   spriteKey?: string,
             *   blueprintSpriteKey: string,
             *   silhouetteColor: string
             * }} OldStaticMapEntity
             */

            // Here we mock the old type of the StaticMapEntity before the change to using
            // a building ID based system (see building_codes.js) to stop the linter from
            // complaining that the type doesn't have the properties.
            // The ignored error is the error that the types do not overlap.  In the case
            // of a v1000 save though, the data will match the mocked type above.
            /** @type OldStaticMapEntity **/
            // @ts-ignore
            const staticComp = entity.components.StaticMapEntity;
            const beltComp = entity.components.Belt;
            if (staticComp) {
                if (staticComp.spriteKey) {
                    staticComp.blueprintSpriteKey = staticComp.spriteKey.replace(
                        "sprites/buildings",
                        "sprites/blueprints"
                    );
                } else {
                    if (entity.components.Hub) {
                        staticComp.blueprintSpriteKey = "";
                    } else if (beltComp) {
                        const direction = beltComp.direction;
                        staticComp.blueprintSpriteKey = "sprites/blueprints/belt_" + direction + ".png";
                    } else {
                        window.assert(false, "Could not deduct entity type for migrating 1000 -> 1001");
                    }
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1001.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1001.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1002.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1002.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1002 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1002", function() { return SavegameInterface_V1002; });
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _translations_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../translations.js */ "./src/js/translations.js");
/* harmony import */ var _1001_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./1001.js */ "./src/js/savegame/schemas/1001.js");




const schema = __webpack_require__(/*! ./1002.json */ "./src/js/savegame/schemas/1002.json");
const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("savegame_interface/1002");

class SavegameInterface_V1002 extends _1001_js__WEBPACK_IMPORTED_MODULE_2__["SavegameInterface_V1001"] {
    getVersion() {
        return 1002;
    }

    getSchemaUncached() {
        return schema;
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1001to1002(data) {
        logger.log("Migrating 1001 to 1002");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        const entities = dump.entities;
        for (let i = 0; i < entities.length; ++i) {
            const entity = entities[i];
            const beltComp = entity.components.Belt;
            const ejectorComp = entity.components.ItemEjector;

            if (beltComp && ejectorComp) {
                // @ts-ignore
                ejectorComp.instantEject = true;
            }
        }
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1002.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1002.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1003.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1003.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1003 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1003", function() { return SavegameInterface_V1003; });
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _1002_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./1002.js */ "./src/js/savegame/schemas/1002.js");



const schema = __webpack_require__(/*! ./1003.json */ "./src/js/savegame/schemas/1003.json");
const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("savegame_interface/1003");

class SavegameInterface_V1003 extends _1002_js__WEBPACK_IMPORTED_MODULE_1__["SavegameInterface_V1002"] {
    getVersion() {
        return 1003;
    }

    getSchemaUncached() {
        return schema;
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1002to1003(data) {
        logger.log("Migrating 1002 to 1003");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        dump.pinnedShapes = { shapes: [] };
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1003.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1003.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1004.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1004.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1004 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1004", function() { return SavegameInterface_V1004; });
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _1003_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./1003.js */ "./src/js/savegame/schemas/1003.js");



const schema = __webpack_require__(/*! ./1004.json */ "./src/js/savegame/schemas/1004.json");
const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("savegame_interface/1004");

class SavegameInterface_V1004 extends _1003_js__WEBPACK_IMPORTED_MODULE_1__["SavegameInterface_V1003"] {
    getVersion() {
        return 1004;
    }

    getSchemaUncached() {
        return schema;
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1003to1004(data) {
        logger.log("Migrating 1003 to 1004");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        // The hub simply has an empty label
        const waypointData = dump.waypoints.waypoints;
        for (let i = 0; i < waypointData.length; ++i) {
            const waypoint = waypointData[i];
            if (!waypoint.deletable) {
                waypoint.label = null;
            }
            delete waypoint.deletable;
        }
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1004.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1004.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1005.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1005.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1005 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1005", function() { return SavegameInterface_V1005; });
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _1004_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./1004.js */ "./src/js/savegame/schemas/1004.js");



const schema = __webpack_require__(/*! ./1005.json */ "./src/js/savegame/schemas/1005.json");
const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("savegame_interface/1005");

class SavegameInterface_V1005 extends _1004_js__WEBPACK_IMPORTED_MODULE_1__["SavegameInterface_V1004"] {
    getVersion() {
        return 1005;
    }

    getSchemaUncached() {
        return schema;
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1004to1005(data) {
        logger.log("Migrating 1004 to 1005");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        // just reset belt paths for now
        dump.beltPaths = [];

        const entities = dump.entities;

        // clear ejector slots
        for (let i = 0; i < entities.length; ++i) {
            const entity = entities[i];
            const itemEjector = entity.components.ItemEjector;
            if (itemEjector) {
                const slots = itemEjector.slots;
                for (let k = 0; k < slots.length; ++k) {
                    const slot = slots[k];
                    slot.item = null;
                    slot.progress = 0;
                }
            }
        }
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1005.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1005.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1006.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1006.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1006 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1006", function() { return SavegameInterface_V1006; });
/* harmony import */ var _core_global_registries_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/global_registries.js */ "./src/js/core/global_registries.js");
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _game_buildings_balancer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../game/buildings/balancer.js */ "./src/js/game/buildings/balancer.js");
/* harmony import */ var _game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../game/buildings/belt.js */ "./src/js/game/buildings/belt.js");
/* harmony import */ var _game_buildings_cutter_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../game/buildings/cutter.js */ "./src/js/game/buildings/cutter.js");
/* harmony import */ var _game_buildings_hub_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../game/buildings/hub.js */ "./src/js/game/buildings/hub.js");
/* harmony import */ var _game_buildings_miner_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../game/buildings/miner.js */ "./src/js/game/buildings/miner.js");
/* harmony import */ var _game_buildings_mixer_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../game/buildings/mixer.js */ "./src/js/game/buildings/mixer.js");
/* harmony import */ var _game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../game/buildings/painter.js */ "./src/js/game/buildings/painter.js");
/* harmony import */ var _game_buildings_rotater_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../../game/buildings/rotater.js */ "./src/js/game/buildings/rotater.js");
/* harmony import */ var _game_buildings_stacker_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../../game/buildings/stacker.js */ "./src/js/game/buildings/stacker.js");
/* harmony import */ var _game_buildings_storage_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../../game/buildings/storage.js */ "./src/js/game/buildings/storage.js");
/* harmony import */ var _game_buildings_trash_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../../game/buildings/trash.js */ "./src/js/game/buildings/trash.js");
/* harmony import */ var _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../../game/buildings/underground_belt.js */ "./src/js/game/buildings/underground_belt.js");
/* harmony import */ var _game_building_codes_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../../game/building_codes.js */ "./src/js/game/building_codes.js");
/* harmony import */ var _game_components_static_map_entity_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../../game/components/static_map_entity.js */ "./src/js/game/components/static_map_entity.js");
/* harmony import */ var _game_entity_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../../game/entity.js */ "./src/js/game/entity.js");
/* harmony import */ var _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../../game/meta_building.js */ "./src/js/game/meta_building.js");
/* harmony import */ var _1005_js__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./1005.js */ "./src/js/savegame/schemas/1005.js");




















const schema = __webpack_require__(/*! ./1006.json */ "./src/js/savegame/schemas/1006.json");
const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("savegame_interface/1006");

/**
 *
 * @param {typeof MetaBuilding} metaBuilding
 * @param {string=} variant
 * @param {number=} rotationVariant
 */
function findCode(metaBuilding, variant = _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], rotationVariant = 0) {
    return Object(_game_building_codes_js__WEBPACK_IMPORTED_MODULE_14__["getCodeFromBuildingData"])(_core_global_registries_js__WEBPACK_IMPORTED_MODULE_0__["gMetaBuildingRegistry"].findByClass(metaBuilding), variant, rotationVariant);
}

/**
 * Rebalances a value from the old balancing to the new one
 * @param {number} value
 * @returns {number}
 */
function rebalance(value) {
    return Math.round(Math.pow(value, 0.75));
}

class SavegameInterface_V1006 extends _1005_js__WEBPACK_IMPORTED_MODULE_18__["SavegameInterface_V1005"] {
    getVersion() {
        return 1006;
    }

    getSchemaUncached() {
        return schema;
    }

    static computeSpriteMapping() {
        return {
            // Belt
            "sprites/blueprints/belt_top.png": findCode(_game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__["MetaBeltBuilding"], _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], 0),
            "sprites/blueprints/belt_left.png": findCode(_game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__["MetaBeltBuilding"], _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], 1),
            "sprites/blueprints/belt_right.png": findCode(_game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__["MetaBeltBuilding"], _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], 2),

            // Splitter (=Balancer)
            "sprites/blueprints/splitter.png": findCode(_game_buildings_balancer_js__WEBPACK_IMPORTED_MODULE_2__["MetaBalancerBuilding"]),
            "sprites/blueprints/splitter-compact.png": findCode(
                _game_buildings_balancer_js__WEBPACK_IMPORTED_MODULE_2__["MetaBalancerBuilding"],
                _game_buildings_balancer_js__WEBPACK_IMPORTED_MODULE_2__["enumBalancerVariants"].merger
            ),
            "sprites/blueprints/splitter-compact-inverse.png": findCode(
                _game_buildings_balancer_js__WEBPACK_IMPORTED_MODULE_2__["MetaBalancerBuilding"],
                _game_buildings_balancer_js__WEBPACK_IMPORTED_MODULE_2__["enumBalancerVariants"].mergerInverse
            ),

            // Underground belt
            "sprites/blueprints/underground_belt_entry.png": findCode(
                _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__["MetaUndergroundBeltBuilding"],
                _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"],
                0
            ),
            "sprites/blueprints/underground_belt_exit.png": findCode(
                _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__["MetaUndergroundBeltBuilding"],
                _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"],
                1
            ),

            "sprites/blueprints/underground_belt_entry-tier2.png": findCode(
                _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__["MetaUndergroundBeltBuilding"],
                _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__["enumUndergroundBeltVariants"].tier2,
                0
            ),
            "sprites/blueprints/underground_belt_exit-tier2.png": findCode(
                _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__["MetaUndergroundBeltBuilding"],
                _game_buildings_underground_belt_js__WEBPACK_IMPORTED_MODULE_13__["enumUndergroundBeltVariants"].tier2,
                1
            ),

            // Miner
            "sprites/blueprints/miner.png": findCode(_game_buildings_miner_js__WEBPACK_IMPORTED_MODULE_6__["MetaMinerBuilding"]),
            "sprites/blueprints/miner-chainable.png": findCode(
                _game_buildings_miner_js__WEBPACK_IMPORTED_MODULE_6__["MetaMinerBuilding"],
                _game_buildings_miner_js__WEBPACK_IMPORTED_MODULE_6__["enumMinerVariants"].chainable,
                0
            ),

            // Cutter
            "sprites/blueprints/cutter.png": findCode(_game_buildings_cutter_js__WEBPACK_IMPORTED_MODULE_4__["MetaCutterBuilding"]),
            "sprites/blueprints/cutter-quad.png": findCode(_game_buildings_cutter_js__WEBPACK_IMPORTED_MODULE_4__["MetaCutterBuilding"], _game_buildings_cutter_js__WEBPACK_IMPORTED_MODULE_4__["enumCutterVariants"].quad),

            // Rotater
            "sprites/blueprints/rotater.png": findCode(_game_buildings_rotater_js__WEBPACK_IMPORTED_MODULE_9__["MetaRotaterBuilding"]),
            "sprites/blueprints/rotater-ccw.png": findCode(_game_buildings_rotater_js__WEBPACK_IMPORTED_MODULE_9__["MetaRotaterBuilding"], _game_buildings_rotater_js__WEBPACK_IMPORTED_MODULE_9__["enumRotaterVariants"].ccw),

            // Stacker
            "sprites/blueprints/stacker.png": findCode(_game_buildings_stacker_js__WEBPACK_IMPORTED_MODULE_10__["MetaStackerBuilding"]),

            // Mixer
            "sprites/blueprints/mixer.png": findCode(_game_buildings_mixer_js__WEBPACK_IMPORTED_MODULE_7__["MetaMixerBuilding"]),

            // Painter
            "sprites/blueprints/painter.png": findCode(_game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["MetaPainterBuilding"]),
            "sprites/blueprints/painter-mirrored.png": findCode(
                _game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["MetaPainterBuilding"],
                _game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["enumPainterVariants"].mirrored
            ),
            "sprites/blueprints/painter-double.png": findCode(
                _game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["MetaPainterBuilding"],
                _game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["enumPainterVariants"].double
            ),
            "sprites/blueprints/painter-quad.png": findCode(_game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["MetaPainterBuilding"], _game_buildings_painter_js__WEBPACK_IMPORTED_MODULE_8__["enumPainterVariants"].quad),

            // Trash
            "sprites/blueprints/trash.png": findCode(_game_buildings_trash_js__WEBPACK_IMPORTED_MODULE_12__["MetaTrashBuilding"]),

            // Storage
            "sprites/blueprints/trash-storage.png": findCode(_game_buildings_storage_js__WEBPACK_IMPORTED_MODULE_11__["MetaStorageBuilding"]),
        };
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1005to1006(data) {
        logger.log("Migrating 1005 to 1006");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        // Reduce stored shapes
        const stored = dump.hubGoals.storedShapes;
        for (const shapeKey in stored) {
            stored[shapeKey] = rebalance(stored[shapeKey]);
        }

        // Reset final game shape
        stored["RuCw--Cw:----Ru--"] = 0;

        // Reduce goals
        if (dump.hubGoals.currentGoal) {
            dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required);
        }

        let level = Math.min(19, dump.hubGoals.level);

        const levelMapping = {
            14: 15,
            15: 16,
            16: 17,
            17: 18,
            18: 19,
            19: 20,
        };

        dump.hubGoals.level = levelMapping[level] || level;

        // Update entities
        const entities = dump.entities;
        for (let i = 0; i < entities.length; ++i) {
            const entity = entities[i];
            const components = entity.components;
            this.migrateStaticComp1005to1006(entity);

            // HUB
            if (components.Hub) {
                // @ts-ignore
                components.Hub = {};
            }

            // Item Processor
            if (components.ItemProcessor) {
                // @ts-ignore
                components.ItemProcessor = {
                    nextOutputSlot: 0,
                };
            }

            // OLD: Unremovable component
            // @ts-ignore
            if (components.Unremovable) {
                // @ts-ignore
                delete components.Unremovable;
            }

            // OLD: ReplaceableMapEntity
            // @ts-ignore
            if (components.ReplaceableMapEntity) {
                // @ts-ignore
                delete components.ReplaceableMapEntity;
            }

            // ItemAcceptor
            if (components.ItemAcceptor) {
                // @ts-ignore
                components.ItemAcceptor = {};
            }

            // Belt
            if (components.Belt) {
                // @ts-ignore
                components.Belt = {};
            }

            // Item Ejector
            if (components.ItemEjector) {
                // @ts-ignore
                components.ItemEjector = {
                    slots: [],
                };
            }

            // UndergroundBelt
            if (components.UndergroundBelt) {
                // @ts-ignore
                components.UndergroundBelt = {
                    pendingItems: [],
                };
            }

            // Miner
            if (components.Miner) {
                // @ts-ignore
                delete components.Miner.chainable;

                components.Miner.lastMiningTime = 0;
                components.Miner.itemChainBuffer = [];
            }

            // Storage
            if (components.Storage) {
                // @ts-ignore
                components.Storage = {
                    storedCount: rebalance(components.Storage.storedCount),
                    storedItem: null,
                };
            }
        }
    }

    /**
     *
     * @param {Entity} entity
     */
    static migrateStaticComp1005to1006(entity) {
        const spriteMapping = this.computeSpriteMapping();
        const staticComp = entity.components.StaticMapEntity;

        /** @type {StaticMapEntityComponent} */
        const newStaticComp = {};
        newStaticComp.origin = staticComp.origin;
        newStaticComp.originalRotation = staticComp.originalRotation;
        newStaticComp.rotation = staticComp.rotation;

        // @ts-ignore
        newStaticComp.code = spriteMapping[staticComp.blueprintSpriteKey];

        // Hub special case
        if (entity.components.Hub) {
            newStaticComp.code = findCode(_game_buildings_hub_js__WEBPACK_IMPORTED_MODULE_5__["MetaHubBuilding"]);
        }

        // Belt special case
        if (entity.components.Belt) {
            const actualCode = {
                top: findCode(_game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__["MetaBeltBuilding"], _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], 0),
                left: findCode(_game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__["MetaBeltBuilding"], _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], 1),
                right: findCode(_game_buildings_belt_js__WEBPACK_IMPORTED_MODULE_3__["MetaBeltBuilding"], _game_meta_building_js__WEBPACK_IMPORTED_MODULE_17__["defaultBuildingVariant"], 2),
            }[entity.components.Belt.direction];
            if (actualCode !== newStaticComp.code) {
                if (false) {}
                newStaticComp.code = actualCode;
            }
        }

        if (!newStaticComp.code) {
            throw new Error(
                // @ts-ignore
                "1006 Migration: Could not reconstruct code for " + staticComp.blueprintSpriteKey
            );
        }

        entity.components.StaticMapEntity = newStaticComp;
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1006.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1006.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4WAUABFEEQC4E8AOBTGAuWB7ARgKxQGM4YAacaWAJxQEcBXASxoBMMIBtAXXMmhgCGLFoziMsAOwEAbAApUsqKmJQBndnCr0U4AL7ggA"));
    })()

/***/ }),

/***/ "./src/js/savegame/schemas/1007.js":
/*!*****************************************!*\
  !*** ./src/js/savegame/schemas/1007.js ***!
  \*****************************************/
/*! exports provided: SavegameInterface_V1007 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SavegameInterface_V1007", function() { return SavegameInterface_V1007; });
/* harmony import */ var _core_logging_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../core/logging.js */ "./src/js/core/logging.js");
/* harmony import */ var _1006_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./1006.js */ "./src/js/savegame/schemas/1006.js");



const schema = __webpack_require__(/*! ./1007.json */ "./src/js/savegame/schemas/1007.json");
const logger = Object(_core_logging_js__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("savegame_interface/1007");

class SavegameInterface_V1007 extends _1006_js__WEBPACK_IMPORTED_MODULE_1__["SavegameInterface_V1006"] {
    getVersion() {
        return 1007;
    }

    getSchemaUncached() {
        return schema;
    }

    /**
     * @param {import("../savegame_typedefs.js").SavegameData} data
     */
    static migrate1006to1007(data) {
        logger.log("Migrating 1006 to 1007");
        const dump = data.dump;
        if (!dump) {
            return true;
        }

        const waypoints = dump.waypoints.waypoints;

        // set waypoint layer to "regular"
        for (let i = 0; i < waypoints.length; ++i) {
            const waypoint = waypoints[i];
            waypoint.layer = "regular";
        }
    }
}


/***/ }),

/***/ "./src/js/savegame/schemas/1007.json":
/*!*******************************************!*\
  !*** ./src/js/savegame/schemas/1007.json ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = (function() {
        return JSON.parse(__webpack_require__(/*! global-compression */ "./src/js/core/lzstring.js").decompressX64("N4KABBYEQC4J4AcCmUBc0D2AjAVkgxjFADTiRQBOSAjgK4CWVAJmmANoC6pk0Ahk03ox6GAHa8ANgAUKGZBWFIAzqxgVaSEAF8QQA"));
    })()

/***/ }),

/***/ "./src/js/savegame/serialization.js":
/*!******************************************!*\
  !*** ./src/js/savegame/serialization.js ***!
  \******************************************/
/*! exports provided: types, BasicSerializableObject, serializeSchema, deserializeSchema, verifySchema, extendSchema */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "types", function() { return types; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BasicSerializableObject", function() { return BasicSerializableObject; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "serializeSchema", function() { return serializeSchema; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "deserializeSchema", function() { return deserializeSchema; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "verifySchema", function() { return verifySchema; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "extendSchema", function() { return extendSchema; });
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./serialization_data_types */ "./src/js/savegame/serialization_data_types.js");



const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_0__["createLogger"])("serialization");

// Schema declarations
const types = {
    int: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeInteger"](),
    uint: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypePositiveInteger"](),
    float: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeNumber"](),
    ufloat: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypePositiveNumber"](),
    string: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeString"](),
    entity: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeEntity"](),
    weakEntityRef: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeEntityWeakref"](),
    vector: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeVector"](),
    tileVector: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeVector"](),
    bool: new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeBoolean"](),

    /**
     * @param {BaseDataType} wrapped
     */
    nullable(wrapped) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeNullable"](wrapped);
    },

    /**
     * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry
     */
    classId(registry) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeClassId"](registry);
    },
    /**
     * @param {BaseDataType} valueType
     * @param {boolean=} includeEmptyValues
     */
    keyValueMap(valueType, includeEmptyValues = true) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeKeyValueMap"](valueType, includeEmptyValues);
    },

    /**
     * @param {Object<string, any>} values
     */
    enum(values) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeEnum"](values);
    },

    /**
     * @param {FactoryTemplate<*>} registry
     * @param {(GameRoot, any) => object=} resolver
     */
    obj(registry, resolver = null) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeClass"](registry, resolver);
    },

    /**
     * @param {FactoryTemplate<*>} registry
     */
    objData(registry) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeClassData"](registry);
    },

    /**
     * @param {typeof BasicSerializableObject} cls
     */
    knownType(cls) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeFixedClass"](cls);
    },

    /**
     * @param {BaseDataType} innerType
     */
    array(innerType) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeArray"](innerType);
    },

    /**
     * @param {BaseDataType} innerType
     */
    fixedSizeArray(innerType) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeArray"](innerType, true);
    },

    /**
     * @param {SingletonFactoryTemplate<*>} innerType
     */
    classRef(registry) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeMetaClass"](registry);
    },

    /**
     * @param {Object.<string, BaseDataType>} descriptor
     */
    structured(descriptor) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeStructuredObject"](descriptor);
    },

    /**
     * @param {BaseDataType} a
     * @param {BaseDataType} b
     */
    pair(a, b) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypePair"](a, b);
    },

    /**
     * @param {typeof BasicSerializableObject} classHandle
     * @param {SingletonFactoryTemplate<*>} registry
     */
    classWithMetaclass(classHandle, registry) {
        return new _serialization_data_types__WEBPACK_IMPORTED_MODULE_1__["TypeClassFromMetaclass"](classHandle, registry);
    },
};

/**
 * A full schema declaration
 * @typedef {Object.<string, BaseDataType>} Schema
 */

const globalSchemaCache = {};

/* dev:start */
const classnamesCache = {};
/* dev:end*/

class BasicSerializableObject {
    /* dev:start */
    /**
     * Fixes typeof DerivedComponent is not assignable to typeof Component, compiled out
     * in non-dev builds
     */
    constructor(...args) {}

    /* dev:end */

    static getId() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Should return the serialization schema
     * @returns {Schema}
     */
    static getSchema() {
        return {};
    }

    // Implementation
    /** @returns {Schema} */
    static getCachedSchema() {
        const id = this.getId();

        /* dev:start */
        window.assert(
            classnamesCache[id] === this || classnamesCache[id] === undefined,
            "Class name taken twice: " + id + " (from " + this.name + ")"
        );
        classnamesCache[id] = this;
        /* dev:end */

        const entry = globalSchemaCache[id];
        if (entry) {
            return entry;
        }

        const schema = this.getSchema();
        globalSchemaCache[id] = schema;
        return schema;
    }

    /** @returns {object} */
    serialize() {
        return serializeSchema(
            this,
            /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema()
        );
    }

    /**
     * @param {any} data
     * @param {import("./savegame_serializer").GameRoot} root
     * @returns {string|void}
     */
    deserialize(data, root = null) {
        return deserializeSchema(
            this,
            /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(),
            data,
            null,
            root
        );
    }

    /** @returns {string|void} */
    static verify(data) {
        return verifySchema(this.getCachedSchema(), data);
    }
}

/**
 * Serializes an object using the given schema, mergin with the given properties
 * @param {object} obj The object to serialize
 * @param {Schema} schema The schema to use
 * @param {object=} mergeWith Any additional properties to merge with the schema, useful for super calls
 * @returns {object} Serialized data object
 */
function serializeSchema(obj, schema, mergeWith = {}) {
    for (const key in schema) {
        if (!obj.hasOwnProperty(key)) {
            logger.error("Invalid schema, property", key, "does not exist on", obj, "(schema=", schema, ")");
            window.assert(
                obj.hasOwnProperty(key),
                "serialization: invalid schema, property does not exist on object: " + key
            );
        }
        if (!schema[key]) {
            window.assert(false, "Invalid schema (bad key '" + key + "'): " + JSON.stringify(schema));
        }

        if (false) {} else {
            mergeWith[key] = schema[key].serialize(obj[key]);
        }
    }
    return mergeWith;
}

/**
 * Deserializes data into an object
 * @param {object} obj The object to store the deserialized data into
 * @param {Schema} schema The schema to use
 * @param {object} data The serialized data
 * @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it
 * @param {import("../game/root").GameRoot=} root Optional game root reference
 * @returns {string|void} String error code or nothing on success
 */
function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) {
    if (baseclassErrorResult) {
        return baseclassErrorResult;
    }

    if (!data) {
        logger.error("Got 'NULL' data for", obj, "and schema", schema, "!");
        return "Got null data";
    }

    for (const key in schema) {
        if (!data.hasOwnProperty(key)) {
            logger.error("Data", data, "does not contain", key, "(schema:", schema, ")");
            return "Missing key in schema: " + key + " of class " + obj.constructor.name;
        }
        if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) {
            logger.error("Data", data, "has null value for", key, "(schema:", schema, ")");
            return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name;
        }

        const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root || root);
        if (errorStatus) {
            logger.error(
                "Deserialization failed with error '" + errorStatus + "' on object",
                obj,
                "and key",
                key,
                "(root? =",
                obj.root ? "y" : "n",
                ")"
            );
            return errorStatus;
        }
    }
}

/**
 * Verifies stored data using the given schema
 * @param {Schema} schema The schema to use
 * @param {object} data The data to verify
 * @returns {string|void} String error code or nothing on success
 */
function verifySchema(schema, data) {
    for (const key in schema) {
        if (!data.hasOwnProperty(key)) {
            logger.error("Data", data, "does not contain", key, "(schema:", schema, ")");
            return "verify: missing key required by schema in stored data: " + key;
        }
        if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) {
            logger.error("Data", data, "has null value for", key, "(schema:", schema, ")");
            return "verify: non-nullable entry is null: " + key;
        }

        const errorStatus = schema[key].verifySerializedValue(data[key]);
        if (errorStatus) {
            logger.error(errorStatus);
            return "verify: " + errorStatus;
        }
    }
}

/**
 * Extends a schema by adding the properties from the new schema to the existing base schema
 * @param {Schema} base
 * @param {Schema} newOne
 * @returns {Schema}
 */
function extendSchema(base, newOne) {
    /** @type {Schema} */
    const result = Object.assign({}, base);
    for (const key in newOne) {
        if (result.hasOwnProperty(key)) {
            logger.error("Extend schema got duplicate key:", key);
            continue;
        }
        result[key] = newOne[key];
    }
    return result;
}


/***/ }),

/***/ "./src/js/savegame/serialization_data_types.js":
/*!*****************************************************!*\
  !*** ./src/js/savegame/serialization_data_types.js ***!
  \*****************************************************/
/*! exports provided: globalJsonSchemaDefs, schemaToJsonSchema, BaseDataType, TypeInteger, TypePositiveInteger, TypeBoolean, TypeString, TypeVector, TypeTileVector, TypeNumber, TypePositiveNumber, TypeEnum, TypeEntity, TypeEntityWeakref, TypeClass, TypeClassData, TypeClassFromMetaclass, TypeMetaClass, TypeArray, TypeFixedClass, TypeKeyValueMap, TypeClassId, TypePair, TypeNullable, TypeStructuredObject */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "globalJsonSchemaDefs", function() { return globalJsonSchemaDefs; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "schemaToJsonSchema", function() { return schemaToJsonSchema; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseDataType", function() { return BaseDataType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeInteger", function() { return TypeInteger; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypePositiveInteger", function() { return TypePositiveInteger; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeBoolean", function() { return TypeBoolean; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeString", function() { return TypeString; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeVector", function() { return TypeVector; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeTileVector", function() { return TypeTileVector; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeNumber", function() { return TypeNumber; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypePositiveNumber", function() { return TypePositiveNumber; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeEnum", function() { return TypeEnum; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeEntity", function() { return TypeEntity; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeEntityWeakref", function() { return TypeEntityWeakref; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeClass", function() { return TypeClass; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeClassData", function() { return TypeClassData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeClassFromMetaclass", function() { return TypeClassFromMetaclass; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeMetaClass", function() { return TypeMetaClass; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeArray", function() { return TypeArray; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeFixedClass", function() { return TypeFixedClass; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeKeyValueMap", function() { return TypeKeyValueMap; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeClassId", function() { return TypeClassId; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypePair", function() { return TypePair; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeNullable", function() { return TypeNullable; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeStructuredObject", function() { return TypeStructuredObject; });
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");




const globalJsonSchemaDefs = {};

/**
 *
 * @param {import("./serialization").Schema} schema
 */
function schemaToJsonSchema(schema) {
    const jsonSchema = {
        type: "object",
        additionalProperties: false,
        required: [],
        properties: {},
    };

    for (const key in schema) {
        const subSchema = schema[key].getAsJsonSchema();
        jsonSchema.required.push(key);
        jsonSchema.properties[key] = subSchema;
    }

    return jsonSchema;
}

/**
 * Helper function to create a json schema object
 * @param {any} properties
 */
function schemaObject(properties) {
    return {
        type: "object",
        required: Object.keys(properties).slice(),
        additionalProperties: false,
        properties,
    };
}

/**
 * Base serialization data type
 */
class BaseDataType {
    /**
     * Serializes a given raw value
     * @param {any} value
     */
    serialize(value) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return {};
    }

    /**
     * Verifies a given serialized value
     * @param {any} value
     * @returns {string|void} String error code or null on success
     */
    verifySerializedValue(value) {}

    /**
     * Deserializes a serialized value into the target object under the given key
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Returns the json schema
     */
    getAsJsonSchema() {
        const key = this.getCacheKey();
        const schema = this.getAsJsonSchemaUncached();

        if (!globalJsonSchemaDefs[key]) {
            // schema.$id = key;
            globalJsonSchemaDefs[key] = schema;
        }

        return {
            $ref: "#/definitions/" + key,
        };
    }

    /**
     * INTERNAL Should return the json schema representation
     */
    getAsJsonSchemaUncached() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
    }

    /**
     * Returns whether null values are okay
     * @returns {boolean}
     */
    allowNull() {
        return false;
    }

    // Helper methods

    /**
     * Deserializes a serialized value, but performs integrity checks before
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserializeWithVerify(value, targetObject, targetKey, root) {
        const errorCode = this.verifySerializedValue(value);
        if (errorCode) {
            return (
                "serialization verify failed: " +
                errorCode +
                " [value " +
                (JSON.stringify(value) || "").substr(0, 100) +
                "]"
            );
        }
        return this.deserialize(value, targetObject, targetKey, root);
    }

    /**
     * Should return a cacheable key
     */
    getCacheKey() {
        window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));;
        return "";
    }
}

class TypeInteger extends BaseDataType {
    serialize(value) {
        window.assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value);
        return value;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "integer",
        };
    }

    verifySerializedValue(value) {
        if (!Number.isInteger(value)) {
            return "Not a valid number";
        }
    }

    getCacheKey() {
        return "int";
    }
}

class TypePositiveInteger extends BaseDataType {
    serialize(value) {
        window.assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value);
        window.assert(value >= 0, "value < 0: " + value);
        return value;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "integer",
            minimum: 0,
        };
    }

    verifySerializedValue(value) {
        if (!Number.isInteger(value)) {
            return "Not a valid number";
        }
        if (value < 0) {
            return "Negative value for positive integer";
        }
    }

    getCacheKey() {
        return "uint";
    }
}

class TypeBoolean extends BaseDataType {
    serialize(value) {
        window.assert(value === true || value === false, "Type bool got non bool for serialize: " + value);
        return value;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "boolean",
        };
    }

    verifySerializedValue(value) {
        if (value !== true && value !== false) {
            return "Not a boolean";
        }
    }

    getCacheKey() {
        return "bool";
    }
}

class TypeString extends BaseDataType {
    serialize(value) {
        window.assert(typeof value === "string", "Type string got non string for serialize: " + value);
        return value;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }
    getAsJsonSchemaUncached() {
        return {
            type: "string",
        };
    }

    verifySerializedValue(value) {
        if (typeof value !== "string") {
            return "Not a valid string";
        }
    }

    getCacheKey() {
        return "string";
    }
}

class TypeVector extends BaseDataType {
    serialize(value) {
        window.assert(value instanceof _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"], "Type vector got non vector for serialize: " + value);
        return {
            x: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round4Digits"])(value.x),
            y: Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round4Digits"])(value.y),
        };
    }

    getAsJsonSchemaUncached() {
        return schemaObject({
            x: {
                type: "number",
            },
            y: {
                type: "number",
            },
        });
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](value.x, value.y);
    }

    verifySerializedValue(value) {
        if (!Number.isFinite(value.x) || !Number.isFinite(value.y)) {
            return "Not a valid vector, missing x/y or bad data type";
        }
    }

    getCacheKey() {
        return "vector";
    }
}

class TypeTileVector extends BaseDataType {
    serialize(value) {
        window.assert(value instanceof _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"], "Type vector got non vector for serialize: " + value);
        window.assert(Number.isInteger(value.x) && value.x > 0, "Invalid tile x:" + value.x);
        window.assert(Number.isInteger(value.y) && value.y > 0, "Invalid tile x:" + value.y);
        return { x: value.x, y: value.y };
    }

    getAsJsonSchemaUncached() {
        return schemaObject({
            x: {
                type: "integer",
                minimum: 0,
                maximum: 256,
            },
            y: {
                type: "integer",
                minimum: 0,
                maximum: 256,
            },
        });
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = new _core_vector__WEBPACK_IMPORTED_MODULE_0__["Vector"](value.x, value.y);
    }

    verifySerializedValue(value) {
        if (!Number.isInteger(value.x) || !Number.isInteger(value.y)) {
            return "Not a valid tile vector, missing x/y or bad data type";
        }
        if (value.x < 0 || value.y < 0) {
            return "Invalid tile vector, x or y < 0";
        }
    }

    getCacheKey() {
        return "tilevector";
    }
}

class TypeNumber extends BaseDataType {
    serialize(value) {
        window.assert(Number.isFinite(value), "Type number got non number for serialize: " + value);
        window.assert(!Number.isNaN(value), "Value is nan: " + value);
        return Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round4Digits"])(value);
    }

    getAsJsonSchemaUncached() {
        return {
            type: "number",
        };
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    verifySerializedValue(value) {
        if (!Number.isFinite(value)) {
            return "Not a valid number: " + value;
        }
    }

    getCacheKey() {
        return "float";
    }
}

class TypePositiveNumber extends BaseDataType {
    serialize(value) {
        window.assert(Number.isFinite(value), "Type number got non number for serialize: " + value);
        window.assert(value >= 0, "Postitive number got negative value: " + value);
        return Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["round4Digits"])(value);
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "number",
            minimum: 0,
        };
    }

    verifySerializedValue(value) {
        if (!Number.isFinite(value)) {
            return "Not a valid number: " + value;
        }
        if (value < 0) {
            return "Positive number got negative value: " + value;
        }
    }

    getCacheKey() {
        return "ufloat";
    }
}

class TypeEnum extends BaseDataType {
    /**
     * @param {Object.<string, any>} enumeration
     */
    constructor(enumeration = {}) {
        super();
        this.availableValues = Object.values(enumeration);
    }

    serialize(value) {
        window.assert(this.availableValues.indexOf(value) >= 0, "Unknown value: " + value);
        return value;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "string",
            enum: this.availableValues,
        };
    }

    verifySerializedValue(value) {
        if (this.availableValues.indexOf(value) < 0) {
            return "Unknown enum value: " + value;
        }
    }

    getCacheKey() {
        return "enum." + this.availableValues.join(",");
    }
}

class TypeEntity extends BaseDataType {
    serialize(value) {
        // assert(value instanceof Entity, "Not a valid entity ref: " + value);
        window.assert(value.uid, "Entity has no uid yet");
        window.assert(!value.destroyed, "Entity already destroyed");
        window.assert(!value.queuedForDestroy, "Entity queued for destroy");

        return value.uid;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "integer",
            minimum: 0,
        };
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        const entity = root.entityMgr.findByUid(value);
        if (!entity) {
            return "Entity not found by uid: " + value;
        }
        targetObject[targetKey] = entity;
    }

    verifySerializedValue(value) {
        if (!Number.isFinite(value)) {
            return "Not a valid uuid: " + value;
        }
    }

    getCacheKey() {
        return "entity";
    }
}

class TypeEntityWeakref extends BaseDataType {
    serialize(value) {
        if (value === null) {
            return null;
        }

        // assert(value instanceof Entity, "Not a valid entity ref (weak): " + value);
        window.assert(value.uid, "Entity has no uid yet");
        if (value.destroyed || value.queuedForDestroy) {
            return null;
        }
        return value.uid;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        if (value === null) {
            targetObject[targetKey] = null;
            return;
        }
        const entity = root.entityMgr.findByUid(value, false);
        targetObject[targetKey] = entity;
    }

    getAsJsonSchemaUncached() {
        return {
            type: ["null", "integer"],
            minimum: 0,
        };
    }

    allowNull() {
        return true;
    }

    verifySerializedValue(value) {
        if (value !== null && !Number.isFinite(value)) {
            return "Not a valid uuid: " + value;
        }
    }

    getCacheKey() {
        return "entity-weakref";
    }
}

class TypeClass extends BaseDataType {
    /**
     *
     * @param {FactoryTemplate<*>} registry
     * @param {(GameRoot, object) => object} customResolver
     */
    constructor(registry, customResolver = null) {
        super();
        this.registry = registry;
        this.customResolver = customResolver;
    }

    serialize(value) {
        window.assert(typeof value === "object", "Not a class instance: " + value);
        return {
            $: value.constructor.getId(),
            data: value.serialize(),
        };
    }

    getAsJsonSchemaUncached() {
        const options = [];
        const entries = this.registry.getEntries();
        for (let i = 0; i < entries.length; ++i) {
            const entry = entries[i];

            options.push(
                schemaObject({
                    $: {
                        type: "string",
                        // @ts-ignore
                        enum: [entry.getId()],
                    },
                    // @ts-ignore
                    data: schemaToJsonSchema(entry.getCachedSchema()),
                })
            );
        }

        return { oneOf: options };
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        let instance;

        if (this.customResolver) {
            instance = this.customResolver(root, value);
            if (!instance) {
                return "Failed to call custom resolver";
            }
        } else {
            const instanceClass = this.registry.findById(value.$);
            if (!instanceClass || !instanceClass.prototype) {
                return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass;
            }
            instance = Object.create(instanceClass.prototype);
            const errorState = instance.deserialize(value.data);
            if (errorState) {
                return errorState;
            }
        }
        targetObject[targetKey] = instance;
    }

    verifySerializedValue(value) {
        if (!value) {
            return "Got null data";
        }

        if (!this.registry.hasId(value.$)) {
            return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")";
        }
    }

    getCacheKey() {
        return "class." + this.registry.getId();
    }
}

class TypeClassData extends BaseDataType {
    /**
     *
     * @param {FactoryTemplate<*>} registry
     */
    constructor(registry) {
        super();
        this.registry = registry;
    }

    serialize(value) {
        window.assert(typeof value === "object", "Not a class instance: " + value);
        return value.serialize();
    }

    getAsJsonSchemaUncached() {
        const options = [];
        const entries = this.registry.getEntries();
        for (let i = 0; i < entries.length; ++i) {
            const entry = entries[i];
            options.push(
                schemaToJsonSchema(/** @type {typeof BasicSerializableObject} */ (entry).getCachedSchema())
            );
        }
        return { oneOf: options };
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        window.assert(false, "can not deserialize class data of type " + this.registry.getId());
    }

    verifySerializedValue(value) {
        if (!value) {
            return "Got null data";
        }
    }

    getCacheKey() {
        return "class." + this.registry.getId();
    }
}

class TypeClassFromMetaclass extends BaseDataType {
    /**
     *
     * @param {typeof BasicSerializableObject} classHandle
     * @param {SingletonFactoryTemplate<*>} registry
     */
    constructor(classHandle, registry) {
        super();
        this.registry = registry;
        this.classHandle = classHandle;
    }

    serialize(value) {
        window.assert(typeof value === "object", "Not a class instance: " + value);
        return {
            $: value.getMetaclass().getId(),
            data: value.serialize(),
        };
    }

    getAsJsonSchemaUncached() {
        // const options = [];
        const ids = this.registry.getAllIds();

        return {
            $: {
                type: "string",
                enum: ids,
            },
            data: schemaToJsonSchema(this.classHandle.getCachedSchema()),
        };
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        const metaClassInstance = this.registry.findById(value.$);
        if (!metaClassInstance || !metaClassInstance.prototype) {
            return "Invalid meta class id (runtime-err): " + value.$ + "->" + metaClassInstance;
        }

        const instanceClass = metaClassInstance.getInstanceClass();
        const instance = Object.create(instanceClass.prototype);
        const errorState = instance.deserialize(value.data);
        if (errorState) {
            return errorState;
        }
        targetObject[targetKey] = instance;
    }

    verifySerializedValue(value) {
        if (!value) {
            return "Got null data";
        }

        if (!this.registry.hasId(value.$)) {
            return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")";
        }
    }

    getCacheKey() {
        return "classofmetaclass." + this.registry.getId();
    }
}

class TypeMetaClass extends BaseDataType {
    /**
     *
     * @param {SingletonFactoryTemplate<*>} registry
     */
    constructor(registry) {
        super();
        this.registry = registry;
    }

    serialize(value) {
        return value.getId();
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        const instanceClass = this.registry.findById(value);
        if (!instanceClass) {
            return "Invalid class id (runtime-err): " + value;
        }
        targetObject[targetKey] = instanceClass;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "string",
            enum: this.registry.getAllIds(),
        };
    }

    verifySerializedValue(value) {
        if (!value) {
            return "Got null data";
        }

        if (typeof value !== "string") {
            return "Got non string data";
        }

        if (!this.registry.hasId(value)) {
            return "Invalid class id: " + value + " (factory is " + this.registry.getId() + ")";
        }
    }

    getCacheKey() {
        return "metaclass." + this.registry.getId();
    }
}

class TypeArray extends BaseDataType {
    /**
     * @param {BaseDataType} innerType
     */
    constructor(innerType, fixedSize = false) {
        super();
        this.fixedSize = fixedSize;
        this.innerType = innerType;
    }

    serialize(value) {
        window.assert(Array.isArray(value), "Not an array");
        const result = new Array(value.length);
        for (let i = 0; i < value.length; ++i) {
            result[i] = this.innerType.serialize(value[i]);
        }
        return result;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        let destination = targetObject[targetKey];
        if (!destination) {
            targetObject[targetKey] = destination = new Array(value.length);
        }

        const size = this.fixedSize ? Math.min(value.length, destination.length) : value.length;

        for (let i = 0; i < size; ++i) {
            const errorStatus = this.innerType.deserializeWithVerify(value[i], destination, i, root);
            if (errorStatus) {
                return errorStatus;
            }
        }
    }

    getAsJsonSchemaUncached() {
        return {
            type: "array",
            items: this.innerType.getAsJsonSchema(),
        };
    }

    verifySerializedValue(value) {
        if (!Array.isArray(value)) {
            return "Not an array: " + value;
        }
    }

    getCacheKey() {
        return "array." + this.innerType.getCacheKey();
    }
}

class TypeFixedClass extends BaseDataType {
    /**
     *
     * @param {typeof BasicSerializableObject} baseclass
     */
    constructor(baseclass) {
        super();
        this.baseclass = baseclass;
    }

    serialize(value) {
        window.assert(value instanceof this.baseclass, "Not a valid class instance");
        return value.serialize();
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        const instance = Object.create(this.baseclass.prototype);
        const errorState = instance.deserialize(value);
        if (errorState) {
            return "Failed to deserialize class: " + errorState;
        }
        targetObject[targetKey] = instance;
    }

    getAsJsonSchemaUncached() {
        this.baseclass.getSchema();
        this.baseclass.getCachedSchema();
        return schemaToJsonSchema(this.baseclass.getCachedSchema());
    }

    verifySerializedValue(value) {
        if (!value) {
            return "Got null data";
        }
    }

    getCacheKey() {
        return "fixedclass." + this.baseclass.getId();
    }
}

class TypeKeyValueMap extends BaseDataType {
    /**
     * @param {BaseDataType} valueType
     * @param {boolean=} includeEmptyValues
     */
    constructor(valueType, includeEmptyValues = true) {
        super();
        this.valueType = valueType;
        this.includeEmptyValues = includeEmptyValues;
    }

    serialize(value) {
        window.assert(typeof value === "object", "not an object");
        let result = {};
        for (const key in value) {
            const serialized = this.valueType.serialize(value[key]);
            if (!this.includeEmptyValues && typeof serialized === "object") {
                if (
                    serialized.$ &&
                    typeof serialized.data === "object" &&
                    Object.keys(serialized.data).length === 0
                ) {
                    continue;
                } else if (Object.keys(serialized).length === 0) {
                    continue;
                }
            }

            result[key] = serialized;
        }
        return result;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        let result = {};
        for (const key in value) {
            const errorCode = this.valueType.deserializeWithVerify(value[key], result, key, root);
            if (errorCode) {
                return errorCode;
            }
        }
        targetObject[targetKey] = result;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "object",
            additionalProperties: this.valueType.getAsJsonSchema(),
        };
    }

    verifySerializedValue(value) {
        if (typeof value !== "object") {
            return "KV map is not an object";
        }
    }

    getCacheKey() {
        return "kvmap." + this.valueType.getCacheKey();
    }
}

class TypeClassId extends BaseDataType {
    /**
     * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry
     */
    constructor(registry) {
        super();
        this.registry = registry;
    }

    serialize(value) {
        window.assert(typeof value === "string", "Not a valid string");
        window.assert(this.registry.hasId(value), "Id " + value + " not found in registry");
        return value;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        targetObject[targetKey] = value;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "string",
            enum: this.registry.getAllIds(),
        };
    }

    verifySerializedValue(value) {
        if (typeof value !== "string") {
            return "Not a valid registry id key: " + value;
        }
        if (!this.registry.hasId(value)) {
            return "Id " + value + " not known to registry";
        }
    }

    getCacheKey() {
        return "classid." + this.registry.getId();
    }
}

class TypePair extends BaseDataType {
    /**
     * @param {BaseDataType} type1
     * @param {BaseDataType} type2
     */
    constructor(type1, type2) {
        super();
        window.assert(type1 && type1 instanceof BaseDataType, "bad first type given for pair");
        window.assert(type2 && type2 instanceof BaseDataType, "bad second type given for pair");
        this.type1 = type1;
        this.type2 = type2;
    }

    serialize(value) {
        window.assert(Array.isArray(value), "pair: not an array");
        window.assert(value.length === 2, "pair: length != 2");
        return [this.type1.serialize(value[0]), this.type2.serialize(value[1])];
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        const result = [undefined, undefined];

        let errorCode = this.type1.deserialize(value[0], result, 0, root);
        if (errorCode) {
            return errorCode;
        }
        errorCode = this.type2.deserialize(value[1], result, 1, root);
        if (errorCode) {
            return errorCode;
        }

        targetObject[targetKey] = result;
    }

    getAsJsonSchemaUncached() {
        return {
            type: "array",
            minLength: 2,
            maxLength: 2,
            items: [this.type1.getAsJsonSchema(), this.type2.getAsJsonSchema()],
        };
    }

    verifySerializedValue(value) {
        if (!Array.isArray(value)) {
            return "Pair is not an array";
        }
        if (value.length !== 2) {
            return "Pair length != 2";
        }
        let errorCode = this.type1.verifySerializedValue(value[0]);
        if (errorCode) {
            return errorCode;
        }
        errorCode = this.type2.verifySerializedValue(value[1]);
        if (errorCode) {
            return errorCode;
        }
    }

    getCacheKey() {
        return "pair.(" + this.type1.getCacheKey() + "," + this.type2.getCacheKey + ")";
    }
}

class TypeNullable extends BaseDataType {
    /**
     * @param {BaseDataType} wrapped
     */
    constructor(wrapped) {
        super();
        this.wrapped = wrapped;
    }

    serialize(value) {
        if (value === null || value === undefined) {
            return null;
        }
        return this.wrapped.serialize(value);
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        if (value === null || value === undefined) {
            targetObject[targetKey] = null;
            return;
        }
        return this.wrapped.deserialize(value, targetObject, targetKey, root);
    }

    verifySerializedValue(value) {
        if (value === null) {
            return;
        }
        return this.wrapped.verifySerializedValue(value);
    }

    getAsJsonSchemaUncached() {
        return {
            oneOf: [
                {
                    type: "null",
                },
                this.wrapped.getAsJsonSchema(),
            ],
        };
    }

    allowNull() {
        return true;
    }

    getCacheKey() {
        return "nullable." + this.wrapped.getCacheKey();
    }
}

class TypeStructuredObject extends BaseDataType {
    /**
     * @param {Object.<string, BaseDataType>} descriptor
     */
    constructor(descriptor) {
        super();
        this.descriptor = descriptor;
    }

    serialize(value) {
        window.assert(typeof value === "object", "not an object");
        let result = {};
        for (const key in this.descriptor) {
            // assert(value.hasOwnProperty(key), "Serialization: Object does not have", key, "property!");
            result[key] = this.descriptor[key].serialize(value[key]);
        }
        return result;
    }

    /**
     * @see BaseDataType.deserialize
     * @param {any} value
     * @param {GameRoot} root
     * @param {object} targetObject
     * @param {string|number} targetKey
     * @returns {string|void} String error code or null on success
     */
    deserialize(value, targetObject, targetKey, root) {
        let target = targetObject[targetKey];
        if (!target) {
            targetObject[targetKey] = target = {};
        }

        for (const key in value) {
            const valueType = this.descriptor[key];
            const errorCode = valueType.deserializeWithVerify(value[key], target, key, root);
            if (errorCode) {
                return errorCode;
            }
        }
    }

    getAsJsonSchemaUncached() {
        let properties = {};
        for (const key in this.descriptor) {
            properties[key] = this.descriptor[key].getAsJsonSchema();
        }

        return {
            type: "object",
            required: Object.keys(this.descriptor),
            properties,
        };
    }

    verifySerializedValue(value) {
        if (typeof value !== "object") {
            return "structured object is not an object";
        }
        for (const key in this.descriptor) {
            if (!value.hasOwnProperty(key)) {
                return "structured object is missing key " + key;
            }
            const subError = this.descriptor[key].verifySerializedValue(value[key]);
            if (subError) {
                return "structured object::" + subError;
            }
        }
    }

    getCacheKey() {
        let props = [];
        for (const key in this.descriptor) {
            props.push(key + "=" + this.descriptor[key].getCacheKey());
        }
        return "structured[" + props.join(",") + "]";
    }
}


/***/ }),

/***/ "./src/js/savegame/serializer_internal.js":
/*!************************************************!*\
  !*** ./src/js/savegame/serializer_internal.js ***!
  \************************************************/
/*! exports provided: SerializerInternal */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SerializerInternal", function() { return SerializerInternal; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_vector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/vector */ "./src/js/core/vector.js");
/* harmony import */ var _game_building_codes__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../game/building_codes */ "./src/js/game/building_codes.js");
/* harmony import */ var _game_entity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../game/entity */ "./src/js/game/entity.js");
/* harmony import */ var _game_root__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../game/root */ "./src/js/game/root.js");







const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("serializer_internal");

// Internal serializer methods
class SerializerInternal {
    /**
     * Serializes an array of entities
     * @param {Array<Entity>} array
     */
    serializeEntityArray(array) {
        const serialized = [];
        for (let i = 0; i < array.length; ++i) {
            const entity = array[i];
            if (!entity.queuedForDestroy && !entity.destroyed) {
                serialized.push(entity.serialize());
            }
        }
        return serialized;
    }

    /**
     *
     * @param {GameRoot} root
     * @param {Array<Entity>} array
     * @returns {string|void}
     */
    deserializeEntityArray(root, array) {
        for (let i = 0; i < array.length; ++i) {
            this.deserializeEntity(root, array[i]);
        }
    }

    /**
     *
     * @param {GameRoot} root
     * @param {Entity} payload
     */
    deserializeEntity(root, payload) {
        const staticData = payload.components.StaticMapEntity;
        window.assert(staticData, "entity has no static data");

        const code = staticData.code;
        const data = Object(_game_building_codes__WEBPACK_IMPORTED_MODULE_3__["getBuildingDataFromCode"])(code);

        const metaBuilding = data.metaInstance;

        const entity = metaBuilding.createEntity({
            root,
            origin: _core_vector__WEBPACK_IMPORTED_MODULE_2__["Vector"].fromSerializedObject(staticData.origin),
            rotation: staticData.rotation,
            originalRotation: staticData.originalRotation,
            rotationVariant: data.rotationVariant,
            variant: data.variant,
        });

        entity.uid = payload.uid;

        this.deserializeComponents(root, entity, payload.components);

        root.entityMgr.registerEntity(entity, payload.uid);
        root.map.placeStaticEntity(entity);
    }

    /////// COMPONENTS ////

    /**
     * Deserializes components of an entity
     * @param {GameRoot} root
     * @param {Entity} entity
     * @param {Object.<string, any>} data
     * @returns {string|void}
     */
    deserializeComponents(root, entity, data) {
        for (const componentId in data) {
            if (!entity.components[componentId]) {
                if (false) {}
                continue;
            }

            const errorStatus = entity.components[componentId].deserialize(data[componentId], root);
            if (errorStatus) {
                return errorStatus;
            }
        }
    }
}


/***/ }),

/***/ "./src/js/states/about.js":
/*!********************************!*\
  !*** ./src/js/states/about.js ***!
  \********************************/
/*! exports provided: AboutState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AboutState", function() { return AboutState; });
/* harmony import */ var _core_textual_game_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/textual_game_state */ "./src/js/core/textual_game_state.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_cachebust__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/cachebust */ "./src/js/core/cachebust.js");





class AboutState extends _core_textual_game_state__WEBPACK_IMPORTED_MODULE_0__["TextualGameState"] {
    constructor() {
        super("AboutState");
    }

    getStateHeaderTitle() {
        return _translations__WEBPACK_IMPORTED_MODULE_1__["T"].about.title;
    }

    getMainContentHTML() {
        return `
            <div class="head">
                <img src="${Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_3__["cachebust"])("res/logo.png")}" alt="shapez.io Logo">
            </div>
            <div class="text">
            ${_translations__WEBPACK_IMPORTED_MODULE_1__["T"].about.body
                .replace("<githublink>", _core_config__WEBPACK_IMPORTED_MODULE_2__["THIRDPARTY_URLS"].github)
                .replace("<discordlink>", _core_config__WEBPACK_IMPORTED_MODULE_2__["THIRDPARTY_URLS"].discord)}
            </div>
        `;
    }

    onEnter() {
        const links = this.htmlElement.querySelectorAll("a[href]");
        links.forEach(link => {
            this.trackClicks(
                link,
                () => this.app.platformWrapper.openExternalLink(link.getAttribute("href")),
                { preventClick: true }
            );
        });
    }

    getDefaultPreviousState() {
        return "SettingsState";
    }
}


/***/ }),

/***/ "./src/js/states/changelog.js":
/*!************************************!*\
  !*** ./src/js/states/changelog.js ***!
  \************************************/
/*! exports provided: ChangelogState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ChangelogState", function() { return ChangelogState; });
/* harmony import */ var _core_textual_game_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/textual_game_state */ "./src/js/core/textual_game_state.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");
/* harmony import */ var _changelog__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../changelog */ "./src/js/changelog.js");




class ChangelogState extends _core_textual_game_state__WEBPACK_IMPORTED_MODULE_0__["TextualGameState"] {
    constructor() {
        super("ChangelogState");
    }

    getStateHeaderTitle() {
        return _translations__WEBPACK_IMPORTED_MODULE_1__["T"].changelog.title;
    }

    getMainContentHTML() {
        const entries = _changelog__WEBPACK_IMPORTED_MODULE_2__["CHANGELOG"];

        let html = "";

        for (let i = 0; i < entries.length; ++i) {
            const entry = entries[i];
            html += `
                <div class="entry">
                    <span class="version">${entry.version}</span>
                    <span class="date">${entry.date}</span>
                    <ul class="changes">
                        ${entry.entries.map(text => `<li>${text}</li>`).join("")}
                    </ul>
                </div>
            `;
        }

        return html;
    }

    onEnter() {
        const links = this.htmlElement.querySelectorAll("a[href]");
        links.forEach(link => {
            this.trackClicks(
                link,
                () => this.app.platformWrapper.openExternalLink(link.getAttribute("href")),
                { preventClick: true }
            );
        });
    }
}


/***/ }),

/***/ "./src/js/states/ingame.js":
/*!*********************************!*\
  !*** ./src/js/states/ingame.js ***!
  \*********************************/
/*! exports provided: gameCreationAction, GameCreationPayload, InGameState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "gameCreationAction", function() { return gameCreationAction; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GameCreationPayload", function() { return GameCreationPayload; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InGameState", function() { return InGameState; });
/* harmony import */ var _core_error_handler__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/error_handler */ "./src/js/core/error_handler.js");
/* harmony import */ var _core_game_state__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _game_game_loading_overlay__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../game/game_loading_overlay */ "./src/js/game/game_loading_overlay.js");
/* harmony import */ var _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../game/key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _savegame_savegame__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../savegame/savegame */ "./src/js/savegame/savegame.js");
/* harmony import */ var _game_core__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../game/core */ "./src/js/game/core.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");











const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["createLogger"])("state/ingame");

// Different sub-states
const stages = {
    s3_createCore: "🌈 3: Create core",
    s4_A_initEmptyGame: "🌈 4/A: Init empty game",
    s4_B_resumeGame: "🌈 4/B: Resume game",

    s5_firstUpdate: "🌈 5: First game update",
    s6_postLoadHook: "🌈 6: Post load hook",
    s7_warmup: "🌈 7: Warmup",

    s10_gameRunning: "🌈 10: Game finally running",

    leaving: "🌈 Saving, then leaving the game",
    destroyed: "🌈 DESTROYED: Core is empty and waits for state leave",
    initFailed: "🌈 ERROR: Initialization failed!",
};

const gameCreationAction = {
    new: "new-game",
    resume: "resume-game",
};

// Typehints
class GameCreationPayload {
    constructor() {
        /** @type {boolean|undefined} */
        this.fastEnter;

        /** @type {Savegame} */
        this.savegame;
    }
}

class InGameState extends _core_game_state__WEBPACK_IMPORTED_MODULE_1__["GameState"] {
    constructor() {
        super("InGameState");

        /** @type {GameCreationPayload} */
        this.creationPayload = null;

        // Stores current stage
        this.stage = "";

        /** @type {GameCore} */
        this.core = null;

        /** @type {KeyActionMapper} */
        this.keyActionMapper = null;

        /** @type {GameLoadingOverlay} */
        this.loadingOverlay = null;

        /** @type {Savegame} */
        this.savegame = null;

        this.boundInputFilter = this.filterInput.bind(this);

        /**
         * Whether we are currently saving the game
         * @TODO: This doesn't realy fit here
         */
        this.currentSavePromise = null;
    }

    /**
     * Switches the game into another sub-state
     * @param {string} stage
     */
    switchStage(stage) {
        window.assert(stage, "Got empty stage");
        if (stage !== this.stage) {
            this.stage = stage;
            logger.log(this.stage);
            return true;
        } else {
            // log(this, "Re entering", stage);
            return false;
        }
    }

    // GameState implementation
    getInnerHTML() {
        return "";
    }

    getThemeMusic() {
        return _platform_sound__WEBPACK_IMPORTED_MODULE_9__["MUSIC"].theme;
    }

    onBeforeExit() {
        // logger.log("Saving before quitting");
        // return this.doSave().then(() => {
        //     logger.log(this, "Successfully saved");
        //     // this.stageDestroyed();
        // });
    }

    onAppPause() {
        // if (this.stage === stages.s10_gameRunning) {
        //     logger.log("Saving because app got paused");
        //     this.doSave();
        // }
    }

    getHasFadeIn() {
        return false;
    }

    getPauseOnFocusLost() {
        return false;
    }

    getHasUnloadConfirmation() {
        return true;
    }

    onLeave() {
        if (this.core) {
            this.stageDestroyed();
        }
        this.app.inputMgr.dismountFilter(this.boundInputFilter);
    }

    onResized(w, h) {
        super.onResized(w, h);
        if (this.stage === stages.s10_gameRunning) {
            this.core.resize(w, h);
        }
    }

    // ---- End of GameState implementation

    /**
     * Goes back to the menu state
     */
    goBackToMenu() {
        this.saveThenGoToState("MainMenuState");
    }

    /**
     * Goes back to the settings state
     */
    goToSettings() {
        this.saveThenGoToState("SettingsState", {
            backToStateId: this.key,
            backToStatePayload: this.creationPayload,
        });
    }

    /**
     * Goes back to the settings state
     */
    goToKeybindings() {
        this.saveThenGoToState("KeybindingsState", {
            backToStateId: this.key,
            backToStatePayload: this.creationPayload,
        });
    }

    /**
     * Moves to a state outside of the game
     * @param {string} stateId
     * @param {any=} payload
     */
    saveThenGoToState(stateId, payload) {
        if (this.stage === stages.leaving || this.stage === stages.destroyed) {
            logger.warn(
                "Tried to leave game twice or during destroy:",
                this.stage,
                "(attempted to move to",
                stateId,
                ")"
            );
            return;
        }
        this.stageLeavingGame();
        this.doSave().then(() => {
            this.stageDestroyed();
            this.moveToState(stateId, payload);
        });
    }

    onBackButton() {
        // do nothing
    }

    /**
     * Called when the game somehow failed to initialize. Resets everything to basic state and
     * then goes to the main menu, showing the error
     * @param {string} err
     */
    onInitializationFailure(err) {
        if (this.switchStage(stages.initFailed)) {
            logger.error("Init failure:", err);
            this.stageDestroyed();
            this.moveToState("MainMenuState", { loadError: err });
        }
    }

    // STAGES

    /**
     * Creates the game core instance, and thus the root
     */
    stage3CreateCore() {
        if (this.switchStage(stages.s3_createCore)) {
            logger.log("Creating new game core");
            this.core = new _game_core__WEBPACK_IMPORTED_MODULE_8__["GameCore"](this.app);

            this.core.initializeRoot(this, this.savegame);

            if (this.savegame.hasGameDump()) {
                this.stage4bResumeGame();
            } else {
                this.app.gameAnalytics.handleGameStarted();
                this.stage4aInitEmptyGame();
            }
        }
    }

    /**
     * Initializes a new empty game
     */
    stage4aInitEmptyGame() {
        if (this.switchStage(stages.s4_A_initEmptyGame)) {
            this.core.initNewGame();
            this.stage5FirstUpdate();
        }
    }

    /**
     * Resumes an existing game
     */
    stage4bResumeGame() {
        if (this.switchStage(stages.s4_B_resumeGame)) {
            if (!this.core.initExistingGame()) {
                this.onInitializationFailure("Savegame is corrupt and can not be restored.");
                return;
            }
            this.app.gameAnalytics.handleGameResumed();
            this.stage5FirstUpdate();
        }
    }

    /**
     * Performs the first game update on the game which initializes most caches
     */
    stage5FirstUpdate() {
        if (this.switchStage(stages.s5_firstUpdate)) {
            this.core.root.logicInitialized = true;
            this.core.updateLogic();
            this.stage6PostLoadHook();
        }
    }

    /**
     * Call the post load hook, this means that we have loaded the game, and all systems
     * can operate and start to work now.
     */
    stage6PostLoadHook() {
        if (this.switchStage(stages.s6_postLoadHook)) {
            logger.log("Post load hook");
            this.core.postLoadHook();
            this.stage7Warmup();
        }
    }

    /**
     * This makes the game idle and draw for a while, because we run most code this way
     * the V8 engine can already start to optimize it. Also this makes sure the resources
     * are in the VRAM and we have a smooth experience once we start.
     */
    stage7Warmup() {
        if (this.switchStage(stages.s7_warmup)) {
            if (this.creationPayload.fastEnter) {
                this.warmupTimeSeconds = _core_config__WEBPACK_IMPORTED_MODULE_4__["globalConfig"].warmupTimeSecondsFast;
            } else {
                this.warmupTimeSeconds = _core_config__WEBPACK_IMPORTED_MODULE_4__["globalConfig"].warmupTimeSecondsRegular;
            }
        }
    }

    /**
     * The final stage where this game is running and updating regulary.
     */
    stage10GameRunning() {
        if (this.switchStage(stages.s10_gameRunning)) {
            this.core.root.signals.readyToRender.dispatch();

            Object(_core_logging__WEBPACK_IMPORTED_MODULE_2__["logSection"])("GAME STARTED", "#26a69a");

            // Initial resize, might have changed during loading (this is possible)
            this.core.resize(this.app.screenWidth, this.app.screenHeight);
        }
    }

    /**
     * This stage destroys the whole game, used to cleanup
     */
    stageDestroyed() {
        if (this.switchStage(stages.destroyed)) {
            // Cleanup all api calls
            this.cancelAllAsyncOperations();

            if (this.syncer) {
                this.syncer.cancelSync();
                this.syncer = null;
            }

            // Cleanup core
            if (this.core) {
                this.core.destruct();
                this.core = null;
            }
        }
    }

    /**
     * When leaving the game
     */
    stageLeavingGame() {
        if (this.switchStage(stages.leaving)) {
            // ...
        }
    }

    // END STAGES

    /**
     * Filters the input (keybindings)
     */
    filterInput() {
        return this.stage === stages.s10_gameRunning;
    }

    /**
     * @param {GameCreationPayload} payload
     */
    onEnter(payload) {
        this.app.inputMgr.installFilter(this.boundInputFilter);

        this.creationPayload = payload;
        this.savegame = payload.savegame;

        this.loadingOverlay = new _game_game_loading_overlay__WEBPACK_IMPORTED_MODULE_5__["GameLoadingOverlay"](this.app, this.getDivElement());
        this.loadingOverlay.showBasic();

        // Remove unneded default element
        document.body.querySelector(".modalDialogParent").remove();

        this.asyncChannel.watch(Object(_core_utils__WEBPACK_IMPORTED_MODULE_3__["waitNextFrame"])()).then(() => this.stage3CreateCore());
    }

    /**
     * Render callback
     * @param {number} dt
     */
    onRender(dt) {
        if (_core_error_handler__WEBPACK_IMPORTED_MODULE_0__["APPLICATION_ERROR_OCCURED"]) {
            // Application somehow crashed, do not do anything
            return;
        }

        if (this.stage === stages.s7_warmup) {
            this.core.draw();
            this.warmupTimeSeconds -= dt / 1000.0;
            if (this.warmupTimeSeconds < 0) {
                logger.log("Warmup completed");
                this.stage10GameRunning();
            }
        }

        if (this.stage === stages.s10_gameRunning) {
            this.core.tick(dt);
        }

        // If the stage is still active (This might not be the case if tick() moved us to game over)
        if (this.stage === stages.s10_gameRunning) {
            // Only draw if page visible
            if (this.app.pageVisible) {
                this.core.draw();
            }

            this.loadingOverlay.removeIfAttached();
        } else {
            if (!this.loadingOverlay.isAttached()) {
                this.loadingOverlay.showBasic();
            }
        }
    }

    onBackgroundTick(dt) {
        this.onRender(dt);
    }

    /**
     * Saves the game
     */

    doSave() {
        if (!this.savegame || !this.savegame.isSaveable()) {
            return Promise.resolve();
        }

        if (_core_error_handler__WEBPACK_IMPORTED_MODULE_0__["APPLICATION_ERROR_OCCURED"]) {
            logger.warn("skipping save because application crashed");
            return Promise.resolve();
        }

        if (
            this.stage !== stages.s10_gameRunning &&
            this.stage !== stages.s7_warmup &&
            this.stage !== stages.leaving
        ) {
            logger.warn("Skipping save because game is not ready");
            return Promise.resolve();
        }

        if (this.currentSavePromise) {
            logger.warn("Skipping double save and returning same promise");
            return this.currentSavePromise;
        }
        logger.log("Starting to save game ...");
        this.savegame.updateData(this.core.root);

        this.currentSavePromise = this.savegame
            .writeSavegameAndMetadata()
            .catch(err => {
                // Catch errors
                logger.warn("Failed to save:", err);
            })
            .then(() => {
                // Clear promise
                logger.log("Saved!");
                this.core.root.signals.gameSaved.dispatch();
                this.currentSavePromise = null;
            });

        return this.currentSavePromise;
    }
}


/***/ }),

/***/ "./src/js/states/keybindings.js":
/*!**************************************!*\
  !*** ./src/js/states/keybindings.js ***!
  \**************************************/
/*! exports provided: KeybindingsState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeybindingsState", function() { return KeybindingsState; });
/* harmony import */ var _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/modal_dialog_elements */ "./src/js/core/modal_dialog_elements.js");
/* harmony import */ var _core_textual_game_state__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/textual_game_state */ "./src/js/core/textual_game_state.js");
/* harmony import */ var _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../game/key_action_mapper */ "./src/js/game/key_action_mapper.js");
/* harmony import */ var _platform_sound__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../platform/sound */ "./src/js/platform/sound.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");






class KeybindingsState extends _core_textual_game_state__WEBPACK_IMPORTED_MODULE_1__["TextualGameState"] {
    constructor() {
        super("KeybindingsState");
    }

    getStateHeaderTitle() {
        return _translations__WEBPACK_IMPORTED_MODULE_4__["T"].keybindings.title;
    }

    getMainContentHTML() {
        return `

            <div class="topEntries">
                <span class="hint">${_translations__WEBPACK_IMPORTED_MODULE_4__["T"].keybindings.hint}</span>
                <button class="styledButton resetBindings">${_translations__WEBPACK_IMPORTED_MODULE_4__["T"].keybindings.resetKeybindings}</button>
            
            </div>

            <div class="keybindings">

            </div>
        `;
    }

    onEnter() {
        const keybindingsElem = this.htmlElement.querySelector(".keybindings");

        this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings);

        for (const category in _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"]) {
            const categoryDiv = document.createElement("div");
            categoryDiv.classList.add("category");
            keybindingsElem.appendChild(categoryDiv);

            const labelDiv = document.createElement("strong");
            labelDiv.innerText = _translations__WEBPACK_IMPORTED_MODULE_4__["T"].keybindings.categoryLabels[category];
            labelDiv.classList.add("categoryLabel");
            categoryDiv.appendChild(labelDiv);

            for (const keybindingId in _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"][category]) {
                const mapped = _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"][category][keybindingId];

                const elem = document.createElement("div");
                elem.classList.add("entry");
                elem.setAttribute("data-keybinding", keybindingId);
                categoryDiv.appendChild(elem);

                const title = document.createElement("span");
                title.classList.add("title");
                title.innerText = _translations__WEBPACK_IMPORTED_MODULE_4__["T"].keybindings.mappings[keybindingId];
                elem.appendChild(title);

                const mappingDiv = document.createElement("span");
                mappingDiv.classList.add("mapping");
                elem.appendChild(mappingDiv);

                const editBtn = document.createElement("button");
                editBtn.classList.add("styledButton", "editKeybinding");

                const resetBtn = document.createElement("button");
                resetBtn.classList.add("styledButton", "resetKeybinding");

                if (mapped.builtin) {
                    editBtn.classList.add("disabled");
                    resetBtn.classList.add("disabled");
                } else {
                    this.trackClicks(editBtn, () => this.editKeybinding(keybindingId));
                    this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId));
                }
                elem.appendChild(editBtn);
                elem.appendChild(resetBtn);
            }
        }
        this.updateKeybindings();
    }

    editKeybinding(id) {
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_0__["Dialog"]({
            app: this.app,
            title: _translations__WEBPACK_IMPORTED_MODULE_4__["T"].dialogs.editKeybinding.title,
            contentHTML: _translations__WEBPACK_IMPORTED_MODULE_4__["T"].dialogs.editKeybinding.desc,
            buttons: ["cancel:good"],
            type: "info",
        });

        dialog.inputReciever.keydown.add(({ keyCode, shift, alt, event }) => {
            if (keyCode === 27) {
                this.dialogs.closeDialog(dialog);
                return;
            }

            if (event) {
                event.preventDefault();
            }

            if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) {
                return;
            }

            if (
                // Enter
                keyCode === 13
            ) {
                // Ignore builtins
                return;
            }

            this.app.settings.updateKeybindingOverride(id, keyCode);

            this.dialogs.closeDialog(dialog);
            this.updateKeybindings();
        });

        dialog.inputReciever.backButton.add(() => {});
        this.dialogs.internalShowDialog(dialog);

        this.app.sound.playUiSound(_platform_sound__WEBPACK_IMPORTED_MODULE_3__["SOUNDS"].dialogOk);
    }

    updateKeybindings() {
        const overrides = this.app.settings.getKeybindingOverrides();
        for (const category in _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"]) {
            for (const keybindingId in _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"][category]) {
                const mapped = _game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["KEYMAPPINGS"][category][keybindingId];

                const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']");
                window.assert(container, "Container for keybinding not found: " + keybindingId);

                let keyCode = mapped.keyCode;
                if (overrides[keybindingId]) {
                    keyCode = overrides[keybindingId];
                }

                const mappingDiv = container.querySelector(".mapping");
                mappingDiv.innerHTML = Object(_game_key_action_mapper__WEBPACK_IMPORTED_MODULE_2__["getStringForKeyCode"])(keyCode);
                mappingDiv.classList.toggle("changed", !!overrides[keybindingId]);

                const resetBtn = container.querySelector("button.resetKeybinding");
                resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]);
            }
        }
    }

    resetKeybinding(id) {
        this.app.settings.resetKeybindingOverride(id);
        this.updateKeybindings();
    }

    resetBindings() {
        const { reset } = this.dialogs.showWarning(
            _translations__WEBPACK_IMPORTED_MODULE_4__["T"].dialogs.resetKeybindingsConfirmation.title,
            _translations__WEBPACK_IMPORTED_MODULE_4__["T"].dialogs.resetKeybindingsConfirmation.desc,
            ["cancel:good", "reset:bad"]
        );

        reset.add(() => {
            this.app.settings.resetKeybindingOverrides();
            this.updateKeybindings();

            this.dialogs.showInfo(_translations__WEBPACK_IMPORTED_MODULE_4__["T"].dialogs.keybindingsResetOk.title, _translations__WEBPACK_IMPORTED_MODULE_4__["T"].dialogs.keybindingsResetOk.desc);
        });
    }

    getDefaultPreviousState() {
        return "SettingsState";
    }
}


/***/ }),

/***/ "./src/js/states/main_menu.js":
/*!************************************!*\
  !*** ./src/js/states/main_menu.js ***!
  \************************************/
/*! exports provided: MainMenuState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MainMenuState", function() { return MainMenuState; });
/* harmony import */ var _core_cachebust__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/cachebust */ "./src/js/core/cachebust.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_game_state__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/modal_dialog_elements */ "./src/js/core/modal_dialog_elements.js");
/* harmony import */ var _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/modal_dialog_forms */ "./src/js/core/modal_dialog_forms.js");
/* harmony import */ var _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/read_write_proxy */ "./src/js/core/read_write_proxy.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _game_hud_parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../game/hud/parts/modal_dialogs */ "./src/js/game/hud/parts/modal_dialogs.js");
/* harmony import */ var _profile_application_settings__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../profile/application_settings */ "./src/js/profile/application_settings.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");











const trim = __webpack_require__(/*! trim */ "./node_modules/trim/index.js");

/**
 * @typedef {import("../savegame/savegame_typedefs").SavegameMetadata} SavegameMetadata
 * @typedef {import("../profile/setting_types").EnumSetting} EnumSetting
 */

class MainMenuState extends _core_game_state__WEBPACK_IMPORTED_MODULE_2__["GameState"] {
    constructor() {
        super("MainMenuState");
    }

    getInnerHTML() {
        const bannerHtml = `
            <h3>${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].demoBanners.title}</h3>
            <p>${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].demoBanners.intro}</p>
            <a href="#" class="steamLink ${_core_config__WEBPACK_IMPORTED_MODULE_1__["A_B_TESTING_LINK_TYPE"]}" target="_blank">Get the shapez.io standalone!</a>
        `;

        const showDemoBadges = this.app.restrictionMgr.getIsStandaloneMarketingActive();

        return `
            <div class="topButtons">
                <button class="languageChoose" data-languageicon="${this.app.settings.getLanguage()}"></button>
                <button class="settingsButton"></button>
            ${
                 false
                    ? undefined
                    : ""
            }
            </div>

            <video autoplay muted loop class="fullscreenBackgroundVideo">
                <source src="${Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_0__["cachebust"])("res/bg_render.webm")}" type="video/webm">
            </video>

            <div class="logo">
                <img src="${Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_0__["cachebust"])("res/logo.png")}" alt="shapez.io Logo">
                <span class="updateLabel">v${"1.2.2"}</span>
            </div>

            <div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}">
                <div class="sideContainer">
                    ${showDemoBadges ? `<div class="standaloneBanner">${bannerHtml}</div>` : ""}
                </div>

                <div class="mainContainer">
                    ${
                        Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["isSupportedBrowser"])()
                            ? ""
                            : `<div class="browserWarning">${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.browserWarning}</div>`
                    }
                    <div class="buttons"></div>
                </div>
            </div>

            <div class="footer">
                <a class="githubLink boxLink" target="_blank">
                    ${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.openSourceHint}
                    <span class="thirdpartyLogo githubLogo"></span>
                </a>

                <a class="discordLink boxLink" target="_blank">
                    ${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.discordLink}
                    <span class="thirdpartyLogo  discordLogo"></span>
                </a>

                <div class="sidelinks">
                    <a class="redditLink">${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.subreddit}</a>

                    <a class="changelog">${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].changelog.title}</a>

                    <a class="helpTranslate">${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.helpTranslate}</a>
                </div>

                <div class="author">${_translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.madeBy.replace(
                    "<author-link>",
                    '<a class="producerLink" target="_blank">Tobias Springer</a>'
                )}</div>
            </div>
        `;
    }

    /**
     * Asks the user to import a savegame
     */
    requestImportSavegame() {
        if (
            this.app.savegameMgr.getSavegamesMetaData().length > 0 &&
            !this.app.restrictionMgr.getHasUnlimitedSavegames()
        ) {
            this.app.analytics.trackUiClick("importgame_slot_limit_show");
            this.showSavegameSlotLimit();
            return;
        }

        // Create a 'fake' file-input to accept savegames
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["startFileChoose"])(".bin").then(file => {
            if (file) {
                const closeLoader = this.dialogs.showLoadingDialog();
                Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["waitNextFrame"])().then(() => {
                    this.app.analytics.trackUiClick("import_savegame");
                    const reader = new FileReader();
                    reader.addEventListener("load", event => {
                        const contents = event.target.result;
                        let realContent;

                        try {
                            realContent = _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_5__["ReadWriteProxy"].deserializeObject(contents);
                        } catch (err) {
                            closeLoader();
                            this.dialogs.showWarning(
                                _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameError.title,
                                _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameError.text + "<br><br>" + err
                            );
                            return;
                        }

                        this.app.savegameMgr.importSavegame(realContent).then(
                            () => {
                                closeLoader();
                                this.dialogs.showWarning(
                                    _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameSuccess.title,
                                    _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameSuccess.text
                                );

                                this.renderMainMenu();
                                this.renderSavegames();
                            },
                            err => {
                                closeLoader();
                                this.dialogs.showWarning(
                                    _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameError.title,
                                    _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameError.text + ":<br><br>" + err
                                );
                            }
                        );
                    });
                    reader.addEventListener("error", error => {
                        this.dialogs.showWarning(
                            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameError.title,
                            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.importSavegameError.text + ":<br><br>" + error
                        );
                    });
                    reader.readAsText(file, "utf-8");
                });
            }
        });
    }

    onBackButton() {
        this.app.platformWrapper.exitApp();
    }

    onEnter(payload) {
        this.dialogs = new _game_hud_parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_7__["HUDModalDialogs"](null, this.app);
        const dialogsElement = document.body.querySelector(".modalDialogParent");
        this.dialogs.initializeToElement(dialogsElement);

        if (payload.loadError) {
            this.dialogs.showWarning(
                _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.gameLoadFailure.title,
                _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.gameLoadFailure.text + "<br><br>" + payload.loadError
            );
        }

        const qs = this.htmlElement.querySelector.bind(this.htmlElement);

        if (false) {}

        // Initialize video
        this.videoElement = this.htmlElement.querySelector("video");
        this.videoElement.playbackRate = 0.9;
        this.videoElement.addEventListener("canplay", () => {
            if (this.videoElement) {
                this.videoElement.classList.add("loaded");
            }
        });

        this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked);
        this.trackClicks(qs(".changelog"), this.onChangelogClicked);
        this.trackClicks(qs(".redditLink"), this.onRedditClicked);
        this.trackClicks(qs(".languageChoose"), this.onLanguageChooseClicked);
        this.trackClicks(qs(".helpTranslate"), this.onTranslationHelpLinkClicked);

        if (false) {}

        this.renderMainMenu();
        this.renderSavegames();

        const steamLink = this.htmlElement.querySelector(".steamLink");
        if (steamLink) {
            this.trackClicks(steamLink, () => this.onSteamLinkClicked(), { preventClick: true });
        }

        const discordLink = this.htmlElement.querySelector(".discordLink");
        this.trackClicks(
            discordLink,
            () => this.app.platformWrapper.openExternalLink(_core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].discord),
            { preventClick: true }
        );

        const githubLink = this.htmlElement.querySelector(".githubLink");
        this.trackClicks(
            githubLink,
            () => this.app.platformWrapper.openExternalLink(_core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].github),
            { preventClick: true }
        );

        const producerLink = this.htmlElement.querySelector(".producerLink");
        this.trackClicks(
            producerLink,
            () => this.app.platformWrapper.openExternalLink("https://tobspr.com"),
            { preventClick: true }
        );
    }

    renderMainMenu() {
        const buttonContainer = this.htmlElement.querySelector(".mainContainer .buttons");
        Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["removeAllChildren"])(buttonContainer);

        // Import button
        const importButtonElement = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeButtonElement"])(
            ["importButton", "styledButton"],
            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.importSavegame
        );
        this.trackClicks(importButtonElement, this.requestImportSavegame);

        if (this.savedGames.length > 0) {
            // Continue game
            const continueButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeButton"])(
                buttonContainer,
                ["continueButton", "styledButton"],
                _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.continue
            );
            this.trackClicks(continueButton, this.onContinueButtonClicked);

            const outerDiv = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeDiv"])(buttonContainer, null, ["outer"], null);
            outerDiv.appendChild(importButtonElement);
            const newGameButton = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeButton"])(
                this.htmlElement.querySelector(".mainContainer .outer"),
                ["newGameButton", "styledButton"],
                _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.newGame
            );
            this.trackClicks(newGameButton, this.onPlayButtonClicked);
        } else {
            // New game
            const playBtn = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeButton"])(buttonContainer, ["playButton", "styledButton"], _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.play);
            this.trackClicks(playBtn, this.onPlayButtonClicked);
            buttonContainer.appendChild(importButtonElement);
        }
    }

    onSteamLinkClicked() {
        this.app.analytics.trackUiClick("main_menu_steam_link_" + _core_config__WEBPACK_IMPORTED_MODULE_1__["A_B_TESTING_LINK_TYPE"]);
        this.app.platformWrapper.openExternalLink(
            _core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].standaloneStorePage + "?ref=mmsl2&prc=" + _core_config__WEBPACK_IMPORTED_MODULE_1__["A_B_TESTING_LINK_TYPE"]
        );

        return false;
    }

    onExitAppButtonClicked() {
        this.app.platformWrapper.exitApp();
    }

    onChangelogClicked() {
        this.moveToState("ChangelogState");
    }

    onRedditClicked() {
        this.app.analytics.trackUiClick("main_menu_reddit_link");
        this.app.platformWrapper.openExternalLink(_core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].reddit);
    }

    onLanguageChooseClicked() {
        this.app.analytics.trackUiClick("choose_language");
        const setting = /** @type {EnumSetting} */ (Object(_profile_application_settings__WEBPACK_IMPORTED_MODULE_8__["getApplicationSettingById"])("language"));

        const { optionSelected } = this.dialogs.showOptionChooser(_translations__WEBPACK_IMPORTED_MODULE_9__["T"].settings.labels.language.title, {
            active: this.app.settings.getLanguage(),
            options: setting.options.map(option => ({
                value: setting.valueGetter(option),
                text: setting.textGetter(option),
                desc: setting.descGetter(option),
                iconPrefix: setting.iconPrefix,
            })),
        });

        optionSelected.add(value => {
            this.app.settings.updateLanguage(value).then(() => {
                if (setting.restartRequired) {
                    if (this.app.platformWrapper.getSupportsRestart()) {
                        this.app.platformWrapper.performRestart();
                    } else {
                        this.dialogs.showInfo(
                            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.restartRequired.title,
                            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.restartRequired.text,
                            ["ok:good"]
                        );
                    }
                }

                if (setting.changeCb) {
                    setting.changeCb(this.app, value);
                }
            });

            // Update current icon
            this.htmlElement.querySelector("button.languageChoose").setAttribute("data-languageIcon", value);
        }, this);
    }

    get savedGames() {
        return this.app.savegameMgr.getSavegamesMetaData();
    }

    renderSavegames() {
        const oldContainer = this.htmlElement.querySelector(".mainContainer .savegames");
        if (oldContainer) {
            oldContainer.remove();
        }
        const games = this.savedGames;
        if (games.length > 0) {
            const parent = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeDiv"])(this.htmlElement.querySelector(".mainContainer"), null, ["savegames"]);

            for (let i = 0; i < games.length; ++i) {
                const elem = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeDiv"])(parent, null, ["savegame"]);

                Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeDiv"])(
                    elem,
                    null,
                    ["playtime"],
                    Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["formatSecondsToTimeAgo"])((new Date().getTime() - games[i].lastUpdate) / 1000.0)
                );

                Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeDiv"])(
                    elem,
                    null,
                    ["level"],
                    games[i].level
                        ? _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.savegameLevel.replace("<x>", "" + games[i].level)
                        : _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.savegameLevelUnknown
                );

                const name = Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["makeDiv"])(
                    elem,
                    null,
                    ["name"],
                    "<span>" + (games[i].name ? games[i].name : _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.savegameUnnamed) + "</span>"
                );

                const deleteButton = document.createElement("button");
                deleteButton.classList.add("styledButton", "deleteGame");
                elem.appendChild(deleteButton);

                const downloadButton = document.createElement("button");
                downloadButton.classList.add("styledButton", "downloadGame");
                elem.appendChild(downloadButton);

                const renameButton = document.createElement("button");
                renameButton.classList.add("styledButton", "renameGame");
                name.appendChild(renameButton);

                const resumeButton = document.createElement("button");
                resumeButton.classList.add("styledButton", "resumeGame");
                elem.appendChild(resumeButton);

                this.trackClicks(deleteButton, () => this.deleteGame(games[i]));
                this.trackClicks(downloadButton, () => this.downloadGame(games[i]));
                this.trackClicks(resumeButton, () => this.resumeGame(games[i]));
                this.trackClicks(renameButton, () => this.requestRenameSavegame(games[i]));
            }
        }
    }

    /**
     * @param {SavegameMetadata} game
     */
    requestRenameSavegame(game) {
        const regex = /^[a-zA-Z0-9_\- ]{1,20}$/;

        const nameInput = new _core_modal_dialog_forms__WEBPACK_IMPORTED_MODULE_4__["FormElementInput"]({
            id: "nameInput",
            label: null,
            placeholder: "",
            defaultValue: game.name || "",
            validator: val => val.match(regex) && trim(val).length > 0,
        });
        const dialog = new _core_modal_dialog_elements__WEBPACK_IMPORTED_MODULE_3__["DialogWithForm"]({
            app: this.app,
            title: _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.renameSavegame.title,
            desc: _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.renameSavegame.desc,
            formElements: [nameInput],
            buttons: ["cancel:bad:escape", "ok:good:enter"],
        });
        this.dialogs.internalShowDialog(dialog);

        // When confirmed, save the name
        dialog.buttonSignals.ok.add(() => {
            game.name = trim(nameInput.getValue());
            this.app.savegameMgr.writeAsync();
            this.renderSavegames();
        });
    }

    /**
     * @param {SavegameMetadata} game
     */
    resumeGame(game) {
        this.app.analytics.trackUiClick("resume_game");

        this.app.adProvider.showVideoAd().then(() => {
            this.app.analytics.trackUiClick("resume_game_adcomplete");
            const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
            savegame
                .readAsync()
                .then(() => {
                    this.moveToState("InGameState", {
                        savegame,
                    });
                })
                .catch(err => {
                    this.dialogs.showWarning(
                        _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.gameLoadFailure.title,
                        _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.gameLoadFailure.text + "<br><br>" + err
                    );
                });
        });
    }

    /**
     * @param {SavegameMetadata} game
     */
    deleteGame(game) {
        this.app.analytics.trackUiClick("delete_game");

        const signals = this.dialogs.showWarning(
            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.confirmSavegameDelete.title,
            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.confirmSavegameDelete.text
                .replace("<savegameName>", game.name || _translations__WEBPACK_IMPORTED_MODULE_9__["T"].mainMenu.savegameUnnamed)
                .replace("<savegameLevel>", String(game.level)),
            ["cancel:good", "delete:bad:timeout"]
        );

        signals.delete.add(() => {
            this.app.savegameMgr.deleteSavegame(game).then(
                () => {
                    this.renderSavegames();
                    if (this.savedGames.length <= 0) this.renderMainMenu();
                },
                err => {
                    this.dialogs.showWarning(
                        _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.savegameDeletionError.title,
                        _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.savegameDeletionError.text + "<br><br>" + err
                    );
                }
            );
        });
    }

    /**
     * @param {SavegameMetadata} game
     */
    downloadGame(game) {
        this.app.analytics.trackUiClick("download_game");

        const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
        savegame.readAsync().then(() => {
            const data = _core_read_write_proxy__WEBPACK_IMPORTED_MODULE_5__["ReadWriteProxy"].serializeObject(savegame.currentData);
            const filename = (game.name || "unnamed") + ".bin";
            Object(_core_utils__WEBPACK_IMPORTED_MODULE_6__["generateFileDownload"])(filename, data);
        });
    }

    /**
     * Shows a hint that the slot limit has been reached
     */
    showSavegameSlotLimit() {
        const { getStandalone } = this.dialogs.showWarning(
            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.oneSavegameLimit.title,
            _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.oneSavegameLimit.desc,
            ["cancel:bad", "getStandalone:good"]
        );
        getStandalone.add(() => {
            this.app.analytics.trackUiClick("visit_steampage_from_slot_limit");
            this.app.platformWrapper.openExternalLink(_core_config__WEBPACK_IMPORTED_MODULE_1__["THIRDPARTY_URLS"].standaloneStorePage + "?reF=ssll");
        });
    }

    onSettingsButtonClicked() {
        this.moveToState("SettingsState");
    }

    onTranslationHelpLinkClicked() {
        this.app.analytics.trackUiClick("translation_help_link");
        this.app.platformWrapper.openExternalLink(
            "https://github.com/tobspr/shapez.io/blob/master/translations"
        );
    }

    onPlayButtonClicked() {
        if (
            this.app.savegameMgr.getSavegamesMetaData().length > 0 &&
            !this.app.restrictionMgr.getHasUnlimitedSavegames()
        ) {
            this.app.analytics.trackUiClick("startgame_slot_limit_show");
            this.showSavegameSlotLimit();
            return;
        }

        this.app.analytics.trackUiClick("startgame");
        this.app.adProvider.showVideoAd().then(() => {
            const savegame = this.app.savegameMgr.createNewSavegame();

            this.moveToState("InGameState", {
                savegame,
            });
            this.app.analytics.trackUiClick("startgame_adcomplete");
        });
    }

    onContinueButtonClicked() {
        let latestLastUpdate = 0;
        let latestInternalId;
        this.app.savegameMgr.currentData.savegames.forEach(saveGame => {
            if (saveGame.lastUpdate > latestLastUpdate) {
                latestLastUpdate = saveGame.lastUpdate;
                latestInternalId = saveGame.internalId;
            }
        });

        const savegame = this.app.savegameMgr.getSavegameById(latestInternalId);
        savegame.readAsync().then(() => {
            this.moveToState("InGameState", {
                savegame,
            });
        });
    }

    onLeave() {
        this.dialogs.cleanup();
    }
}


/***/ }),

/***/ "./src/js/states/mobile_warning.js":
/*!*****************************************!*\
  !*** ./src/js/states/mobile_warning.js ***!
  \*****************************************/
/*! exports provided: MobileWarningState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MobileWarningState", function() { return MobileWarningState; });
/* harmony import */ var _core_game_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _core_cachebust__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/cachebust */ "./src/js/core/cachebust.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");




class MobileWarningState extends _core_game_state__WEBPACK_IMPORTED_MODULE_0__["GameState"] {
    constructor() {
        super("MobileWarningState");
    }

    getInnerHTML() {
        return `
    
            <img class="logo" src="${Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_1__["cachebust"])("res/logo.png")}" alt="shapez.io Logo">

            <p>
                I'm sorry, but shapez.io is not available on mobile devices yet!
                There is also no estimate when this will change, but feel to make a contribution! It's
                &nbsp;<a href="https://github.com/tobspr/shapez.io" target="_blank">open source</a>!</p>
            
            <p>If you want to play on your computer, you can also get the standalone on Steam:</p>

            
            <a href="${
                _core_config__WEBPACK_IMPORTED_MODULE_2__["THIRDPARTY_URLS"].standaloneStorePage + "?ref=mobile"
            }" class="standaloneLink" target="_blank">Get the shapez.io standalone!</a>
        `;
    }

    getThemeMusic() {
        return null;
    }

    getHasFadeIn() {
        return false;
    }

    onEnter() {
        try {
            if (window.gtag) {
                window.gtag("event", "click", {
                    event_category: "ui",
                    event_label: "mobile_warning",
                });
            }
        } catch (ex) {
            console.warn("Failed to track mobile click:", ex);
        }
    }
    onLeave() {
        // this.dialogs.cleanup();
    }
}


/***/ }),

/***/ "./src/js/states/preload.js":
/*!**********************************!*\
  !*** ./src/js/states/preload.js ***!
  \**********************************/
/*! exports provided: PreloadState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PreloadState", function() { return PreloadState; });
/* harmony import */ var _changelog__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../changelog */ "./src/js/changelog.js");
/* harmony import */ var _core_cachebust__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/cachebust */ "./src/js/core/cachebust.js");
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_game_state__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../core/game_state */ "./src/js/core/game_state.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _game_hints__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../game/hints */ "./src/js/game/hints.js");
/* harmony import */ var _game_hud_parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../game/hud/parts/modal_dialogs */ "./src/js/game/hud/parts/modal_dialogs.js");
/* harmony import */ var _platform_browser_wrapper__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../platform/browser/wrapper */ "./src/js/platform/browser/wrapper.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");











const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_4__["createLogger"])("state/preload");

class PreloadState extends _core_game_state__WEBPACK_IMPORTED_MODULE_3__["GameState"] {
    constructor() {
        super("PreloadState");
    }

    getInnerHTML() {
        return `
            <div class="loadingImage"></div>
            <div class="loadingStatus">
                <span class="desc">Booting</span>
                </div>
            </div>
            <span class="prefab_GameHint"></span>
        `;
    }

    getThemeMusic() {
        return null;
    }

    getHasFadeIn() {
        return false;
    }

    onEnter() {
        this.htmlElement.classList.add("prefab_LoadingState");

        const elementsToRemove = ["#loadingPreload", "#fontPreload"];
        for (let i = 0; i < elementsToRemove.length; ++i) {
            const elem = document.querySelector(elementsToRemove[i]);
            if (elem) {
                elem.remove();
            }
        }

        this.dialogs = new _game_hud_parts_modal_dialogs__WEBPACK_IMPORTED_MODULE_7__["HUDModalDialogs"](null, this.app);
        const dialogsElement = document.body.querySelector(".modalDialogParent");
        this.dialogs.initializeToElement(dialogsElement);

        /** @type {HTMLElement} */
        this.statusText = this.htmlElement.querySelector(".loadingStatus > .desc");

        /** @type {HTMLElement} */
        this.hintsText = this.htmlElement.querySelector(".prefab_GameHint");
        this.lastHintShown = -1000;
        this.nextHintDuration = 0;

        this.currentStatus = "booting";

        this.startLoading();
    }

    onLeave() {
        // this.dialogs.cleanup();
    }

    startLoading() {
        this.setStatus("Booting")

            .then(() => this.setStatus("Creating platform wrapper"))
            .then(() => this.app.platformWrapper.initialize())

            .then(() => this.setStatus("Initializing local storage"))
            .then(() => {
                const wrapper = this.app.platformWrapper;
                if (wrapper instanceof _platform_browser_wrapper__WEBPACK_IMPORTED_MODULE_8__["PlatformWrapperImplBrowser"]) {
                    try {
                        window.localStorage.setItem("local_storage_test", "1");
                        window.localStorage.removeItem("local_storage_test");
                    } catch (ex) {
                        logger.error("Failed to read/write local storage:", ex);
                        return new Promise(() => {
                            alert(`Your brower does not support thirdparty cookies or you have disabled it in your security settings.\n\n
                                In Chrome this setting is called "Block third-party cookies and site data".\n\n
                                Please allow third party cookies and then reload the page.`);
                            // Never return
                        });
                    }
                }
            })

            .then(() => this.setStatus("Creating storage"))
            .then(() => {
                return this.app.storage.initialize();
            })

            .then(() => this.setStatus("Initializing libraries"))
            .then(() => this.app.analytics.initialize())
            .then(() => this.app.gameAnalytics.initialize())

            .then(() => this.setStatus("Initializing settings"))
            .then(() => {
                return this.app.settings.initialize();
            })

            .then(() => {
                // Initialize fullscreen
                if (this.app.platformWrapper.getSupportsFullscreen()) {
                    this.app.platformWrapper.setFullscreen(this.app.settings.getIsFullScreen());
                }
            })

            .then(() => this.setStatus("Initializing language"))
            .then(() => {
                if (this.app.settings.getLanguage() === "auto-detect") {
                    const language = Object(_translations__WEBPACK_IMPORTED_MODULE_9__["autoDetectLanguageId"])();
                    logger.log("Setting language to", language);
                    return this.app.settings.updateLanguage(language);
                }
            })
            .then(() => {
                const language = this.app.settings.getLanguage();
                Object(_translations__WEBPACK_IMPORTED_MODULE_9__["updateApplicationLanguage"])(language);
            })

            .then(() => this.setStatus("Initializing sounds"))
            .then(() => {
                // Notice: We don't await the sounds loading itself
                return this.app.sound.initialize();
            })

            .then(() => {
                this.app.backgroundResourceLoader.startLoading();
            })

            .then(() => this.setStatus("Initializing restrictions"))
            .then(() => {
                return this.app.restrictionMgr.initialize();
            })

            .then(() => this.setStatus("Initializing savegame"))
            .then(() => {
                return this.app.savegameMgr.initialize().catch(err => {
                    logger.error("Failed to initialize savegames:", err);
                    alert(
                        "Your savegames failed to load, it seems your data files got corrupted. I'm so sorry!\n\n(This can happen if your pc crashed while a game was saved).\n\nYou can try re-importing your savegames."
                    );
                    return this.app.savegameMgr.writeAsync();
                });
            })

            .then(() => this.setStatus("Downloading resources"))
            .then(() => {
                return this.app.backgroundResourceLoader.getPromiseForBareGame();
            })

            .then(() => this.setStatus("Checking changelog"))
            .then(() => {
                if (false) {}

                return this.app.storage
                    .readFileAsync("lastversion.bin")
                    .catch(err => {
                        logger.warn("Failed to read lastversion:", err);
                        return "1.2.2";
                    })
                    .then(version => {
                        logger.log("Last version:", version, "App version:", "1.2.2");
                        this.app.storage.writeFileAsync("lastversion.bin", "1.2.2");
                        return version;
                    })
                    .then(version => {
                        let changelogEntries = [];
                        logger.log("Last seen version:", version);

                        for (let i = 0; i < _changelog__WEBPACK_IMPORTED_MODULE_0__["CHANGELOG"].length; ++i) {
                            if (_changelog__WEBPACK_IMPORTED_MODULE_0__["CHANGELOG"][i].version === version) {
                                break;
                            }
                            changelogEntries.push(_changelog__WEBPACK_IMPORTED_MODULE_0__["CHANGELOG"][i]);
                        }
                        if (changelogEntries.length === 0) {
                            return;
                        }

                        let dialogHtml = _translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.updateSummary.desc;
                        for (let i = 0; i < changelogEntries.length; ++i) {
                            const entry = changelogEntries[i];
                            dialogHtml += `
                            <div class="changelogDialogEntry">
                                <span class="version">${entry.version}</span>
                                <span class="date">${entry.date}</span>
                                <ul class="changes">
                                    ${entry.entries.map(text => `<li>${text}</li>`).join("")}
                                </ul>
                            </div>
                        `;
                        }

                        return new Promise(resolve => {
                            this.dialogs.showInfo(_translations__WEBPACK_IMPORTED_MODULE_9__["T"].dialogs.updateSummary.title, dialogHtml).ok.add(resolve);
                        });
                    });
            })

            .then(() => this.setStatus("Launching"))
            .then(
                () => {
                    this.moveToState("MainMenuState");
                },
                err => {
                    this.showFailMessage(err);
                }
            );
    }

    update() {
        const now = performance.now();
        if (now - this.lastHintShown > this.nextHintDuration) {
            this.lastHintShown = now;
            const hintText = Object(_game_hints__WEBPACK_IMPORTED_MODULE_6__["getRandomHint"])();

            this.hintsText.innerHTML = hintText;

            /**
             * Compute how long the user will need to read the hint.
             * We calculate with 130 words per minute, with an average of 5 chars
             * that is 650 characters / minute
             */
            this.nextHintDuration = Math.max(2500, (hintText.length / 650) * 60 * 1000);
        }
    }

    onRender() {
        this.update();
    }

    onBackgroundTick() {
        this.update();
    }

    /**
     *
     * @param {string} text
     */
    setStatus(text) {
        logger.log("✅ " + text);
        this.currentStatus = text;
        this.statusText.innerText = text;
        return Promise.resolve();
    }

    showFailMessage(text) {
        logger.error("App init failed:", text);

        const email = "bugs@shapez.io";

        const subElement = document.createElement("div");
        subElement.classList.add("failureBox");

        subElement.innerHTML = `
                <div class="logo">
                    <img src="${Object(_core_cachebust__WEBPACK_IMPORTED_MODULE_1__["cachebust"])("res/logo.png")}" alt="Shapez.io Logo">
                </div>
                <div class="failureInner">
                    <div class="errorHeader">
                    Failed to initialize application!
                    </div>
                    <div class="errorMessage">
                        ${this.currentStatus} failed:<br/>
                        ${text}
                    </div>

                    <div class="supportHelp">
                    Please send me an email with steps to reproduce and what you did before this happened:
                        <br /><a class="email" href="mailto:${email}?subject=App%20does%20not%20launch">${email}</a>
                    </div>

                    <div class="lower">
                        <button class="resetApp styledButton">Reset App</button>
                        <i>Build ${"1.2.2"} @ ${"b5a96a91"}</i>
                    </div>
                </div>
        `;

        this.htmlElement.classList.add("failure");
        this.htmlElement.appendChild(subElement);

        const resetBtn = subElement.querySelector("button.resetApp");
        this.trackClicks(resetBtn, this.showResetConfirm);

        this.hintsText.remove();
    }

    showResetConfirm() {
        if (confirm("Are you sure you want to reset the app? This will delete all your savegames!")) {
            this.resetApp();
        }
    }

    resetApp() {
        this.app.settings
            .resetEverythingAsync()
            .then(() => {
                this.app.savegameMgr.resetEverythingAsync();
            })
            .then(() => {
                this.app.settings.resetEverythingAsync();
            })
            .then(() => {
                window.location.reload();
            });
    }
}


/***/ }),

/***/ "./src/js/states/settings.js":
/*!***********************************!*\
  !*** ./src/js/states/settings.js ***!
  \***********************************/
/*! exports provided: SettingsState */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SettingsState", function() { return SettingsState; });
/* harmony import */ var _core_textual_game_state__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/textual_game_state */ "./src/js/core/textual_game_state.js");
/* harmony import */ var _core_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/utils */ "./src/js/core/utils.js");
/* harmony import */ var _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../profile/application_settings */ "./src/js/profile/application_settings.js");
/* harmony import */ var _translations__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../translations */ "./src/js/translations.js");





class SettingsState extends _core_textual_game_state__WEBPACK_IMPORTED_MODULE_0__["TextualGameState"] {
    constructor() {
        super("SettingsState");
    }

    getStateHeaderTitle() {
        return _translations__WEBPACK_IMPORTED_MODULE_3__["T"].settings.title;
    }

    getMainContentHTML() {
        return `

        <div class="sidebar">
            ${this.getCategoryButtonsHtml()}

            ${
                this.app.platformWrapper.getSupportsKeyboard()
                    ? `
            <button class="styledButton categoryButton editKeybindings">
            ${_translations__WEBPACK_IMPORTED_MODULE_3__["T"].keybindings.title}
            </button>`
                    : ""
            }

            <div class="other">
                <button class="styledButton about">${_translations__WEBPACK_IMPORTED_MODULE_3__["T"].about.title}</button>

                <div class="versionbar">
                    <div class="buildVersion">${_translations__WEBPACK_IMPORTED_MODULE_3__["T"].global.loading} ...</div>
                </div>
            </div>
        </div>

        <div class="categoryContainer">
            ${this.getSettingsHtml()}
        </div>

        `;
    }

    getCategoryButtonsHtml() {
        return Object.keys(_profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["enumCategories"])
            .map(key => _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["enumCategories"][key])
            .map(
                category =>
                    `
                    <button class="styledButton categoryButton" data-category-btn="${category}">
                        ${_translations__WEBPACK_IMPORTED_MODULE_3__["T"].settings.categories[category]}
                    </button>
                    `
            )
            .join("");
    }

    getSettingsHtml() {
        const categoriesHTML = {};

        Object.keys(_profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["enumCategories"]).forEach(key => {
            const catName = _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["enumCategories"][key];
            categoriesHTML[catName] = `<div class="category" data-category="${catName}">`;
        });

        for (let i = 0; i < _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["allApplicationSettings"].length; ++i) {
            const setting = _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["allApplicationSettings"][i];

            categoriesHTML[setting.categoryId] += setting.getHtml(this.app);
        }

        return Object.keys(categoriesHTML)
            .map(k => categoriesHTML[k] + "</div>")
            .join("");
    }

    renderBuildText() {
        const labelVersion = this.htmlElement.querySelector(".buildVersion");
        const lastBuildMs = new Date().getTime() - 1612259262750;
        const lastBuildText = Object(_core_utils__WEBPACK_IMPORTED_MODULE_1__["formatSecondsToTimeAgo"])(lastBuildMs / 1000.0);

        const version = _translations__WEBPACK_IMPORTED_MODULE_3__["T"].settings.versionBadges["dev"];

        labelVersion.innerHTML = `
            <span class='version'>
                ${"1.2.2"} @ ${version} @ ${"b5a96a91"}
            </span>
            <span class='buildTime'>
                ${_translations__WEBPACK_IMPORTED_MODULE_3__["T"].settings.buildDate.replace("<at-date>", lastBuildText)}<br />
            </span>`;
    }

    onEnter(payload) {
        this.renderBuildText();
        this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
            preventDefault: false,
        });

        const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");

        if (keybindingsButton) {
            this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false });
        }

        this.initSettings();
        this.initCategoryButtons();

        this.htmlElement.querySelector(".category").classList.add("active");
        this.htmlElement.querySelector(".categoryButton").classList.add("active");
    }

    setActiveCategory(category) {
        const previousCategory = this.htmlElement.querySelector(".category.active");
        const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active");

        if (previousCategory.getAttribute("data-category") == category) {
            return;
        }

        previousCategory.classList.remove("active");
        previousCategoryButton.classList.remove("active");

        const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']");
        const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']");

        newCategory.classList.add("active");
        newCategoryButton.classList.add("active");
    }

    initSettings() {
        _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["allApplicationSettings"].forEach(setting => {
            /** @type {HTMLElement} */
            const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']");
            setting.bind(this.app, element, this.dialogs);
            setting.syncValueToElement();
            this.trackClicks(
                element,
                () => {
                    setting.modify();
                },
                { preventDefault: false }
            );
        });
    }

    initCategoryButtons() {
        Object.keys(_profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["enumCategories"]).forEach(key => {
            const category = _profile_application_settings__WEBPACK_IMPORTED_MODULE_2__["enumCategories"][key];
            const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']");
            this.trackClicks(
                button,
                () => {
                    this.setActiveCategory(category);
                },
                { preventDefault: false }
            );
        });
    }

    onAboutClicked() {
        this.moveToStateAddGoBack("AboutState");
    }

    onKeybindingsClicked() {
        this.moveToStateAddGoBack("KeybindingsState");
    }
}


/***/ }),

/***/ "./src/js/translations.js":
/*!********************************!*\
  !*** ./src/js/translations.js ***!
  \********************************/
/*! exports provided: T, applyLanguage, autoDetectLanguageId, updateApplicationLanguage */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "T", function() { return T; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "applyLanguage", function() { return applyLanguage; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "autoDetectLanguageId", function() { return autoDetectLanguageId; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "updateApplicationLanguage", function() { return updateApplicationLanguage; });
/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./core/config */ "./src/js/core/config.js");
/* harmony import */ var _core_logging__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./core/logging */ "./src/js/core/logging.js");
/* harmony import */ var _languages__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./languages */ "./src/js/languages.js");




const logger = Object(_core_logging__WEBPACK_IMPORTED_MODULE_1__["createLogger"])("translations");

// @ts-ignore
const baseTranslations = __webpack_require__(/*! ./built-temp/base-en.json */ "./src/js/built-temp/base-en.json");

let T = baseTranslations;

if (false) {}

function applyLanguage(languageCode) {
    logger.log("Applying language:", languageCode);
    const data = _languages__WEBPACK_IMPORTED_MODULE_2__["LANGUAGES"][languageCode];
    if (!data) {
        logger.error("Language not found:", languageCode);
        return false;
    }
}

// Language key is something like de-DE or en or en-US
function mapLanguageCodeToId(languageKey) {
    const key = languageKey.toLowerCase();
    const shortKey = key.split("-")[0];

    // Try to match by key or short key
    for (const id in _languages__WEBPACK_IMPORTED_MODULE_2__["LANGUAGES"]) {
        const data = _languages__WEBPACK_IMPORTED_MODULE_2__["LANGUAGES"][id];
        const code = data.code.toLowerCase();
        if (code === key) {
            console.log("-> Match", languageKey, "->", id);
            return id;
        }
        if (code === shortKey) {
            console.log("-> Match by short key", languageKey, "->", id);
            return id;
        }
    }

    // If none found, try to find a better alternative by using the base language at least
    for (const id in _languages__WEBPACK_IMPORTED_MODULE_2__["LANGUAGES"]) {
        const data = _languages__WEBPACK_IMPORTED_MODULE_2__["LANGUAGES"][id];
        const code = data.code.toLowerCase();
        const shortCode = code.split("-")[0];

        if (shortCode === key) {
            console.log("-> Desperate Match", languageKey, "->", id);
            return id;
        }
        if (shortCode === shortKey) {
            console.log("-> Desperate Match by short key", languageKey, "->", id);
            return id;
        }
    }

    return null;
}

/**
 * Tries to auto-detect a language
 * @returns {string}
 */
function autoDetectLanguageId() {
    let languages = [];
    if (navigator.languages) {
        languages = navigator.languages.slice();
    } else if (navigator.language) {
        languages = [navigator.language];
    } else {
        logger.warn("Navigator has no languages prop");
    }

    for (let i = 0; i < languages.length; ++i) {
        logger.log("Trying to find language target for", languages[i]);
        const trans = mapLanguageCodeToId(languages[i]);
        if (trans) {
            return trans;
        }
    }

    // Fallback
    return "en";
}

function matchDataRecursive(dest, src) {
    if (typeof dest !== "object" || typeof src !== "object") {
        return;
    }

    for (const key in dest) {
        if (src[key]) {
            // console.log("copy", key);
            const data = dest[key];
            if (typeof data === "object") {
                matchDataRecursive(dest[key], src[key]);
            } else if (typeof data === "string" || typeof data === "number") {
                // console.log("match string", key);
                dest[key] = src[key];
            } else {
                logger.log("Unknown type:", typeof data, "in key", key);
            }
        }
    }
}

function updateApplicationLanguage(id) {
    logger.log("Setting application language:", id);

    const data = _languages__WEBPACK_IMPORTED_MODULE_2__["LANGUAGES"][id];

    if (!data) {
        logger.error("Unknown language:", id);
        return;
    }

    if (data.data) {
        logger.log("Applying translations ...");
        matchDataRecursive(T, data.data);
    }
}


/***/ }),

/***/ "./src/js/webworkers/background_animation_frame_emittter.worker.js":
/*!*************************************************************************!*\
  !*** ./src/js/webworkers/background_animation_frame_emittter.worker.js ***!
  \*************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = function() {
  return __webpack_require__(/*! !./node_modules/worker-loader/dist/workers/InlineWorker.js */ "./node_modules/worker-loader/dist/workers/InlineWorker.js")("/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./node_modules/webpack-strip-block/index.js?!./src/js/webworkers/background_animation_frame_emittter.worker.js\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./node_modules/webpack-strip-block/index.js?!./src/js/webworkers/background_animation_frame_emittter.worker.js\":\n/*!**********************************************************************************************************************!*\\\n  !*** ./node_modules/webpack-strip-block??ref--7-0!./src/js/webworkers/background_animation_frame_emittter.worker.js ***!\n  \\**********************************************************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\n// We clamp high deltas so 30 fps is fairly ok\nconst bgFps = 30;\nconst desiredMsDelay = 1000 / bgFps;\n\nlet lastTick = performance.now();\n\nfunction tick() {\n    const now = performance.now();\n    const delta = now - lastTick;\n    lastTick = now;\n\n    // @ts-ignore\n    self.postMessage({ delta });\n}\n\nsetInterval(tick, desiredMsDelay);\n\n\n/***/ })\n\n/******/ });\n//# sourceMappingURL=5bcbab89b4b3d3bf4467.worker.js.map", null);
};

/***/ }),

/***/ "./src/js/webworkers/compression.worker.js":
/*!*************************************************!*\
  !*** ./src/js/webworkers/compression.worker.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = function() {
  return __webpack_require__(/*! !./node_modules/worker-loader/dist/workers/InlineWorker.js */ "./node_modules/worker-loader/dist/workers/InlineWorker.js")("/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./node_modules/webpack-strip-block/index.js?!./src/js/webworkers/compression.worker.js\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./gulp/node_modules/base64-js/index.js\":\n/*!**********************************************!*\\\n  !*** ./gulp/node_modules/base64-js/index.js ***!\n  \\**********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n  lookup[i] = code[i]\n  revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n  var len = b64.length\n\n  if (len % 4 > 0) {\n    throw new Error('Invalid string. Length must be a multiple of 4')\n  }\n\n  // Trim off extra bytes after placeholder bytes are found\n  // See: https://github.com/beatgammit/base64-js/issues/42\n  var validLen = b64.indexOf('=')\n  if (validLen === -1) validLen = len\n\n  var placeHoldersLen = validLen === len\n    ? 0\n    : 4 - (validLen % 4)\n\n  return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n  var lens = getLens(b64)\n  var validLen = lens[0]\n  var placeHoldersLen = lens[1]\n  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n  var tmp\n  var lens = getLens(b64)\n  var validLen = lens[0]\n  var placeHoldersLen = lens[1]\n\n  var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n  var curByte = 0\n\n  // if there are placeholders, only get up to the last complete 4 chars\n  var len = placeHoldersLen > 0\n    ? validLen - 4\n    : validLen\n\n  var i\n  for (i = 0; i < len; i += 4) {\n    tmp =\n      (revLookup[b64.charCodeAt(i)] << 18) |\n      (revLookup[b64.charCodeAt(i + 1)] << 12) |\n      (revLookup[b64.charCodeAt(i + 2)] << 6) |\n      revLookup[b64.charCodeAt(i + 3)]\n    arr[curByte++] = (tmp >> 16) & 0xFF\n    arr[curByte++] = (tmp >> 8) & 0xFF\n    arr[curByte++] = tmp & 0xFF\n  }\n\n  if (placeHoldersLen === 2) {\n    tmp =\n      (revLookup[b64.charCodeAt(i)] << 2) |\n      (revLookup[b64.charCodeAt(i + 1)] >> 4)\n    arr[curByte++] = tmp & 0xFF\n  }\n\n  if (placeHoldersLen === 1) {\n    tmp =\n      (revLookup[b64.charCodeAt(i)] << 10) |\n      (revLookup[b64.charCodeAt(i + 1)] << 4) |\n      (revLookup[b64.charCodeAt(i + 2)] >> 2)\n    arr[curByte++] = (tmp >> 8) & 0xFF\n    arr[curByte++] = tmp & 0xFF\n  }\n\n  return arr\n}\n\nfunction tripletToBase64 (num) {\n  return lookup[num >> 18 & 0x3F] +\n    lookup[num >> 12 & 0x3F] +\n    lookup[num >> 6 & 0x3F] +\n    lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n  var tmp\n  var output = []\n  for (var i = start; i < end; i += 3) {\n    tmp =\n      ((uint8[i] << 16) & 0xFF0000) +\n      ((uint8[i + 1] << 8) & 0xFF00) +\n      (uint8[i + 2] & 0xFF)\n    output.push(tripletToBase64(tmp))\n  }\n  return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n  var tmp\n  var len = uint8.length\n  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n  var parts = []\n  var maxChunkLength = 16383 // must be multiple of 3\n\n  // go through the array every three bytes, we'll deal with trailing stuff later\n  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n    parts.push(encodeChunk(\n      uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)\n    ))\n  }\n\n  // pad the end with zeros, but make sure to not forget the extra bytes\n  if (extraBytes === 1) {\n    tmp = uint8[len - 1]\n    parts.push(\n      lookup[tmp >> 2] +\n      lookup[(tmp << 4) & 0x3F] +\n      '=='\n    )\n  } else if (extraBytes === 2) {\n    tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n    parts.push(\n      lookup[tmp >> 10] +\n      lookup[(tmp >> 4) & 0x3F] +\n      lookup[(tmp << 2) & 0x3F] +\n      '='\n    )\n  }\n\n  return parts.join('')\n}\n\n\n/***/ }),\n\n/***/ \"./gulp/node_modules/ieee754/index.js\":\n/*!********************************************!*\\\n  !*** ./gulp/node_modules/ieee754/index.js ***!\n  \\********************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nexports.read = function (buffer, offset, isLE, mLen, nBytes) {\n  var e, m\n  var eLen = (nBytes * 8) - mLen - 1\n  var eMax = (1 << eLen) - 1\n  var eBias = eMax >> 1\n  var nBits = -7\n  var i = isLE ? (nBytes - 1) : 0\n  var d = isLE ? -1 : 1\n  var s = buffer[offset + i]\n\n  i += d\n\n  e = s & ((1 << (-nBits)) - 1)\n  s >>= (-nBits)\n  nBits += eLen\n  for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n  m = e & ((1 << (-nBits)) - 1)\n  e >>= (-nBits)\n  nBits += mLen\n  for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n  if (e === 0) {\n    e = 1 - eBias\n  } else if (e === eMax) {\n    return m ? NaN : ((s ? -1 : 1) * Infinity)\n  } else {\n    m = m + Math.pow(2, mLen)\n    e = e - eBias\n  }\n  return (s ? -1 : 1) * m * Math.pow(2, e - mLen)\n}\n\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n  var e, m, c\n  var eLen = (nBytes * 8) - mLen - 1\n  var eMax = (1 << eLen) - 1\n  var eBias = eMax >> 1\n  var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)\n  var i = isLE ? 0 : (nBytes - 1)\n  var d = isLE ? 1 : -1\n  var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0\n\n  value = Math.abs(value)\n\n  if (isNaN(value) || value === Infinity) {\n    m = isNaN(value) ? 1 : 0\n    e = eMax\n  } else {\n    e = Math.floor(Math.log(value) / Math.LN2)\n    if (value * (c = Math.pow(2, -e)) < 1) {\n      e--\n      c *= 2\n    }\n    if (e + eBias >= 1) {\n      value += rt / c\n    } else {\n      value += rt * Math.pow(2, 1 - eBias)\n    }\n    if (value * c >= 2) {\n      e++\n      c /= 2\n    }\n\n    if (e + eBias >= eMax) {\n      m = 0\n      e = eMax\n    } else if (e + eBias >= 1) {\n      m = ((value * c) - 1) * Math.pow(2, mLen)\n      e = e + eBias\n    } else {\n      m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)\n      e = 0\n    }\n  }\n\n  for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n\n  e = (e << mLen) | m\n  eLen += mLen\n  for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n\n  buffer[offset + i - d] |= s * 128\n}\n\n\n/***/ }),\n\n/***/ \"./gulp/node_modules/isarray/index.js\":\n/*!********************************************!*\\\n  !*** ./gulp/node_modules/isarray/index.js ***!\n  \\********************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nvar toString = {}.toString;\n\nmodule.exports = Array.isArray || function (arr) {\n  return toString.call(arr) == '[object Array]';\n};\n\n\n/***/ }),\n\n/***/ \"./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js\":\n/*!**************************************************************************!*\\\n  !*** ./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js ***!\n  \\**************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(global) {/*!\n * The buffer module from node.js, for the browser.\n *\n * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>\n * @license  MIT\n */\n/* eslint-disable no-proto */\n\n\n\nvar base64 = __webpack_require__(/*! base64-js */ \"./gulp/node_modules/base64-js/index.js\")\nvar ieee754 = __webpack_require__(/*! ieee754 */ \"./gulp/node_modules/ieee754/index.js\")\nvar isArray = __webpack_require__(/*! isarray */ \"./gulp/node_modules/isarray/index.js\")\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n *   === true    Use Uint8Array implementation (fastest)\n *   === false   Use Object implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * Due to various browser bugs, sometimes the Object implementation will be used even\n * when the browser supports typed arrays.\n *\n * Note:\n *\n *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,\n *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.\n *\n *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.\n *\n *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of\n *     incorrect length in some situations.\n\n * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they\n * get the Object implementation, which is slower but behaves correctly.\n */\nBuffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined\n  ? global.TYPED_ARRAY_SUPPORT\n  : typedArraySupport()\n\n/*\n * Export kMaxLength after typed array support is determined.\n */\nexports.kMaxLength = kMaxLength()\n\nfunction typedArraySupport () {\n  try {\n    var arr = new Uint8Array(1)\n    arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}\n    return arr.foo() === 42 && // typed array instances can be augmented\n        typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`\n        arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`\n  } catch (e) {\n    return false\n  }\n}\n\nfunction kMaxLength () {\n  return Buffer.TYPED_ARRAY_SUPPORT\n    ? 0x7fffffff\n    : 0x3fffffff\n}\n\nfunction createBuffer (that, length) {\n  if (kMaxLength() < length) {\n    throw new RangeError('Invalid typed array length')\n  }\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    // Return an augmented `Uint8Array` instance, for best performance\n    that = new Uint8Array(length)\n    that.__proto__ = Buffer.prototype\n  } else {\n    // Fallback: Return an object instance of the Buffer class\n    if (that === null) {\n      that = new Buffer(length)\n    }\n    that.length = length\n  }\n\n  return that\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n  if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {\n    return new Buffer(arg, encodingOrOffset, length)\n  }\n\n  // Common case.\n  if (typeof arg === 'number') {\n    if (typeof encodingOrOffset === 'string') {\n      throw new Error(\n        'If encoding is specified then the first argument must be a string'\n      )\n    }\n    return allocUnsafe(this, arg)\n  }\n  return from(this, arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\n// TODO: Legacy, not needed anymore. Remove in next major version.\nBuffer._augment = function (arr) {\n  arr.__proto__ = Buffer.prototype\n  return arr\n}\n\nfunction from (that, value, encodingOrOffset, length) {\n  if (typeof value === 'number') {\n    throw new TypeError('\"value\" argument must not be a number')\n  }\n\n  if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {\n    return fromArrayBuffer(that, value, encodingOrOffset, length)\n  }\n\n  if (typeof value === 'string') {\n    return fromString(that, value, encodingOrOffset)\n  }\n\n  return fromObject(that, value)\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n  return from(null, value, encodingOrOffset, length)\n}\n\nif (Buffer.TYPED_ARRAY_SUPPORT) {\n  Buffer.prototype.__proto__ = Uint8Array.prototype\n  Buffer.__proto__ = Uint8Array\n  if (typeof Symbol !== 'undefined' && Symbol.species &&\n      Buffer[Symbol.species] === Buffer) {\n    // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97\n    Object.defineProperty(Buffer, Symbol.species, {\n      value: null,\n      configurable: true\n    })\n  }\n}\n\nfunction assertSize (size) {\n  if (typeof size !== 'number') {\n    throw new TypeError('\"size\" argument must be a number')\n  } else if (size < 0) {\n    throw new RangeError('\"size\" argument must not be negative')\n  }\n}\n\nfunction alloc (that, size, fill, encoding) {\n  assertSize(size)\n  if (size <= 0) {\n    return createBuffer(that, size)\n  }\n  if (fill !== undefined) {\n    // Only pay attention to encoding if it's a string. This\n    // prevents accidentally sending in a number that would\n    // be interpretted as a start offset.\n    return typeof encoding === 'string'\n      ? createBuffer(that, size).fill(fill, encoding)\n      : createBuffer(that, size).fill(fill)\n  }\n  return createBuffer(that, size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n  return alloc(null, size, fill, encoding)\n}\n\nfunction allocUnsafe (that, size) {\n  assertSize(size)\n  that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)\n  if (!Buffer.TYPED_ARRAY_SUPPORT) {\n    for (var i = 0; i < size; ++i) {\n      that[i] = 0\n    }\n  }\n  return that\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n  return allocUnsafe(null, size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n  return allocUnsafe(null, size)\n}\n\nfunction fromString (that, string, encoding) {\n  if (typeof encoding !== 'string' || encoding === '') {\n    encoding = 'utf8'\n  }\n\n  if (!Buffer.isEncoding(encoding)) {\n    throw new TypeError('\"encoding\" must be a valid string encoding')\n  }\n\n  var length = byteLength(string, encoding) | 0\n  that = createBuffer(that, length)\n\n  var actual = that.write(string, encoding)\n\n  if (actual !== length) {\n    // Writing a hex string, for example, that contains invalid characters will\n    // cause everything after the first invalid character to be ignored. (e.g.\n    // 'abxxcd' will be treated as 'ab')\n    that = that.slice(0, actual)\n  }\n\n  return that\n}\n\nfunction fromArrayLike (that, array) {\n  var length = array.length < 0 ? 0 : checked(array.length) | 0\n  that = createBuffer(that, length)\n  for (var i = 0; i < length; i += 1) {\n    that[i] = array[i] & 255\n  }\n  return that\n}\n\nfunction fromArrayBuffer (that, array, byteOffset, length) {\n  array.byteLength // this throws if `array` is not a valid ArrayBuffer\n\n  if (byteOffset < 0 || array.byteLength < byteOffset) {\n    throw new RangeError('\\'offset\\' is out of bounds')\n  }\n\n  if (array.byteLength < byteOffset + (length || 0)) {\n    throw new RangeError('\\'length\\' is out of bounds')\n  }\n\n  if (byteOffset === undefined && length === undefined) {\n    array = new Uint8Array(array)\n  } else if (length === undefined) {\n    array = new Uint8Array(array, byteOffset)\n  } else {\n    array = new Uint8Array(array, byteOffset, length)\n  }\n\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    // Return an augmented `Uint8Array` instance, for best performance\n    that = array\n    that.__proto__ = Buffer.prototype\n  } else {\n    // Fallback: Return an object instance of the Buffer class\n    that = fromArrayLike(that, array)\n  }\n  return that\n}\n\nfunction fromObject (that, obj) {\n  if (Buffer.isBuffer(obj)) {\n    var len = checked(obj.length) | 0\n    that = createBuffer(that, len)\n\n    if (that.length === 0) {\n      return that\n    }\n\n    obj.copy(that, 0, 0, len)\n    return that\n  }\n\n  if (obj) {\n    if ((typeof ArrayBuffer !== 'undefined' &&\n        obj.buffer instanceof ArrayBuffer) || 'length' in obj) {\n      if (typeof obj.length !== 'number' || isnan(obj.length)) {\n        return createBuffer(that, 0)\n      }\n      return fromArrayLike(that, obj)\n    }\n\n    if (obj.type === 'Buffer' && isArray(obj.data)) {\n      return fromArrayLike(that, obj.data)\n    }\n  }\n\n  throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')\n}\n\nfunction checked (length) {\n  // Note: cannot use `length < kMaxLength()` here because that fails when\n  // length is NaN (which is otherwise coerced to zero.)\n  if (length >= kMaxLength()) {\n    throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n                         'size: 0x' + kMaxLength().toString(16) + ' bytes')\n  }\n  return length | 0\n}\n\nfunction SlowBuffer (length) {\n  if (+length != length) { // eslint-disable-line eqeqeq\n    length = 0\n  }\n  return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n  return !!(b != null && b._isBuffer)\n}\n\nBuffer.compare = function compare (a, b) {\n  if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n    throw new TypeError('Arguments must be Buffers')\n  }\n\n  if (a === b) return 0\n\n  var x = a.length\n  var y = b.length\n\n  for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n    if (a[i] !== b[i]) {\n      x = a[i]\n      y = b[i]\n      break\n    }\n  }\n\n  if (x < y) return -1\n  if (y < x) return 1\n  return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n  switch (String(encoding).toLowerCase()) {\n    case 'hex':\n    case 'utf8':\n    case 'utf-8':\n    case 'ascii':\n    case 'latin1':\n    case 'binary':\n    case 'base64':\n    case 'ucs2':\n    case 'ucs-2':\n    case 'utf16le':\n    case 'utf-16le':\n      return true\n    default:\n      return false\n  }\n}\n\nBuffer.concat = function concat (list, length) {\n  if (!isArray(list)) {\n    throw new TypeError('\"list\" argument must be an Array of Buffers')\n  }\n\n  if (list.length === 0) {\n    return Buffer.alloc(0)\n  }\n\n  var i\n  if (length === undefined) {\n    length = 0\n    for (i = 0; i < list.length; ++i) {\n      length += list[i].length\n    }\n  }\n\n  var buffer = Buffer.allocUnsafe(length)\n  var pos = 0\n  for (i = 0; i < list.length; ++i) {\n    var buf = list[i]\n    if (!Buffer.isBuffer(buf)) {\n      throw new TypeError('\"list\" argument must be an Array of Buffers')\n    }\n    buf.copy(buffer, pos)\n    pos += buf.length\n  }\n  return buffer\n}\n\nfunction byteLength (string, encoding) {\n  if (Buffer.isBuffer(string)) {\n    return string.length\n  }\n  if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&\n      (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {\n    return string.byteLength\n  }\n  if (typeof string !== 'string') {\n    string = '' + string\n  }\n\n  var len = string.length\n  if (len === 0) return 0\n\n  // Use a for loop to avoid recursion\n  var loweredCase = false\n  for (;;) {\n    switch (encoding) {\n      case 'ascii':\n      case 'latin1':\n      case 'binary':\n        return len\n      case 'utf8':\n      case 'utf-8':\n      case undefined:\n        return utf8ToBytes(string).length\n      case 'ucs2':\n      case 'ucs-2':\n      case 'utf16le':\n      case 'utf-16le':\n        return len * 2\n      case 'hex':\n        return len >>> 1\n      case 'base64':\n        return base64ToBytes(string).length\n      default:\n        if (loweredCase) return utf8ToBytes(string).length // assume utf8\n        encoding = ('' + encoding).toLowerCase()\n        loweredCase = true\n    }\n  }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n  var loweredCase = false\n\n  // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n  // property of a typed array.\n\n  // This behaves neither like String nor Uint8Array in that we set start/end\n  // to their upper/lower bounds if the value passed is out of range.\n  // undefined is handled specially as per ECMA-262 6th Edition,\n  // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n  if (start === undefined || start < 0) {\n    start = 0\n  }\n  // Return early if start > this.length. Done here to prevent potential uint32\n  // coercion fail below.\n  if (start > this.length) {\n    return ''\n  }\n\n  if (end === undefined || end > this.length) {\n    end = this.length\n  }\n\n  if (end <= 0) {\n    return ''\n  }\n\n  // Force coersion to uint32. This will also coerce falsey/NaN values to 0.\n  end >>>= 0\n  start >>>= 0\n\n  if (end <= start) {\n    return ''\n  }\n\n  if (!encoding) encoding = 'utf8'\n\n  while (true) {\n    switch (encoding) {\n      case 'hex':\n        return hexSlice(this, start, end)\n\n      case 'utf8':\n      case 'utf-8':\n        return utf8Slice(this, start, end)\n\n      case 'ascii':\n        return asciiSlice(this, start, end)\n\n      case 'latin1':\n      case 'binary':\n        return latin1Slice(this, start, end)\n\n      case 'base64':\n        return base64Slice(this, start, end)\n\n      case 'ucs2':\n      case 'ucs-2':\n      case 'utf16le':\n      case 'utf-16le':\n        return utf16leSlice(this, start, end)\n\n      default:\n        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n        encoding = (encoding + '').toLowerCase()\n        loweredCase = true\n    }\n  }\n}\n\n// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect\n// Buffer instances.\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n  var i = b[n]\n  b[n] = b[m]\n  b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n  var len = this.length\n  if (len % 2 !== 0) {\n    throw new RangeError('Buffer size must be a multiple of 16-bits')\n  }\n  for (var i = 0; i < len; i += 2) {\n    swap(this, i, i + 1)\n  }\n  return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n  var len = this.length\n  if (len % 4 !== 0) {\n    throw new RangeError('Buffer size must be a multiple of 32-bits')\n  }\n  for (var i = 0; i < len; i += 4) {\n    swap(this, i, i + 3)\n    swap(this, i + 1, i + 2)\n  }\n  return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n  var len = this.length\n  if (len % 8 !== 0) {\n    throw new RangeError('Buffer size must be a multiple of 64-bits')\n  }\n  for (var i = 0; i < len; i += 8) {\n    swap(this, i, i + 7)\n    swap(this, i + 1, i + 6)\n    swap(this, i + 2, i + 5)\n    swap(this, i + 3, i + 4)\n  }\n  return this\n}\n\nBuffer.prototype.toString = function toString () {\n  var length = this.length | 0\n  if (length === 0) return ''\n  if (arguments.length === 0) return utf8Slice(this, 0, length)\n  return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.equals = function equals (b) {\n  if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n  if (this === b) return true\n  return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n  var str = ''\n  var max = exports.INSPECT_MAX_BYTES\n  if (this.length > 0) {\n    str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')\n    if (this.length > max) str += ' ... '\n  }\n  return '<Buffer ' + str + '>'\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n  if (!Buffer.isBuffer(target)) {\n    throw new TypeError('Argument must be a Buffer')\n  }\n\n  if (start === undefined) {\n    start = 0\n  }\n  if (end === undefined) {\n    end = target ? target.length : 0\n  }\n  if (thisStart === undefined) {\n    thisStart = 0\n  }\n  if (thisEnd === undefined) {\n    thisEnd = this.length\n  }\n\n  if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n    throw new RangeError('out of range index')\n  }\n\n  if (thisStart >= thisEnd && start >= end) {\n    return 0\n  }\n  if (thisStart >= thisEnd) {\n    return -1\n  }\n  if (start >= end) {\n    return 1\n  }\n\n  start >>>= 0\n  end >>>= 0\n  thisStart >>>= 0\n  thisEnd >>>= 0\n\n  if (this === target) return 0\n\n  var x = thisEnd - thisStart\n  var y = end - start\n  var len = Math.min(x, y)\n\n  var thisCopy = this.slice(thisStart, thisEnd)\n  var targetCopy = target.slice(start, end)\n\n  for (var i = 0; i < len; ++i) {\n    if (thisCopy[i] !== targetCopy[i]) {\n      x = thisCopy[i]\n      y = targetCopy[i]\n      break\n    }\n  }\n\n  if (x < y) return -1\n  if (y < x) return 1\n  return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n  // Empty buffer means no match\n  if (buffer.length === 0) return -1\n\n  // Normalize byteOffset\n  if (typeof byteOffset === 'string') {\n    encoding = byteOffset\n    byteOffset = 0\n  } else if (byteOffset > 0x7fffffff) {\n    byteOffset = 0x7fffffff\n  } else if (byteOffset < -0x80000000) {\n    byteOffset = -0x80000000\n  }\n  byteOffset = +byteOffset  // Coerce to Number.\n  if (isNaN(byteOffset)) {\n    // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n    byteOffset = dir ? 0 : (buffer.length - 1)\n  }\n\n  // Normalize byteOffset: negative offsets start from the end of the buffer\n  if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n  if (byteOffset >= buffer.length) {\n    if (dir) return -1\n    else byteOffset = buffer.length - 1\n  } else if (byteOffset < 0) {\n    if (dir) byteOffset = 0\n    else return -1\n  }\n\n  // Normalize val\n  if (typeof val === 'string') {\n    val = Buffer.from(val, encoding)\n  }\n\n  // Finally, search either indexOf (if dir is true) or lastIndexOf\n  if (Buffer.isBuffer(val)) {\n    // Special case: looking for empty string/buffer always fails\n    if (val.length === 0) {\n      return -1\n    }\n    return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n  } else if (typeof val === 'number') {\n    val = val & 0xFF // Search for a byte value [0-255]\n    if (Buffer.TYPED_ARRAY_SUPPORT &&\n        typeof Uint8Array.prototype.indexOf === 'function') {\n      if (dir) {\n        return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n      } else {\n        return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n      }\n    }\n    return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)\n  }\n\n  throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n  var indexSize = 1\n  var arrLength = arr.length\n  var valLength = val.length\n\n  if (encoding !== undefined) {\n    encoding = String(encoding).toLowerCase()\n    if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n        encoding === 'utf16le' || encoding === 'utf-16le') {\n      if (arr.length < 2 || val.length < 2) {\n        return -1\n      }\n      indexSize = 2\n      arrLength /= 2\n      valLength /= 2\n      byteOffset /= 2\n    }\n  }\n\n  function read (buf, i) {\n    if (indexSize === 1) {\n      return buf[i]\n    } else {\n      return buf.readUInt16BE(i * indexSize)\n    }\n  }\n\n  var i\n  if (dir) {\n    var foundIndex = -1\n    for (i = byteOffset; i < arrLength; i++) {\n      if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n        if (foundIndex === -1) foundIndex = i\n        if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n      } else {\n        if (foundIndex !== -1) i -= i - foundIndex\n        foundIndex = -1\n      }\n    }\n  } else {\n    if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n    for (i = byteOffset; i >= 0; i--) {\n      var found = true\n      for (var j = 0; j < valLength; j++) {\n        if (read(arr, i + j) !== read(val, j)) {\n          found = false\n          break\n        }\n      }\n      if (found) return i\n    }\n  }\n\n  return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n  return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n  return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n  return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n  offset = Number(offset) || 0\n  var remaining = buf.length - offset\n  if (!length) {\n    length = remaining\n  } else {\n    length = Number(length)\n    if (length > remaining) {\n      length = remaining\n    }\n  }\n\n  // must be an even number of digits\n  var strLen = string.length\n  if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')\n\n  if (length > strLen / 2) {\n    length = strLen / 2\n  }\n  for (var i = 0; i < length; ++i) {\n    var parsed = parseInt(string.substr(i * 2, 2), 16)\n    if (isNaN(parsed)) return i\n    buf[offset + i] = parsed\n  }\n  return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n  return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n  return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction latin1Write (buf, string, offset, length) {\n  return asciiWrite(buf, string, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n  return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n  return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n  // Buffer#write(string)\n  if (offset === undefined) {\n    encoding = 'utf8'\n    length = this.length\n    offset = 0\n  // Buffer#write(string, encoding)\n  } else if (length === undefined && typeof offset === 'string') {\n    encoding = offset\n    length = this.length\n    offset = 0\n  // Buffer#write(string, offset[, length][, encoding])\n  } else if (isFinite(offset)) {\n    offset = offset | 0\n    if (isFinite(length)) {\n      length = length | 0\n      if (encoding === undefined) encoding = 'utf8'\n    } else {\n      encoding = length\n      length = undefined\n    }\n  // legacy write(string, encoding, offset, length) - remove in v0.13\n  } else {\n    throw new Error(\n      'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n    )\n  }\n\n  var remaining = this.length - offset\n  if (length === undefined || length > remaining) length = remaining\n\n  if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n    throw new RangeError('Attempt to write outside buffer bounds')\n  }\n\n  if (!encoding) encoding = 'utf8'\n\n  var loweredCase = false\n  for (;;) {\n    switch (encoding) {\n      case 'hex':\n        return hexWrite(this, string, offset, length)\n\n      case 'utf8':\n      case 'utf-8':\n        return utf8Write(this, string, offset, length)\n\n      case 'ascii':\n        return asciiWrite(this, string, offset, length)\n\n      case 'latin1':\n      case 'binary':\n        return latin1Write(this, string, offset, length)\n\n      case 'base64':\n        // Warning: maxLength not taken into account in base64Write\n        return base64Write(this, string, offset, length)\n\n      case 'ucs2':\n      case 'ucs-2':\n      case 'utf16le':\n      case 'utf-16le':\n        return ucs2Write(this, string, offset, length)\n\n      default:\n        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n        encoding = ('' + encoding).toLowerCase()\n        loweredCase = true\n    }\n  }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n  return {\n    type: 'Buffer',\n    data: Array.prototype.slice.call(this._arr || this, 0)\n  }\n}\n\nfunction base64Slice (buf, start, end) {\n  if (start === 0 && end === buf.length) {\n    return base64.fromByteArray(buf)\n  } else {\n    return base64.fromByteArray(buf.slice(start, end))\n  }\n}\n\nfunction utf8Slice (buf, start, end) {\n  end = Math.min(buf.length, end)\n  var res = []\n\n  var i = start\n  while (i < end) {\n    var firstByte = buf[i]\n    var codePoint = null\n    var bytesPerSequence = (firstByte > 0xEF) ? 4\n      : (firstByte > 0xDF) ? 3\n      : (firstByte > 0xBF) ? 2\n      : 1\n\n    if (i + bytesPerSequence <= end) {\n      var secondByte, thirdByte, fourthByte, tempCodePoint\n\n      switch (bytesPerSequence) {\n        case 1:\n          if (firstByte < 0x80) {\n            codePoint = firstByte\n          }\n          break\n        case 2:\n          secondByte = buf[i + 1]\n          if ((secondByte & 0xC0) === 0x80) {\n            tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n            if (tempCodePoint > 0x7F) {\n              codePoint = tempCodePoint\n            }\n          }\n          break\n        case 3:\n          secondByte = buf[i + 1]\n          thirdByte = buf[i + 2]\n          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n            tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n            if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n              codePoint = tempCodePoint\n            }\n          }\n          break\n        case 4:\n          secondByte = buf[i + 1]\n          thirdByte = buf[i + 2]\n          fourthByte = buf[i + 3]\n          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n            tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n            if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n              codePoint = tempCodePoint\n            }\n          }\n      }\n    }\n\n    if (codePoint === null) {\n      // we did not generate a valid codePoint so insert a\n      // replacement char (U+FFFD) and advance only 1 byte\n      codePoint = 0xFFFD\n      bytesPerSequence = 1\n    } else if (codePoint > 0xFFFF) {\n      // encode to utf16 (surrogate pair dance)\n      codePoint -= 0x10000\n      res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n      codePoint = 0xDC00 | codePoint & 0x3FF\n    }\n\n    res.push(codePoint)\n    i += bytesPerSequence\n  }\n\n  return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nvar MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n  var len = codePoints.length\n  if (len <= MAX_ARGUMENTS_LENGTH) {\n    return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n  }\n\n  // Decode in chunks to avoid \"call stack size exceeded\".\n  var res = ''\n  var i = 0\n  while (i < len) {\n    res += String.fromCharCode.apply(\n      String,\n      codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n    )\n  }\n  return res\n}\n\nfunction asciiSlice (buf, start, end) {\n  var ret = ''\n  end = Math.min(buf.length, end)\n\n  for (var i = start; i < end; ++i) {\n    ret += String.fromCharCode(buf[i] & 0x7F)\n  }\n  return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n  var ret = ''\n  end = Math.min(buf.length, end)\n\n  for (var i = start; i < end; ++i) {\n    ret += String.fromCharCode(buf[i])\n  }\n  return ret\n}\n\nfunction hexSlice (buf, start, end) {\n  var len = buf.length\n\n  if (!start || start < 0) start = 0\n  if (!end || end < 0 || end > len) end = len\n\n  var out = ''\n  for (var i = start; i < end; ++i) {\n    out += toHex(buf[i])\n  }\n  return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n  var bytes = buf.slice(start, end)\n  var res = ''\n  for (var i = 0; i < bytes.length; i += 2) {\n    res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)\n  }\n  return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n  var len = this.length\n  start = ~~start\n  end = end === undefined ? len : ~~end\n\n  if (start < 0) {\n    start += len\n    if (start < 0) start = 0\n  } else if (start > len) {\n    start = len\n  }\n\n  if (end < 0) {\n    end += len\n    if (end < 0) end = 0\n  } else if (end > len) {\n    end = len\n  }\n\n  if (end < start) end = start\n\n  var newBuf\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    newBuf = this.subarray(start, end)\n    newBuf.__proto__ = Buffer.prototype\n  } else {\n    var sliceLen = end - start\n    newBuf = new Buffer(sliceLen, undefined)\n    for (var i = 0; i < sliceLen; ++i) {\n      newBuf[i] = this[i + start]\n    }\n  }\n\n  return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n  if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n  if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n  offset = offset | 0\n  byteLength = byteLength | 0\n  if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n  var val = this[offset]\n  var mul = 1\n  var i = 0\n  while (++i < byteLength && (mul *= 0x100)) {\n    val += this[offset + i] * mul\n  }\n\n  return val\n}\n\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n  offset = offset | 0\n  byteLength = byteLength | 0\n  if (!noAssert) {\n    checkOffset(offset, byteLength, this.length)\n  }\n\n  var val = this[offset + --byteLength]\n  var mul = 1\n  while (byteLength > 0 && (mul *= 0x100)) {\n    val += this[offset + --byteLength] * mul\n  }\n\n  return val\n}\n\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 1, this.length)\n  return this[offset]\n}\n\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 2, this.length)\n  return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 2, this.length)\n  return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 4, this.length)\n\n  return ((this[offset]) |\n      (this[offset + 1] << 8) |\n      (this[offset + 2] << 16)) +\n      (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 4, this.length)\n\n  return (this[offset] * 0x1000000) +\n    ((this[offset + 1] << 16) |\n    (this[offset + 2] << 8) |\n    this[offset + 3])\n}\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n  offset = offset | 0\n  byteLength = byteLength | 0\n  if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n  var val = this[offset]\n  var mul = 1\n  var i = 0\n  while (++i < byteLength && (mul *= 0x100)) {\n    val += this[offset + i] * mul\n  }\n  mul *= 0x80\n\n  if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n  return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n  offset = offset | 0\n  byteLength = byteLength | 0\n  if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n  var i = byteLength\n  var mul = 1\n  var val = this[offset + --i]\n  while (i > 0 && (mul *= 0x100)) {\n    val += this[offset + --i] * mul\n  }\n  mul *= 0x80\n\n  if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n  return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 1, this.length)\n  if (!(this[offset] & 0x80)) return (this[offset])\n  return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 2, this.length)\n  var val = this[offset] | (this[offset + 1] << 8)\n  return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 2, this.length)\n  var val = this[offset + 1] | (this[offset] << 8)\n  return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 4, this.length)\n\n  return (this[offset]) |\n    (this[offset + 1] << 8) |\n    (this[offset + 2] << 16) |\n    (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 4, this.length)\n\n  return (this[offset] << 24) |\n    (this[offset + 1] << 16) |\n    (this[offset + 2] << 8) |\n    (this[offset + 3])\n}\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 4, this.length)\n  return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 4, this.length)\n  return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 8, this.length)\n  return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n  if (!noAssert) checkOffset(offset, 8, this.length)\n  return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n  if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n  if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n  if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n  value = +value\n  offset = offset | 0\n  byteLength = byteLength | 0\n  if (!noAssert) {\n    var maxBytes = Math.pow(2, 8 * byteLength) - 1\n    checkInt(this, value, offset, byteLength, maxBytes, 0)\n  }\n\n  var mul = 1\n  var i = 0\n  this[offset] = value & 0xFF\n  while (++i < byteLength && (mul *= 0x100)) {\n    this[offset + i] = (value / mul) & 0xFF\n  }\n\n  return offset + byteLength\n}\n\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n  value = +value\n  offset = offset | 0\n  byteLength = byteLength | 0\n  if (!noAssert) {\n    var maxBytes = Math.pow(2, 8 * byteLength) - 1\n    checkInt(this, value, offset, byteLength, maxBytes, 0)\n  }\n\n  var i = byteLength - 1\n  var mul = 1\n  this[offset + i] = value & 0xFF\n  while (--i >= 0 && (mul *= 0x100)) {\n    this[offset + i] = (value / mul) & 0xFF\n  }\n\n  return offset + byteLength\n}\n\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n  this[offset] = (value & 0xff)\n  return offset + 1\n}\n\nfunction objectWriteUInt16 (buf, value, offset, littleEndian) {\n  if (value < 0) value = 0xffff + value + 1\n  for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {\n    buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>\n      (littleEndian ? i : 1 - i) * 8\n  }\n}\n\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value & 0xff)\n    this[offset + 1] = (value >>> 8)\n  } else {\n    objectWriteUInt16(this, value, offset, true)\n  }\n  return offset + 2\n}\n\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value >>> 8)\n    this[offset + 1] = (value & 0xff)\n  } else {\n    objectWriteUInt16(this, value, offset, false)\n  }\n  return offset + 2\n}\n\nfunction objectWriteUInt32 (buf, value, offset, littleEndian) {\n  if (value < 0) value = 0xffffffff + value + 1\n  for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {\n    buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff\n  }\n}\n\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset + 3] = (value >>> 24)\n    this[offset + 2] = (value >>> 16)\n    this[offset + 1] = (value >>> 8)\n    this[offset] = (value & 0xff)\n  } else {\n    objectWriteUInt32(this, value, offset, true)\n  }\n  return offset + 4\n}\n\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value >>> 24)\n    this[offset + 1] = (value >>> 16)\n    this[offset + 2] = (value >>> 8)\n    this[offset + 3] = (value & 0xff)\n  } else {\n    objectWriteUInt32(this, value, offset, false)\n  }\n  return offset + 4\n}\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) {\n    var limit = Math.pow(2, 8 * byteLength - 1)\n\n    checkInt(this, value, offset, byteLength, limit - 1, -limit)\n  }\n\n  var i = 0\n  var mul = 1\n  var sub = 0\n  this[offset] = value & 0xFF\n  while (++i < byteLength && (mul *= 0x100)) {\n    if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n      sub = 1\n    }\n    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n  }\n\n  return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) {\n    var limit = Math.pow(2, 8 * byteLength - 1)\n\n    checkInt(this, value, offset, byteLength, limit - 1, -limit)\n  }\n\n  var i = byteLength - 1\n  var mul = 1\n  var sub = 0\n  this[offset + i] = value & 0xFF\n  while (--i >= 0 && (mul *= 0x100)) {\n    if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n      sub = 1\n    }\n    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n  }\n\n  return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n  if (value < 0) value = 0xff + value + 1\n  this[offset] = (value & 0xff)\n  return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value & 0xff)\n    this[offset + 1] = (value >>> 8)\n  } else {\n    objectWriteUInt16(this, value, offset, true)\n  }\n  return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value >>> 8)\n    this[offset + 1] = (value & 0xff)\n  } else {\n    objectWriteUInt16(this, value, offset, false)\n  }\n  return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value & 0xff)\n    this[offset + 1] = (value >>> 8)\n    this[offset + 2] = (value >>> 16)\n    this[offset + 3] = (value >>> 24)\n  } else {\n    objectWriteUInt32(this, value, offset, true)\n  }\n  return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n  value = +value\n  offset = offset | 0\n  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n  if (value < 0) value = 0xffffffff + value + 1\n  if (Buffer.TYPED_ARRAY_SUPPORT) {\n    this[offset] = (value >>> 24)\n    this[offset + 1] = (value >>> 16)\n    this[offset + 2] = (value >>> 8)\n    this[offset + 3] = (value & 0xff)\n  } else {\n    objectWriteUInt32(this, value, offset, false)\n  }\n  return offset + 4\n}\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n  if (offset + ext > buf.length) throw new RangeError('Index out of range')\n  if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n  if (!noAssert) {\n    checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n  }\n  ieee754.write(buf, value, offset, littleEndian, 23, 4)\n  return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n  return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n  return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n  if (!noAssert) {\n    checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n  }\n  ieee754.write(buf, value, offset, littleEndian, 52, 8)\n  return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n  return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n  return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n  if (!start) start = 0\n  if (!end && end !== 0) end = this.length\n  if (targetStart >= target.length) targetStart = target.length\n  if (!targetStart) targetStart = 0\n  if (end > 0 && end < start) end = start\n\n  // Copy 0 bytes; we're done\n  if (end === start) return 0\n  if (target.length === 0 || this.length === 0) return 0\n\n  // Fatal error conditions\n  if (targetStart < 0) {\n    throw new RangeError('targetStart out of bounds')\n  }\n  if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')\n  if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n  // Are we oob?\n  if (end > this.length) end = this.length\n  if (target.length - targetStart < end - start) {\n    end = target.length - targetStart + start\n  }\n\n  var len = end - start\n  var i\n\n  if (this === target && start < targetStart && targetStart < end) {\n    // descending copy from end\n    for (i = len - 1; i >= 0; --i) {\n      target[i + targetStart] = this[i + start]\n    }\n  } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {\n    // ascending copy from start\n    for (i = 0; i < len; ++i) {\n      target[i + targetStart] = this[i + start]\n    }\n  } else {\n    Uint8Array.prototype.set.call(\n      target,\n      this.subarray(start, start + len),\n      targetStart\n    )\n  }\n\n  return len\n}\n\n// Usage:\n//    buffer.fill(number[, offset[, end]])\n//    buffer.fill(buffer[, offset[, end]])\n//    buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n  // Handle string cases:\n  if (typeof val === 'string') {\n    if (typeof start === 'string') {\n      encoding = start\n      start = 0\n      end = this.length\n    } else if (typeof end === 'string') {\n      encoding = end\n      end = this.length\n    }\n    if (val.length === 1) {\n      var code = val.charCodeAt(0)\n      if (code < 256) {\n        val = code\n      }\n    }\n    if (encoding !== undefined && typeof encoding !== 'string') {\n      throw new TypeError('encoding must be a string')\n    }\n    if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n      throw new TypeError('Unknown encoding: ' + encoding)\n    }\n  } else if (typeof val === 'number') {\n    val = val & 255\n  }\n\n  // Invalid ranges are not set to a default, so can range check early.\n  if (start < 0 || this.length < start || this.length < end) {\n    throw new RangeError('Out of range index')\n  }\n\n  if (end <= start) {\n    return this\n  }\n\n  start = start >>> 0\n  end = end === undefined ? this.length : end >>> 0\n\n  if (!val) val = 0\n\n  var i\n  if (typeof val === 'number') {\n    for (i = start; i < end; ++i) {\n      this[i] = val\n    }\n  } else {\n    var bytes = Buffer.isBuffer(val)\n      ? val\n      : utf8ToBytes(new Buffer(val, encoding).toString())\n    var len = bytes.length\n    for (i = 0; i < end - start; ++i) {\n      this[i + start] = bytes[i % len]\n    }\n  }\n\n  return this\n}\n\n// HELPER FUNCTIONS\n// ================\n\nvar INVALID_BASE64_RE = /[^+\\/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n  // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n  str = stringtrim(str).replace(INVALID_BASE64_RE, '')\n  // Node converts strings with length < 2 to ''\n  if (str.length < 2) return ''\n  // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n  while (str.length % 4 !== 0) {\n    str = str + '='\n  }\n  return str\n}\n\nfunction stringtrim (str) {\n  if (str.trim) return str.trim()\n  return str.replace(/^\\s+|\\s+$/g, '')\n}\n\nfunction toHex (n) {\n  if (n < 16) return '0' + n.toString(16)\n  return n.toString(16)\n}\n\nfunction utf8ToBytes (string, units) {\n  units = units || Infinity\n  var codePoint\n  var length = string.length\n  var leadSurrogate = null\n  var bytes = []\n\n  for (var i = 0; i < length; ++i) {\n    codePoint = string.charCodeAt(i)\n\n    // is surrogate component\n    if (codePoint > 0xD7FF && codePoint < 0xE000) {\n      // last char was a lead\n      if (!leadSurrogate) {\n        // no lead yet\n        if (codePoint > 0xDBFF) {\n          // unexpected trail\n          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n          continue\n        } else if (i + 1 === length) {\n          // unpaired lead\n          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n          continue\n        }\n\n        // valid lead\n        leadSurrogate = codePoint\n\n        continue\n      }\n\n      // 2 leads in a row\n      if (codePoint < 0xDC00) {\n        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n        leadSurrogate = codePoint\n        continue\n      }\n\n      // valid surrogate pair\n      codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n    } else if (leadSurrogate) {\n      // valid bmp char, but last char was a lead\n      if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n    }\n\n    leadSurrogate = null\n\n    // encode utf8\n    if (codePoint < 0x80) {\n      if ((units -= 1) < 0) break\n      bytes.push(codePoint)\n    } else if (codePoint < 0x800) {\n      if ((units -= 2) < 0) break\n      bytes.push(\n        codePoint >> 0x6 | 0xC0,\n        codePoint & 0x3F | 0x80\n      )\n    } else if (codePoint < 0x10000) {\n      if ((units -= 3) < 0) break\n      bytes.push(\n        codePoint >> 0xC | 0xE0,\n        codePoint >> 0x6 & 0x3F | 0x80,\n        codePoint & 0x3F | 0x80\n      )\n    } else if (codePoint < 0x110000) {\n      if ((units -= 4) < 0) break\n      bytes.push(\n        codePoint >> 0x12 | 0xF0,\n        codePoint >> 0xC & 0x3F | 0x80,\n        codePoint >> 0x6 & 0x3F | 0x80,\n        codePoint & 0x3F | 0x80\n      )\n    } else {\n      throw new Error('Invalid code point')\n    }\n  }\n\n  return bytes\n}\n\nfunction asciiToBytes (str) {\n  var byteArray = []\n  for (var i = 0; i < str.length; ++i) {\n    // Node's code seems to be doing this and not & 0x7F..\n    byteArray.push(str.charCodeAt(i) & 0xFF)\n  }\n  return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n  var c, hi, lo\n  var byteArray = []\n  for (var i = 0; i < str.length; ++i) {\n    if ((units -= 2) < 0) break\n\n    c = str.charCodeAt(i)\n    hi = c >> 8\n    lo = c % 256\n    byteArray.push(lo)\n    byteArray.push(hi)\n  }\n\n  return byteArray\n}\n\nfunction base64ToBytes (str) {\n  return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n  for (var i = 0; i < length; ++i) {\n    if ((i + offset >= dst.length) || (i >= src.length)) break\n    dst[i + offset] = src[i]\n  }\n  return i\n}\n\nfunction isnan (val) {\n  return val !== val // eslint-disable-line no-self-compare\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ \"./gulp/node_modules/webpack/buildin/global.js\")))\n\n/***/ }),\n\n/***/ \"./gulp/node_modules/webpack/buildin/global.js\":\n/*!***********************************!*\\\n  !*** (webpack)/buildin/global.js ***!\n  \\***********************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nvar g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n\n\n/***/ }),\n\n/***/ \"./node_modules/crc/crc32.js\":\n/*!***********************************!*\\\n  !*** ./node_modules/crc/crc32.js ***!\n  \\***********************************/\n/*! exports provided: default */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! buffer */ \"./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js\");\n/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(buffer__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _create_buffer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./create_buffer */ \"./node_modules/crc/create_buffer.js\");\n/* harmony import */ var _define_crc__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./define_crc */ \"./node_modules/crc/define_crc.js\");\n\n\n\n\n// Generated by `./pycrc.py --algorithm=table-driven --model=crc-32 --generate=c`\n// prettier-ignore\nlet TABLE = [\n  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,\n  0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,\n  0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,\n  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,\n  0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,\n  0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,\n  0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,\n  0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,\n  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\n  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,\n  0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,\n  0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,\n  0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,\n  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\n  0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,\n  0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,\n  0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,\n  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,\n  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,\n  0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,\n  0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,\n  0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,\n  0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,\n  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,\n  0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,\n  0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,\n  0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,\n  0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,\n  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,\n  0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,\n  0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,\n  0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,\n  0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,\n  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,\n  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,\n  0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,\n  0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,\n  0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,\n  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,\n  0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,\n  0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,\n  0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,\n  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,\n  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,\n  0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,\n  0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,\n  0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,\n  0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,\n  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,\n  0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,\n  0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,\n  0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,\n  0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,\n  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d\n];\n\nif (typeof Int32Array !== 'undefined') TABLE = new Int32Array(TABLE);\n\nconst crc32 = Object(_define_crc__WEBPACK_IMPORTED_MODULE_2__[\"default\"])('crc-32', function(buf, previous) {\n  if (!buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"].isBuffer(buf)) buf = Object(_create_buffer__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(buf);\n\n  let crc = previous === 0 ? 0 : ~~previous ^ -1;\n\n  for (let index = 0; index < buf.length; index++) {\n    const byte = buf[index];\n    crc = TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);\n  }\n\n  return crc ^ -1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (crc32);\n\n\n/***/ }),\n\n/***/ \"./node_modules/crc/create_buffer.js\":\n/*!*******************************************!*\\\n  !*** ./node_modules/crc/create_buffer.js ***!\n  \\*******************************************/\n/*! exports provided: default */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! buffer */ \"./gulp/node_modules/node-libs-browser/node_modules/buffer/index.js\");\n/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(buffer__WEBPACK_IMPORTED_MODULE_0__);\n\n\nconst createBuffer =\n  buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"].from && buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"].alloc && buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"].allocUnsafe && buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"].allocUnsafeSlow\n    ? buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"].from\n    : // support for Node < 5.10\n      val => new buffer__WEBPACK_IMPORTED_MODULE_0__[\"Buffer\"](val);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (createBuffer);\n\n\n/***/ }),\n\n/***/ \"./node_modules/crc/define_crc.js\":\n/*!****************************************!*\\\n  !*** ./node_modules/crc/define_crc.js ***!\n  \\****************************************/\n/*! exports provided: default */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(model, calc) {\n  const fn = (buf, previous) => calc(buf, previous) >>> 0;\n  fn.signed = calc;\n  fn.unsigned = fn;\n  fn.model = model;\n\n  return fn;\n});\n\n\n/***/ }),\n\n/***/ \"./node_modules/rusha/dist/rusha.js\":\n/*!******************************************!*\\\n  !*** ./node_modules/rusha/dist/rusha.js ***!\n  \\******************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(true)\n\t\tmodule.exports = factory();\n\telse {}\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 3);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n/* eslint-env commonjs, browser */\n\nvar RushaCore = __webpack_require__(5);\n\nvar _require = __webpack_require__(1),\n    toHex = _require.toHex,\n    ceilHeapSize = _require.ceilHeapSize;\n\nvar conv = __webpack_require__(6);\n\n// Calculate the length of buffer that the sha1 routine uses\n// including the padding.\nvar padlen = function (len) {\n  for (len += 9; len % 64 > 0; len += 1) {}\n  return len;\n};\n\nvar padZeroes = function (bin, len) {\n  var h8 = new Uint8Array(bin.buffer);\n  var om = len % 4,\n      align = len - om;\n  switch (om) {\n    case 0:\n      h8[align + 3] = 0;\n    case 1:\n      h8[align + 2] = 0;\n    case 2:\n      h8[align + 1] = 0;\n    case 3:\n      h8[align + 0] = 0;\n  }\n  for (var i = (len >> 2) + 1; i < bin.length; i++) {\n    bin[i] = 0;\n  }\n};\n\nvar padData = function (bin, chunkLen, msgLen) {\n  bin[chunkLen >> 2] |= 0x80 << 24 - (chunkLen % 4 << 3);\n  // To support msgLen >= 2 GiB, use a float division when computing the\n  // high 32-bits of the big-endian message length in bits.\n  bin[((chunkLen >> 2) + 2 & ~0x0f) + 14] = msgLen / (1 << 29) | 0;\n  bin[((chunkLen >> 2) + 2 & ~0x0f) + 15] = msgLen << 3;\n};\n\nvar getRawDigest = function (heap, padMaxChunkLen) {\n  var io = new Int32Array(heap, padMaxChunkLen + 320, 5);\n  var out = new Int32Array(5);\n  var arr = new DataView(out.buffer);\n  arr.setInt32(0, io[0], false);\n  arr.setInt32(4, io[1], false);\n  arr.setInt32(8, io[2], false);\n  arr.setInt32(12, io[3], false);\n  arr.setInt32(16, io[4], false);\n  return out;\n};\n\nvar Rusha = function () {\n  function Rusha(chunkSize) {\n    _classCallCheck(this, Rusha);\n\n    chunkSize = chunkSize || 64 * 1024;\n    if (chunkSize % 64 > 0) {\n      throw new Error('Chunk size must be a multiple of 128 bit');\n    }\n    this._offset = 0;\n    this._maxChunkLen = chunkSize;\n    this._padMaxChunkLen = padlen(chunkSize);\n    // The size of the heap is the sum of:\n    // 1. The padded input message size\n    // 2. The extended space the algorithm needs (320 byte)\n    // 3. The 160 bit state the algoritm uses\n    this._heap = new ArrayBuffer(ceilHeapSize(this._padMaxChunkLen + 320 + 20));\n    this._h32 = new Int32Array(this._heap);\n    this._h8 = new Int8Array(this._heap);\n    this._core = new RushaCore({ Int32Array: Int32Array }, {}, this._heap);\n  }\n\n  Rusha.prototype._initState = function _initState(heap, padMsgLen) {\n    this._offset = 0;\n    var io = new Int32Array(heap, padMsgLen + 320, 5);\n    io[0] = 1732584193;\n    io[1] = -271733879;\n    io[2] = -1732584194;\n    io[3] = 271733878;\n    io[4] = -1009589776;\n  };\n\n  Rusha.prototype._padChunk = function _padChunk(chunkLen, msgLen) {\n    var padChunkLen = padlen(chunkLen);\n    var view = new Int32Array(this._heap, 0, padChunkLen >> 2);\n    padZeroes(view, chunkLen);\n    padData(view, chunkLen, msgLen);\n    return padChunkLen;\n  };\n\n  Rusha.prototype._write = function _write(data, chunkOffset, chunkLen, off) {\n    conv(data, this._h8, this._h32, chunkOffset, chunkLen, off || 0);\n  };\n\n  Rusha.prototype._coreCall = function _coreCall(data, chunkOffset, chunkLen, msgLen, finalize) {\n    var padChunkLen = chunkLen;\n    this._write(data, chunkOffset, chunkLen);\n    if (finalize) {\n      padChunkLen = this._padChunk(chunkLen, msgLen);\n    }\n    this._core.hash(padChunkLen, this._padMaxChunkLen);\n  };\n\n  Rusha.prototype.rawDigest = function rawDigest(str) {\n    var msgLen = str.byteLength || str.length || str.size || 0;\n    this._initState(this._heap, this._padMaxChunkLen);\n    var chunkOffset = 0,\n        chunkLen = this._maxChunkLen;\n    for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) {\n      this._coreCall(str, chunkOffset, chunkLen, msgLen, false);\n    }\n    this._coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true);\n    return getRawDigest(this._heap, this._padMaxChunkLen);\n  };\n\n  Rusha.prototype.digest = function digest(str) {\n    return toHex(this.rawDigest(str).buffer);\n  };\n\n  Rusha.prototype.digestFromString = function digestFromString(str) {\n    return this.digest(str);\n  };\n\n  Rusha.prototype.digestFromBuffer = function digestFromBuffer(str) {\n    return this.digest(str);\n  };\n\n  Rusha.prototype.digestFromArrayBuffer = function digestFromArrayBuffer(str) {\n    return this.digest(str);\n  };\n\n  Rusha.prototype.resetState = function resetState() {\n    this._initState(this._heap, this._padMaxChunkLen);\n    return this;\n  };\n\n  Rusha.prototype.append = function append(chunk) {\n    var chunkOffset = 0;\n    var chunkLen = chunk.byteLength || chunk.length || chunk.size || 0;\n    var turnOffset = this._offset % this._maxChunkLen;\n    var inputLen = void 0;\n\n    this._offset += chunkLen;\n    while (chunkOffset < chunkLen) {\n      inputLen = Math.min(chunkLen - chunkOffset, this._maxChunkLen - turnOffset);\n      this._write(chunk, chunkOffset, inputLen, turnOffset);\n      turnOffset += inputLen;\n      chunkOffset += inputLen;\n      if (turnOffset === this._maxChunkLen) {\n        this._core.hash(this._maxChunkLen, this._padMaxChunkLen);\n        turnOffset = 0;\n      }\n    }\n    return this;\n  };\n\n  Rusha.prototype.getState = function getState() {\n    var turnOffset = this._offset % this._maxChunkLen;\n    var heap = void 0;\n    if (!turnOffset) {\n      var io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5);\n      heap = io.buffer.slice(io.byteOffset, io.byteOffset + io.byteLength);\n    } else {\n      heap = this._heap.slice(0);\n    }\n    return {\n      offset: this._offset,\n      heap: heap\n    };\n  };\n\n  Rusha.prototype.setState = function setState(state) {\n    this._offset = state.offset;\n    if (state.heap.byteLength === 20) {\n      var io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5);\n      io.set(new Int32Array(state.heap));\n    } else {\n      this._h32.set(new Int32Array(state.heap));\n    }\n    return this;\n  };\n\n  Rusha.prototype.rawEnd = function rawEnd() {\n    var msgLen = this._offset;\n    var chunkLen = msgLen % this._maxChunkLen;\n    var padChunkLen = this._padChunk(chunkLen, msgLen);\n    this._core.hash(padChunkLen, this._padMaxChunkLen);\n    var result = getRawDigest(this._heap, this._padMaxChunkLen);\n    this._initState(this._heap, this._padMaxChunkLen);\n    return result;\n  };\n\n  Rusha.prototype.end = function end() {\n    return toHex(this.rawEnd().buffer);\n  };\n\n  return Rusha;\n}();\n\nmodule.exports = Rusha;\nmodule.exports._core = RushaCore;\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports) {\n\n/* eslint-env commonjs, browser */\n\n//\n// toHex\n//\n\nvar precomputedHex = new Array(256);\nfor (var i = 0; i < 256; i++) {\n  precomputedHex[i] = (i < 0x10 ? '0' : '') + i.toString(16);\n}\n\nmodule.exports.toHex = function (arrayBuffer) {\n  var binarray = new Uint8Array(arrayBuffer);\n  var res = new Array(arrayBuffer.byteLength);\n  for (var _i = 0; _i < res.length; _i++) {\n    res[_i] = precomputedHex[binarray[_i]];\n  }\n  return res.join('');\n};\n\n//\n// ceilHeapSize\n//\n\nmodule.exports.ceilHeapSize = function (v) {\n  // The asm.js spec says:\n  // The heap object's byteLength must be either\n  // 2^n for n in [12, 24) or 2^24 * n for n ≥ 1.\n  // Also, byteLengths smaller than 2^16 are deprecated.\n  var p = 0;\n  // If v is smaller than 2^16, the smallest possible solution\n  // is 2^16.\n  if (v <= 65536) return 65536;\n  // If v < 2^24, we round up to 2^n,\n  // otherwise we round up to 2^24 * n.\n  if (v < 16777216) {\n    for (p = 1; p < v; p = p << 1) {}\n  } else {\n    for (p = 16777216; p < v; p += 16777216) {}\n  }\n  return p;\n};\n\n//\n// isDedicatedWorkerScope\n//\n\nmodule.exports.isDedicatedWorkerScope = function (self) {\n  var isRunningInWorker = 'WorkerGlobalScope' in self && self instanceof self.WorkerGlobalScope;\n  var isRunningInSharedWorker = 'SharedWorkerGlobalScope' in self && self instanceof self.SharedWorkerGlobalScope;\n  var isRunningInServiceWorker = 'ServiceWorkerGlobalScope' in self && self instanceof self.ServiceWorkerGlobalScope;\n\n  // Detects whether we run inside a dedicated worker or not.\n  //\n  // We can't just check for `DedicatedWorkerGlobalScope`, since IE11\n  // has a bug where it only supports `WorkerGlobalScope`.\n  //\n  // Therefore, we consider us as running inside a dedicated worker\n  // when we are running inside a worker, but not in a shared or service worker.\n  //\n  // When new types of workers are introduced, we will need to adjust this code.\n  return isRunningInWorker && !isRunningInSharedWorker && !isRunningInServiceWorker;\n};\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* eslint-env commonjs, worker */\n\nmodule.exports = function () {\n  var Rusha = __webpack_require__(0);\n\n  var hashData = function (hasher, data, cb) {\n    try {\n      return cb(null, hasher.digest(data));\n    } catch (e) {\n      return cb(e);\n    }\n  };\n\n  var hashFile = function (hasher, readTotal, blockSize, file, cb) {\n    var reader = new self.FileReader();\n    reader.onloadend = function onloadend() {\n      if (reader.error) {\n        return cb(reader.error);\n      }\n      var buffer = reader.result;\n      readTotal += reader.result.byteLength;\n      try {\n        hasher.append(buffer);\n      } catch (e) {\n        cb(e);\n        return;\n      }\n      if (readTotal < file.size) {\n        hashFile(hasher, readTotal, blockSize, file, cb);\n      } else {\n        cb(null, hasher.end());\n      }\n    };\n    reader.readAsArrayBuffer(file.slice(readTotal, readTotal + blockSize));\n  };\n\n  var workerBehaviourEnabled = true;\n\n  self.onmessage = function (event) {\n    if (!workerBehaviourEnabled) {\n      return;\n    }\n\n    var data = event.data.data,\n        file = event.data.file,\n        id = event.data.id;\n    if (typeof id === 'undefined') return;\n    if (!file && !data) return;\n    var blockSize = event.data.blockSize || 4 * 1024 * 1024;\n    var hasher = new Rusha(blockSize);\n    hasher.resetState();\n    var done = function (err, hash) {\n      if (!err) {\n        self.postMessage({ id: id, hash: hash });\n      } else {\n        self.postMessage({ id: id, error: err.name });\n      }\n    };\n    if (data) hashData(hasher, data, done);\n    if (file) hashFile(hasher, 0, blockSize, file, done);\n  };\n\n  return function () {\n    workerBehaviourEnabled = false;\n  };\n};\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* eslint-env commonjs, browser */\n\nvar work = __webpack_require__(4);\nvar Rusha = __webpack_require__(0);\nvar createHash = __webpack_require__(7);\nvar runWorker = __webpack_require__(2);\n\nvar _require = __webpack_require__(1),\n    isDedicatedWorkerScope = _require.isDedicatedWorkerScope;\n\nvar isRunningInDedicatedWorker = typeof self !== 'undefined' && isDedicatedWorkerScope(self);\n\nRusha.disableWorkerBehaviour = isRunningInDedicatedWorker ? runWorker() : function () {};\n\nRusha.createWorker = function () {\n  var worker = work(/*require.resolve*/(2));\n  var terminate = worker.terminate;\n  worker.terminate = function () {\n    URL.revokeObjectURL(worker.objectURL);\n    terminate.call(worker);\n  };\n  return worker;\n};\n\nRusha.createHash = createHash;\n\nmodule.exports = Rusha;\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\nfunction webpackBootstrapFunc (modules) {\n/******/  // The module cache\n/******/  var installedModules = {};\n\n/******/  // The require function\n/******/  function __webpack_require__(moduleId) {\n\n/******/    // Check if module is in cache\n/******/    if(installedModules[moduleId])\n/******/      return installedModules[moduleId].exports;\n\n/******/    // Create a new module (and put it into the cache)\n/******/    var module = installedModules[moduleId] = {\n/******/      i: moduleId,\n/******/      l: false,\n/******/      exports: {}\n/******/    };\n\n/******/    // Execute the module function\n/******/    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n/******/    // Flag the module as loaded\n/******/    module.l = true;\n\n/******/    // Return the exports of the module\n/******/    return module.exports;\n/******/  }\n\n/******/  // expose the modules object (__webpack_modules__)\n/******/  __webpack_require__.m = modules;\n\n/******/  // expose the module cache\n/******/  __webpack_require__.c = installedModules;\n\n/******/  // identity function for calling harmony imports with the correct context\n/******/  __webpack_require__.i = function(value) { return value; };\n\n/******/  // define getter function for harmony exports\n/******/  __webpack_require__.d = function(exports, name, getter) {\n/******/    if(!__webpack_require__.o(exports, name)) {\n/******/      Object.defineProperty(exports, name, {\n/******/        configurable: false,\n/******/        enumerable: true,\n/******/        get: getter\n/******/      });\n/******/    }\n/******/  };\n\n/******/  // define __esModule on exports\n/******/  __webpack_require__.r = function(exports) {\n/******/    Object.defineProperty(exports, '__esModule', { value: true });\n/******/  };\n\n/******/  // getDefaultExport function for compatibility with non-harmony modules\n/******/  __webpack_require__.n = function(module) {\n/******/    var getter = module && module.__esModule ?\n/******/      function getDefault() { return module['default']; } :\n/******/      function getModuleExports() { return module; };\n/******/    __webpack_require__.d(getter, 'a', getter);\n/******/    return getter;\n/******/  };\n\n/******/  // Object.prototype.hasOwnProperty.call\n/******/  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n/******/  // __webpack_public_path__\n/******/  __webpack_require__.p = \"/\";\n\n/******/  // on error function for async loading\n/******/  __webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n  var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE)\n  return f.default || f // try to call default if defined to also support babel esmodule exports\n}\n\nvar moduleNameReqExp = '[\\\\.|\\\\-|\\\\+|\\\\w|\\/|@]+'\nvar dependencyRegExp = '\\\\((\\/\\\\*.*?\\\\*\\/)?\\s?.*?(' + moduleNameReqExp + ').*?\\\\)' // additional chars when output.pathinfo is true\n\n// http://stackoverflow.com/a/2593661/130442\nfunction quoteRegExp (str) {\n  return (str + '').replace(/[.?*+^$[\\]\\\\(){}|-]/g, '\\\\$&')\n}\n\nfunction getModuleDependencies (sources, module, queueName) {\n  var retval = {}\n  retval[queueName] = []\n\n  var fnString = module.toString()\n  var wrapperSignature = fnString.match(/^function\\s?\\(\\w+,\\s*\\w+,\\s*(\\w+)\\)/)\n  if (!wrapperSignature) return retval\n  var webpackRequireName = wrapperSignature[1]\n\n  // main bundle deps\n  var re = new RegExp('(\\\\\\\\n|\\\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g')\n  var match\n  while ((match = re.exec(fnString))) {\n    if (match[3] === 'dll-reference') continue\n    retval[queueName].push(match[3])\n  }\n\n  // dll deps\n  re = new RegExp('\\\\(' + quoteRegExp(webpackRequireName) + '\\\\(\"(dll-reference\\\\s(' + moduleNameReqExp + '))\"\\\\)\\\\)' + dependencyRegExp, 'g')\n  while ((match = re.exec(fnString))) {\n    if (!sources[match[2]]) {\n      retval[queueName].push(match[1])\n      sources[match[2]] = __webpack_require__(match[1]).m\n    }\n    retval[match[2]] = retval[match[2]] || []\n    retval[match[2]].push(match[4])\n  }\n\n  return retval\n}\n\nfunction hasValuesInQueues (queues) {\n  var keys = Object.keys(queues)\n  return keys.reduce(function (hasValues, key) {\n    return hasValues || queues[key].length > 0\n  }, false)\n}\n\nfunction getRequiredModules (sources, moduleId) {\n  var modulesQueue = {\n    main: [moduleId]\n  }\n  var requiredModules = {\n    main: []\n  }\n  var seenModules = {\n    main: {}\n  }\n\n  while (hasValuesInQueues(modulesQueue)) {\n    var queues = Object.keys(modulesQueue)\n    for (var i = 0; i < queues.length; i++) {\n      var queueName = queues[i]\n      var queue = modulesQueue[queueName]\n      var moduleToCheck = queue.pop()\n      seenModules[queueName] = seenModules[queueName] || {}\n      if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue\n      seenModules[queueName][moduleToCheck] = true\n      requiredModules[queueName] = requiredModules[queueName] || []\n      requiredModules[queueName].push(moduleToCheck)\n      var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName)\n      var newModulesKeys = Object.keys(newModules)\n      for (var j = 0; j < newModulesKeys.length; j++) {\n        modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || []\n        modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]])\n      }\n    }\n  }\n\n  return requiredModules\n}\n\nmodule.exports = function (moduleId, options) {\n  options = options || {}\n  var sources = {\n    main: __webpack_require__.m\n  }\n\n  var requiredModules = options.all ? { main: Object.keys(sources) } : getRequiredModules(sources, moduleId)\n\n  var src = ''\n\n  Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) {\n    var entryModule = 0\n    while (requiredModules[module][entryModule]) {\n      entryModule++\n    }\n    requiredModules[module].push(entryModule)\n    sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })'\n    src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\\n'\n  })\n\n  src = src + '(' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '})(self);'\n\n  var blob = new window.Blob([src], { type: 'text/javascript' })\n  if (options.bare) { return blob }\n\n  var URL = window.URL || window.webkitURL || window.mozURL || window.msURL\n\n  var workerUrl = URL.createObjectURL(blob)\n  var worker = new window.Worker(workerUrl)\n  worker.objectURL = workerUrl\n\n  return worker\n}\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports) {\n\n// The low-level RushCore module provides the heart of Rusha,\n// a high-speed sha1 implementation working on an Int32Array heap.\n// At first glance, the implementation seems complicated, however\n// with the SHA1 spec at hand, it is obvious this almost a textbook\n// implementation that has a few functions hand-inlined and a few loops\n// hand-unrolled.\nmodule.exports = function RushaCore(stdlib$846, foreign$847, heap$848) {\n    'use asm';\n    var H$849 = new stdlib$846.Int32Array(heap$848);\n    function hash$850(k$851, x$852) {\n        // k in bytes\n        k$851 = k$851 | 0;\n        x$852 = x$852 | 0;\n        var i$853 = 0, j$854 = 0, y0$855 = 0, z0$856 = 0, y1$857 = 0, z1$858 = 0, y2$859 = 0, z2$860 = 0, y3$861 = 0, z3$862 = 0, y4$863 = 0, z4$864 = 0, t0$865 = 0, t1$866 = 0;\n        y0$855 = H$849[x$852 + 320 >> 2] | 0;\n        y1$857 = H$849[x$852 + 324 >> 2] | 0;\n        y2$859 = H$849[x$852 + 328 >> 2] | 0;\n        y3$861 = H$849[x$852 + 332 >> 2] | 0;\n        y4$863 = H$849[x$852 + 336 >> 2] | 0;\n        for (i$853 = 0; (i$853 | 0) < (k$851 | 0); i$853 = i$853 + 64 | 0) {\n            z0$856 = y0$855;\n            z1$858 = y1$857;\n            z2$860 = y2$859;\n            z3$862 = y3$861;\n            z4$864 = y4$863;\n            for (j$854 = 0; (j$854 | 0) < 64; j$854 = j$854 + 4 | 0) {\n                t1$866 = H$849[i$853 + j$854 >> 2] | 0;\n                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 & y2$859 | ~y1$857 & y3$861) | 0) + ((t1$866 + y4$863 | 0) + 1518500249 | 0) | 0;\n                y4$863 = y3$861;\n                y3$861 = y2$859;\n                y2$859 = y1$857 << 30 | y1$857 >>> 2;\n                y1$857 = y0$855;\n                y0$855 = t0$865;\n                H$849[k$851 + j$854 >> 2] = t1$866;\n            }\n            for (j$854 = k$851 + 64 | 0; (j$854 | 0) < (k$851 + 80 | 0); j$854 = j$854 + 4 | 0) {\n                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;\n                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 & y2$859 | ~y1$857 & y3$861) | 0) + ((t1$866 + y4$863 | 0) + 1518500249 | 0) | 0;\n                y4$863 = y3$861;\n                y3$861 = y2$859;\n                y2$859 = y1$857 << 30 | y1$857 >>> 2;\n                y1$857 = y0$855;\n                y0$855 = t0$865;\n                H$849[j$854 >> 2] = t1$866;\n            }\n            for (j$854 = k$851 + 80 | 0; (j$854 | 0) < (k$851 + 160 | 0); j$854 = j$854 + 4 | 0) {\n                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;\n                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 ^ y2$859 ^ y3$861) | 0) + ((t1$866 + y4$863 | 0) + 1859775393 | 0) | 0;\n                y4$863 = y3$861;\n                y3$861 = y2$859;\n                y2$859 = y1$857 << 30 | y1$857 >>> 2;\n                y1$857 = y0$855;\n                y0$855 = t0$865;\n                H$849[j$854 >> 2] = t1$866;\n            }\n            for (j$854 = k$851 + 160 | 0; (j$854 | 0) < (k$851 + 240 | 0); j$854 = j$854 + 4 | 0) {\n                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;\n                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 & y2$859 | y1$857 & y3$861 | y2$859 & y3$861) | 0) + ((t1$866 + y4$863 | 0) - 1894007588 | 0) | 0;\n                y4$863 = y3$861;\n                y3$861 = y2$859;\n                y2$859 = y1$857 << 30 | y1$857 >>> 2;\n                y1$857 = y0$855;\n                y0$855 = t0$865;\n                H$849[j$854 >> 2] = t1$866;\n            }\n            for (j$854 = k$851 + 240 | 0; (j$854 | 0) < (k$851 + 320 | 0); j$854 = j$854 + 4 | 0) {\n                t1$866 = (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) << 1 | (H$849[j$854 - 12 >> 2] ^ H$849[j$854 - 32 >> 2] ^ H$849[j$854 - 56 >> 2] ^ H$849[j$854 - 64 >> 2]) >>> 31;\n                t0$865 = ((y0$855 << 5 | y0$855 >>> 27) + (y1$857 ^ y2$859 ^ y3$861) | 0) + ((t1$866 + y4$863 | 0) - 899497514 | 0) | 0;\n                y4$863 = y3$861;\n                y3$861 = y2$859;\n                y2$859 = y1$857 << 30 | y1$857 >>> 2;\n                y1$857 = y0$855;\n                y0$855 = t0$865;\n                H$849[j$854 >> 2] = t1$866;\n            }\n            y0$855 = y0$855 + z0$856 | 0;\n            y1$857 = y1$857 + z1$858 | 0;\n            y2$859 = y2$859 + z2$860 | 0;\n            y3$861 = y3$861 + z3$862 | 0;\n            y4$863 = y4$863 + z4$864 | 0;\n        }\n        H$849[x$852 + 320 >> 2] = y0$855;\n        H$849[x$852 + 324 >> 2] = y1$857;\n        H$849[x$852 + 328 >> 2] = y2$859;\n        H$849[x$852 + 332 >> 2] = y3$861;\n        H$849[x$852 + 336 >> 2] = y4$863;\n    }\n    return { hash: hash$850 };\n};\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports) {\n\nvar _this = this;\n\n/* eslint-env commonjs, browser */\n\nvar reader = void 0;\nif (typeof self !== 'undefined' && typeof self.FileReaderSync !== 'undefined') {\n  reader = new self.FileReaderSync();\n}\n\n// Convert a binary string and write it to the heap.\n// A binary string is expected to only contain char codes < 256.\nvar convStr = function (str, H8, H32, start, len, off) {\n  var i = void 0,\n      om = off % 4,\n      lm = (len + om) % 4,\n      j = len - lm;\n  switch (om) {\n    case 0:\n      H8[off] = str.charCodeAt(start + 3);\n    case 1:\n      H8[off + 1 - (om << 1) | 0] = str.charCodeAt(start + 2);\n    case 2:\n      H8[off + 2 - (om << 1) | 0] = str.charCodeAt(start + 1);\n    case 3:\n      H8[off + 3 - (om << 1) | 0] = str.charCodeAt(start);\n  }\n  if (len < lm + (4 - om)) {\n    return;\n  }\n  for (i = 4 - om; i < j; i = i + 4 | 0) {\n    H32[off + i >> 2] = str.charCodeAt(start + i) << 24 | str.charCodeAt(start + i + 1) << 16 | str.charCodeAt(start + i + 2) << 8 | str.charCodeAt(start + i + 3);\n  }\n  switch (lm) {\n    case 3:\n      H8[off + j + 1 | 0] = str.charCodeAt(start + j + 2);\n    case 2:\n      H8[off + j + 2 | 0] = str.charCodeAt(start + j + 1);\n    case 1:\n      H8[off + j + 3 | 0] = str.charCodeAt(start + j);\n  }\n};\n\n// Convert a buffer or array and write it to the heap.\n// The buffer or array is expected to only contain elements < 256.\nvar convBuf = function (buf, H8, H32, start, len, off) {\n  var i = void 0,\n      om = off % 4,\n      lm = (len + om) % 4,\n      j = len - lm;\n  switch (om) {\n    case 0:\n      H8[off] = buf[start + 3];\n    case 1:\n      H8[off + 1 - (om << 1) | 0] = buf[start + 2];\n    case 2:\n      H8[off + 2 - (om << 1) | 0] = buf[start + 1];\n    case 3:\n      H8[off + 3 - (om << 1) | 0] = buf[start];\n  }\n  if (len < lm + (4 - om)) {\n    return;\n  }\n  for (i = 4 - om; i < j; i = i + 4 | 0) {\n    H32[off + i >> 2 | 0] = buf[start + i] << 24 | buf[start + i + 1] << 16 | buf[start + i + 2] << 8 | buf[start + i + 3];\n  }\n  switch (lm) {\n    case 3:\n      H8[off + j + 1 | 0] = buf[start + j + 2];\n    case 2:\n      H8[off + j + 2 | 0] = buf[start + j + 1];\n    case 1:\n      H8[off + j + 3 | 0] = buf[start + j];\n  }\n};\n\nvar convBlob = function (blob, H8, H32, start, len, off) {\n  var i = void 0,\n      om = off % 4,\n      lm = (len + om) % 4,\n      j = len - lm;\n  var buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len)));\n  switch (om) {\n    case 0:\n      H8[off] = buf[3];\n    case 1:\n      H8[off + 1 - (om << 1) | 0] = buf[2];\n    case 2:\n      H8[off + 2 - (om << 1) | 0] = buf[1];\n    case 3:\n      H8[off + 3 - (om << 1) | 0] = buf[0];\n  }\n  if (len < lm + (4 - om)) {\n    return;\n  }\n  for (i = 4 - om; i < j; i = i + 4 | 0) {\n    H32[off + i >> 2 | 0] = buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3];\n  }\n  switch (lm) {\n    case 3:\n      H8[off + j + 1 | 0] = buf[j + 2];\n    case 2:\n      H8[off + j + 2 | 0] = buf[j + 1];\n    case 1:\n      H8[off + j + 3 | 0] = buf[j];\n  }\n};\n\nmodule.exports = function (data, H8, H32, start, len, off) {\n  if (typeof data === 'string') {\n    return convStr(data, H8, H32, start, len, off);\n  }\n  if (data instanceof Array) {\n    return convBuf(data, H8, H32, start, len, off);\n  }\n  // Safely doing a Buffer check using \"this\" to avoid Buffer polyfill to be included in the dist\n  if (_this && _this.Buffer && _this.Buffer.isBuffer(data)) {\n    return convBuf(data, H8, H32, start, len, off);\n  }\n  if (data instanceof ArrayBuffer) {\n    return convBuf(new Uint8Array(data), H8, H32, start, len, off);\n  }\n  if (data.buffer instanceof ArrayBuffer) {\n    return convBuf(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), H8, H32, start, len, off);\n  }\n  if (data instanceof Blob) {\n    return convBlob(data, H8, H32, start, len, off);\n  }\n  throw new Error('Unsupported data type.');\n};\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports, __webpack_require__) {\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n/* eslint-env commonjs, browser */\n\nvar Rusha = __webpack_require__(0);\n\nvar _require = __webpack_require__(1),\n    toHex = _require.toHex;\n\nvar Hash = function () {\n  function Hash() {\n    _classCallCheck(this, Hash);\n\n    this._rusha = new Rusha();\n    this._rusha.resetState();\n  }\n\n  Hash.prototype.update = function update(data) {\n    this._rusha.append(data);\n    return this;\n  };\n\n  Hash.prototype.digest = function digest(encoding) {\n    var digest = this._rusha.rawEnd().buffer;\n    if (!encoding) {\n      return digest;\n    }\n    if (encoding === 'hex') {\n      return toHex(digest);\n    }\n    throw new Error('unsupported digest encoding');\n  };\n\n  return Hash;\n}();\n\nmodule.exports = function () {\n  return new Hash();\n};\n\n/***/ })\n/******/ ]);\n});\n\n/***/ }),\n\n/***/ \"./node_modules/webpack-strip-block/index.js?!./src/js/webworkers/compression.worker.js\":\n/*!**********************************************************************************************!*\\\n  !*** ./node_modules/webpack-strip-block??ref--7-0!./src/js/webworkers/compression.worker.js ***!\n  \\**********************************************************************************************/\n/*! no exports provided */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _core_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/config */ \"./src/js/core/config.js\");\n/* harmony import */ var _core_lzstring__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../core/lzstring */ \"./src/js/core/lzstring.js\");\n/* harmony import */ var _core_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/sensitive_utils.encrypt */ \"./src/js/core/sensitive_utils.encrypt.js\");\n/* harmony import */ var _savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../savegame/savegame_compressor */ \"./src/js/savegame/savegame_compressor.js\");\n\n\n\n\n\nfunction accessNestedPropertyReverse(obj, keys) {\n    let result = obj;\n    for (let i = keys.length - 1; i >= 0; --i) {\n        result = result[keys[i]];\n    }\n    return result;\n}\n\nconst salt = accessNestedPropertyReverse(_core_config__WEBPACK_IMPORTED_MODULE_0__[\"globalConfig\"], [\"file\", \"info\"]);\n\nself.addEventListener(\"message\", event => {\n    // @ts-ignore\n    const { jobId, job, data } = event.data;\n    const result = performJob(job, data);\n\n    // @ts-ignore\n    self.postMessage({ jobId, result });\n});\n\nfunction performJob(job, data) {\n    switch (job) {\n        case \"compressX64\": {\n            return Object(_core_lzstring__WEBPACK_IMPORTED_MODULE_1__[\"compressX64\"])(data);\n        }\n\n        case \"compressObject\": {\n            const optimized = Object(_savegame_savegame_compressor__WEBPACK_IMPORTED_MODULE_3__[\"compressObject\"])(data.obj);\n            const stringified = JSON.stringify(optimized);\n\n            const checksum = Object(_core_sensitive_utils_encrypt__WEBPACK_IMPORTED_MODULE_2__[\"computeCrc\"])(stringified + salt);\n            return data.compressionPrefix + Object(_core_lzstring__WEBPACK_IMPORTED_MODULE_1__[\"compressX64\"])(checksum + stringified);\n        }\n        default:\n            throw new Error(\"Webworker: Unknown job: \" + job);\n    }\n}\n\n\n/***/ }),\n\n/***/ \"./src/js/core/config.js\":\n/*!*******************************!*\\\n  !*** ./src/js/core/config.js ***!\n  \\*******************************/\n/*! exports provided: IS_DEBUG, SUPPORT_TOUCH, IS_MAC, THIRDPARTY_URLS, A_B_TESTING_LINK_TYPE, globalConfig, IS_MOBILE */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"IS_DEBUG\", function() { return IS_DEBUG; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"SUPPORT_TOUCH\", function() { return SUPPORT_TOUCH; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"IS_MAC\", function() { return IS_MAC; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"THIRDPARTY_URLS\", function() { return THIRDPARTY_URLS; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"A_B_TESTING_LINK_TYPE\", function() { return A_B_TESTING_LINK_TYPE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"globalConfig\", function() { return globalConfig; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"IS_MOBILE\", function() { return IS_MOBILE; });\nconst IS_DEBUG =\r\n     false &&\r\n    false;\r\n\r\nconst SUPPORT_TOUCH = false;\r\n\r\nconst IS_MAC = navigator.platform.toLowerCase().indexOf(\"mac\") >= 0;\r\n\r\nconst smoothCanvas = true;\r\n\r\nconst THIRDPARTY_URLS = {\r\n    discord: \"https://discord.gg/HN7EVzV\",\r\n    github: \"https://github.com/tobspr/shapez.io\",\r\n    reddit: \"https://www.reddit.com/r/shapezio\",\r\n    shapeViewer: \"https://viewer.shapez.io\",\r\n\r\n    standaloneStorePage: \"https://store.steampowered.com/app/1318690/shapezio/\",\r\n\r\n    levelTutorialVideos: {\r\n        21: \"https://www.youtube.com/watch?v=0nUfRLMCcgo&\",\r\n        25: \"https://www.youtube.com/watch?v=7OCV1g40Iew&\",\r\n        26: \"https://www.youtube.com/watch?v=gfm6dS1dCoY\",\r\n    },\r\n};\r\n\r\n// export const A_B_TESTING_LINK_TYPE = Math.random() > 0.95 ? \"steam_1_pr\" : \"steam_2_npr\";\r\nconst A_B_TESTING_LINK_TYPE = \"steam_2_npr\";\r\n\r\nconst globalConfig = {\r\n    // Size of a single tile in Pixels.\r\n    // NOTICE: Update webpack.production.config too!\r\n    tileSize: 32,\r\n    halfTileSize: 16,\r\n\r\n    // Which dpi the assets have\r\n    assetsDpi: 192 / 32,\r\n    assetsSharpness: 1.5,\r\n    shapesSharpness: 1.4,\r\n\r\n    // Production analytics\r\n    statisticsGraphDpi: 2.5,\r\n    statisticsGraphSlices: 100,\r\n    analyticsSliceDurationSeconds:  false ? undefined : 10,\r\n\r\n    minimumTickRate: 25,\r\n    maximumTickRate: 500,\r\n\r\n    // Map\r\n    mapChunkSize: 16,\r\n    mapChunkOverviewMinZoom: 0.9,\r\n    mapChunkWorldSize: null, // COMPUTED\r\n\r\n    // Belt speeds\r\n    // NOTICE: Update webpack.production.config too!\r\n    beltSpeedItemsPerSecond: 2,\r\n    minerSpeedItemsPerSecond: 0, // COMPUTED\r\n\r\n    defaultItemDiameter: 20,\r\n\r\n    itemSpacingOnBelts: 0.63,\r\n\r\n    wiresSpeedItemsPerSecond: 6,\r\n\r\n    undergroundBeltMaxTilesByTier: [5, 9],\r\n\r\n    readerAnalyzeIntervalSeconds: 10,\r\n\r\n    buildingSpeeds: {\r\n        cutter: 1 / 4,\r\n        cutterQuad: 1 / 4,\r\n        rotater: 1 / 1,\r\n        rotaterCCW: 1 / 1,\r\n        rotater180: 1 / 1,\r\n        painter: 1 / 6,\r\n        painterDouble: 1 / 8,\r\n        painterQuad: 1 / 2,\r\n        mixer: 1 / 5,\r\n        stacker: 1 / 8,\r\n    },\r\n\r\n    // Zooming\r\n    initialZoom: 1.9,\r\n    minZoomLevel: 0.1,\r\n    maxZoomLevel: 3,\r\n\r\n    // Global game speed\r\n    gameSpeed: 1,\r\n\r\n    warmupTimeSecondsFast: 0.5,\r\n    warmupTimeSecondsRegular: 3,\r\n\r\n    smoothing: {\r\n        smoothMainCanvas: smoothCanvas && true,\r\n        quality: \"low\", // Low is CRUCIAL for mobile performance!\r\n    },\r\n\r\n    rendering: {},\r\n    debug: __webpack_require__(/*! ./config.local */ \"./src/js/core/config.local.js\").default,\r\n\r\n    // Secret vars\r\n    info: {\r\n        // Binary file salt\r\n        file: \"Ec'])@^+*9zMevK3uMV4432x9%iK'=\",\r\n\r\n        // Savegame salt\r\n        sgSalt: \"}95Q3%8/.837Lqym_BJx%q7)pAHJbF\",\r\n\r\n        // Analytics key\r\n        analyticsApiKey: \"baf6a50f0cc7dfdec5a0e21c88a1c69a4b34bc4a\",\r\n    },\r\n};\r\n\r\nconst IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);\r\n\r\n// Automatic calculations\r\nglobalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5;\r\n\r\nglobalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize;\r\n\r\n// Dynamic calculations\r\nif (globalConfig.debug.disableMapOverview) {\r\n    globalConfig.mapChunkOverviewMinZoom = 0;\r\n}\r\n\r\n// Stuff for making the trailer\r\nif (false) {}\r\n\r\nif (globalConfig.debug.fastGameEnter) {\r\n    globalConfig.debug.noArtificialDelays = true;\r\n}\r\n\r\nif (false) {}\r\n\n\n/***/ }),\n\n/***/ \"./src/js/core/config.local.js\":\n/*!*************************************!*\\\n  !*** ./src/js/core/config.local.js ***!\n  \\*************************************/\n/*! exports provided: default */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\r\n    // You can set any debug options here!\r\n    /* dev:start */\r\n    // -----------------------------------------------------------------------------------\r\n    // Quickly enters the game and skips the main menu - good for fast iterating\r\n    // fastGameEnter: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Skips any delays like transitions between states and such\r\n    // noArtificialDelays: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables writing of savegames, useful for testing the same savegame over and over\r\n    // disableSavegameWrite: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Shows bounds of all entities\r\n    // showEntityBounds: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Shows arrows for every ejector / acceptor\r\n    // showAcceptorEjectors: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables the music (Overrides any setting, can cause weird behaviour)\r\n    // disableMusic: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Do not render static map entities (=most buildings)\r\n    // doNotRenderStatics: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Allow to zoom freely without limits\r\n    // disableZoomLimits: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // All rewards can be unlocked by passing just 1 of any shape\r\n    // rewardsInstant: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Unlocks all buildings\r\n    // allBuildingsUnlocked: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables cost of blueprints\r\n    // blueprintsNoCost: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables cost of upgrades\r\n    // upgradesNoCost: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables the dialog when completing a level\r\n    // disableUnlockDialog: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables the simulation - This effectively pauses the game.\r\n    // disableLogicTicks: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Test the rendering if everything is clipped out properly\r\n    // testClipping: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Allows to render slower, useful for recording at half speed to avoid stuttering\r\n    // framePausesBetweenTicks: 250,\r\n    // -----------------------------------------------------------------------------------\r\n    // Replace all translations with emojis to see which texts are translateable\r\n    // testTranslations: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Enables an inspector which shows information about the entity below the curosr\r\n    // enableEntityInspector: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Enables ads in the local build (normally they are deactivated there)\r\n    // testAds: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables the automatic switch to an overview when zooming out\r\n    // disableMapOverview: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables the notification when there are new entries in the changelog since last played\r\n    // disableUpgradeNotification: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Makes belts almost infinitely fast\r\n    // instantBelts: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Makes item processors almost infinitely fast\r\n    // instantProcessors: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Makes miners almost infinitely fast\r\n    // instantMiners: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // When using fastGameEnter, controls whether a new game is started or the last one is resumed\r\n    // resumeGameOnFastEnter: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Special option used to render the trailer\r\n    // renderForTrailer: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Whether to render changes\r\n    // renderChanges: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Whether to render belt paths\r\n    // renderBeltPaths: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Whether to check belt paths\r\n    // checkBeltPaths: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Whether to items / s instead of items / m in stats\r\n    // detailedStatistics: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Shows detailed information about which atlas is used\r\n    // showAtlasInfo: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Renders the rotation of all wires\r\n    // renderWireRotations: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Renders information about wire networks\r\n    // renderWireNetworkInfos: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables ejector animations and processing\r\n    // disableEjectorProcessing: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Allows manual ticking\r\n    // manualTickOnly: true,\r\n    // -----------------------------------------------------------------------------------\r\n    // Disables slow asserts, useful for debugging performance\r\n    // disableSlowAsserts: true,\r\n    // -----------------------------------------------------------------------------------\r\n    /* dev:end */\r\n});\r\n\n\n/***/ }),\n\n/***/ \"./src/js/core/lzstring.js\":\n/*!*********************************!*\\\n  !*** ./src/js/core/lzstring.js ***!\n  \\*********************************/\n/*! exports provided: compressU8, compressU8WHeader, decompressU8WHeader, compressX64, decompressX64 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"compressU8\", function() { return compressU8; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"compressU8WHeader\", function() { return compressU8WHeader; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decompressU8WHeader\", function() { return decompressU8WHeader; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"compressX64\", function() { return compressX64; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decompressX64\", function() { return decompressX64; });\n// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>\n// This work is free. You can redistribute it and/or modify it\n// under the terms of the WTFPL, Version 2\n// For more information see LICENSE.txt or http://www.wtfpl.net/\n//\n// For more information, the home page:\n// http://pieroxy.net/blog/pages/lz-string/testing.html\n//\n// LZ-based compression algorithm, version 1.4.4\n\nconst fromCharCode = String.fromCharCode;\nconst hasOwnProperty = Object.prototype.hasOwnProperty;\n\nconst keyStrUriSafe = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$\";\nconst baseReverseDic = {};\n\nfunction getBaseValue(alphabet, character) {\n    if (!baseReverseDic[alphabet]) {\n        baseReverseDic[alphabet] = {};\n        for (let i = 0; i < alphabet.length; i++) {\n            baseReverseDic[alphabet][alphabet.charAt(i)] = i;\n        }\n    }\n    return baseReverseDic[alphabet][character];\n}\n\n//compress into uint8array (UCS-2 big endian format)\nfunction compressU8(uncompressed) {\n    let compressed = compress(uncompressed);\n    let buf = new Uint8Array(compressed.length * 2); // 2 bytes per character\n\n    for (let i = 0, TotalLen = compressed.length; i < TotalLen; i++) {\n        let current_value = compressed.charCodeAt(i);\n        buf[i * 2] = current_value >>> 8;\n        buf[i * 2 + 1] = current_value % 256;\n    }\n    return buf;\n}\n\n// Compreses with header\n/**\n * @param {string} uncompressed\n * @param {number} header\n */\nfunction compressU8WHeader(uncompressed, header) {\n    let compressed = compress(uncompressed);\n    let buf = new Uint8Array(2 + compressed.length * 2); // 2 bytes per character\n\n    buf[0] = header >>> 8;\n    buf[1] = header % 256;\n    for (let i = 0, TotalLen = compressed.length; i < TotalLen; i++) {\n        let current_value = compressed.charCodeAt(i);\n        buf[2 + i * 2] = current_value >>> 8;\n        buf[2 + i * 2 + 1] = current_value % 256;\n    }\n    return buf;\n}\n\n//decompress from uint8array (UCS-2 big endian format)\n/**\n *\n * @param {Uint8Array} compressed\n */\nfunction decompressU8WHeader(compressed) {\n    // let buf = new Array(compressed.length / 2); // 2 bytes per character\n    // for (let i = 0, TotalLen = buf.length; i < TotalLen; i++) {\n    //     buf[i] = compressed[i * 2] * 256 + compressed[i * 2 + 1];\n    // }\n\n    // let result = [];\n    // buf.forEach(function (c) {\n    //     result.push(fromCharCode(c));\n    // });\n    let result = [];\n    for (let i = 2, n = compressed.length; i < n; i += 2) {\n        const code = compressed[i] * 256 + compressed[i + 1];\n        result.push(fromCharCode(code));\n    }\n    return decompress(result.join(\"\"));\n}\n\n//compress into a string that is already URI encoded\nfunction compressX64(input) {\n    if (input == null) return \"\";\n    return _compress(input, 6, function (a) {\n        return keyStrUriSafe.charAt(a);\n    });\n}\n\n//decompress from an output of compressToEncodedURIComponent\nfunction decompressX64(input) {\n    if (input == null) return \"\";\n    if (input == \"\") return null;\n    input = input.replace(/ /g, \"+\");\n    return _decompress(input.length, 32, function (index) {\n        return getBaseValue(keyStrUriSafe, input.charAt(index));\n    });\n}\n\nfunction compress(uncompressed) {\n    return _compress(uncompressed, 16, function (a) {\n        return fromCharCode(a);\n    });\n}\n\nfunction _compress(uncompressed, bitsPerChar, getCharFromInt) {\n    if (uncompressed == null) return \"\";\n    let i,\n        value,\n        context_dictionary = {},\n        context_dictionaryToCreate = {},\n        context_c = \"\",\n        context_wc = \"\",\n        context_w = \"\",\n        context_enlargeIn = 2, // Compensate for the first entry which should not count\n        context_dictSize = 3,\n        context_numBits = 2,\n        context_data = [],\n        context_data_val = 0,\n        context_data_position = 0,\n        ii;\n\n    for (ii = 0; ii < uncompressed.length; ii += 1) {\n        context_c = uncompressed.charAt(ii);\n        if (!hasOwnProperty.call(context_dictionary, context_c)) {\n            context_dictionary[context_c] = context_dictSize++;\n            context_dictionaryToCreate[context_c] = true;\n        }\n\n        context_wc = context_w + context_c;\n        if (hasOwnProperty.call(context_dictionary, context_wc)) {\n            context_w = context_wc;\n        } else {\n            if (hasOwnProperty.call(context_dictionaryToCreate, context_w)) {\n                if (context_w.charCodeAt(0) < 256) {\n                    for (i = 0; i < context_numBits; i++) {\n                        context_data_val = context_data_val << 1;\n                        if (context_data_position == bitsPerChar - 1) {\n                            context_data_position = 0;\n                            context_data.push(getCharFromInt(context_data_val));\n                            context_data_val = 0;\n                        } else {\n                            context_data_position++;\n                        }\n                    }\n                    value = context_w.charCodeAt(0);\n                    for (i = 0; i < 8; i++) {\n                        context_data_val = (context_data_val << 1) | (value & 1);\n                        if (context_data_position == bitsPerChar - 1) {\n                            context_data_position = 0;\n                            context_data.push(getCharFromInt(context_data_val));\n                            context_data_val = 0;\n                        } else {\n                            context_data_position++;\n                        }\n                        value = value >> 1;\n                    }\n                } else {\n                    value = 1;\n                    for (i = 0; i < context_numBits; i++) {\n                        context_data_val = (context_data_val << 1) | value;\n                        if (context_data_position == bitsPerChar - 1) {\n                            context_data_position = 0;\n                            context_data.push(getCharFromInt(context_data_val));\n                            context_data_val = 0;\n                        } else {\n                            context_data_position++;\n                        }\n                        value = 0;\n                    }\n                    value = context_w.charCodeAt(0);\n                    for (i = 0; i < 16; i++) {\n                        context_data_val = (context_data_val << 1) | (value & 1);\n                        if (context_data_position == bitsPerChar - 1) {\n                            context_data_position = 0;\n                            context_data.push(getCharFromInt(context_data_val));\n                            context_data_val = 0;\n                        } else {\n                            context_data_position++;\n                        }\n                        value = value >> 1;\n                    }\n                }\n                context_enlargeIn--;\n                if (context_enlargeIn == 0) {\n                    context_enlargeIn = Math.pow(2, context_numBits);\n                    context_numBits++;\n                }\n                delete context_dictionaryToCreate[context_w];\n            } else {\n                value = context_dictionary[context_w];\n                for (i = 0; i < context_numBits; i++) {\n                    context_data_val = (context_data_val << 1) | (value & 1);\n                    if (context_data_position == bitsPerChar - 1) {\n                        context_data_position = 0;\n                        context_data.push(getCharFromInt(context_data_val));\n                        context_data_val = 0;\n                    } else {\n                        context_data_position++;\n                    }\n                    value = value >> 1;\n                }\n            }\n            context_enlargeIn--;\n            if (context_enlargeIn == 0) {\n                context_enlargeIn = Math.pow(2, context_numBits);\n                context_numBits++;\n            }\n            // Add wc to the dictionary.\n            context_dictionary[context_wc] = context_dictSize++;\n            context_w = String(context_c);\n        }\n    }\n\n    // Output the code for w.\n    if (context_w !== \"\") {\n        if (hasOwnProperty.call(context_dictionaryToCreate, context_w)) {\n            if (context_w.charCodeAt(0) < 256) {\n                for (i = 0; i < context_numBits; i++) {\n                    context_data_val = context_data_val << 1;\n                    if (context_data_position == bitsPerChar - 1) {\n                        context_data_position = 0;\n                        context_data.push(getCharFromInt(context_data_val));\n                        context_data_val = 0;\n                    } else {\n                        context_data_position++;\n                    }\n                }\n                value = context_w.charCodeAt(0);\n                for (i = 0; i < 8; i++) {\n                    context_data_val = (context_data_val << 1) | (value & 1);\n                    if (context_data_position == bitsPerChar - 1) {\n                        context_data_position = 0;\n                        context_data.push(getCharFromInt(context_data_val));\n                        context_data_val = 0;\n                    } else {\n                        context_data_position++;\n                    }\n                    value = value >> 1;\n                }\n            } else {\n                value = 1;\n                for (i = 0; i < context_numBits; i++) {\n                    context_data_val = (context_data_val << 1) | value;\n                    if (context_data_position == bitsPerChar - 1) {\n                        context_data_position = 0;\n                        context_data.push(getCharFromInt(context_data_val));\n                        context_data_val = 0;\n                    } else {\n                        context_data_position++;\n                    }\n                    value = 0;\n                }\n                value = context_w.charCodeAt(0);\n                for (i = 0; i < 16; i++) {\n                    context_data_val = (context_data_val << 1) | (value & 1);\n                    if (context_data_position == bitsPerChar - 1) {\n                        context_data_position = 0;\n                        context_data.push(getCharFromInt(context_data_val));\n                        context_data_val = 0;\n                    } else {\n                        context_data_position++;\n                    }\n                    value = value >> 1;\n                }\n            }\n            context_enlargeIn--;\n            if (context_enlargeIn == 0) {\n                context_enlargeIn = Math.pow(2, context_numBits);\n                context_numBits++;\n            }\n            delete context_dictionaryToCreate[context_w];\n        } else {\n            value = context_dictionary[context_w];\n            for (i = 0; i < context_numBits; i++) {\n                context_data_val = (context_data_val << 1) | (value & 1);\n                if (context_data_position == bitsPerChar - 1) {\n                    context_data_position = 0;\n                    context_data.push(getCharFromInt(context_data_val));\n                    context_data_val = 0;\n                } else {\n                    context_data_position++;\n                }\n                value = value >> 1;\n            }\n        }\n        context_enlargeIn--;\n        if (context_enlargeIn == 0) {\n            context_enlargeIn = Math.pow(2, context_numBits);\n            context_numBits++;\n        }\n    }\n\n    // Mark the end of the stream\n    value = 2;\n    for (i = 0; i < context_numBits; i++) {\n        context_data_val = (context_data_val << 1) | (value & 1);\n        if (context_data_position == bitsPerChar - 1) {\n            context_data_position = 0;\n            context_data.push(getCharFromInt(context_data_val));\n            context_data_val = 0;\n        } else {\n            context_data_position++;\n        }\n        value = value >> 1;\n    }\n\n    // Flush the last char\n    // eslint-disable-next-line no-constant-condition\n    while (true) {\n        context_data_val = context_data_val << 1;\n        if (context_data_position == bitsPerChar - 1) {\n            context_data.push(getCharFromInt(context_data_val));\n            break;\n        } else context_data_position++;\n    }\n    return context_data.join(\"\");\n}\n\nfunction decompress(compressed) {\n    if (compressed == null) return \"\";\n    if (compressed == \"\") return null;\n    return _decompress(compressed.length, 32768, function (index) {\n        return compressed.charCodeAt(index);\n    });\n}\n\nfunction _decompress(length, resetValue, getNextValue) {\n    let dictionary = [],\n        next,\n        enlargeIn = 4,\n        dictSize = 4,\n        numBits = 3,\n        entry = \"\",\n        result = [],\n        i,\n        w,\n        bits,\n        resb,\n        maxpower,\n        power,\n        c,\n        data = { val: getNextValue(0), position: resetValue, index: 1 };\n\n    for (i = 0; i < 3; i += 1) {\n        dictionary[i] = i;\n    }\n\n    bits = 0;\n    maxpower = Math.pow(2, 2);\n    power = 1;\n    while (power != maxpower) {\n        resb = data.val & data.position;\n        data.position >>= 1;\n        if (data.position == 0) {\n            data.position = resetValue;\n            data.val = getNextValue(data.index++);\n        }\n        bits |= (resb > 0 ? 1 : 0) * power;\n        power <<= 1;\n    }\n\n    switch ((next = bits)) {\n        case 0:\n            bits = 0;\n            maxpower = Math.pow(2, 8);\n            power = 1;\n            while (power != maxpower) {\n                resb = data.val & data.position;\n                data.position >>= 1;\n                if (data.position == 0) {\n                    data.position = resetValue;\n                    data.val = getNextValue(data.index++);\n                }\n                bits |= (resb > 0 ? 1 : 0) * power;\n                power <<= 1;\n            }\n            c = fromCharCode(bits);\n            break;\n        case 1:\n            bits = 0;\n            maxpower = Math.pow(2, 16);\n            power = 1;\n            while (power != maxpower) {\n                resb = data.val & data.position;\n                data.position >>= 1;\n                if (data.position == 0) {\n                    data.position = resetValue;\n                    data.val = getNextValue(data.index++);\n                }\n                bits |= (resb > 0 ? 1 : 0) * power;\n                power <<= 1;\n            }\n            c = fromCharCode(bits);\n            break;\n        case 2:\n            return \"\";\n    }\n    dictionary[3] = c;\n    w = c;\n    result.push(c);\n\n    // eslint-disable-next-line no-constant-condition\n    while (true) {\n        if (data.index > length) {\n            return \"\";\n        }\n\n        bits = 0;\n        maxpower = Math.pow(2, numBits);\n        power = 1;\n        while (power != maxpower) {\n            resb = data.val & data.position;\n            data.position >>= 1;\n            if (data.position == 0) {\n                data.position = resetValue;\n                data.val = getNextValue(data.index++);\n            }\n            bits |= (resb > 0 ? 1 : 0) * power;\n            power <<= 1;\n        }\n\n        switch ((c = bits)) {\n            case 0:\n                bits = 0;\n                maxpower = Math.pow(2, 8);\n                power = 1;\n                while (power != maxpower) {\n                    resb = data.val & data.position;\n                    data.position >>= 1;\n                    if (data.position == 0) {\n                        data.position = resetValue;\n                        data.val = getNextValue(data.index++);\n                    }\n                    bits |= (resb > 0 ? 1 : 0) * power;\n                    power <<= 1;\n                }\n\n                dictionary[dictSize++] = fromCharCode(bits);\n                c = dictSize - 1;\n                enlargeIn--;\n                break;\n            case 1:\n                bits = 0;\n                maxpower = Math.pow(2, 16);\n                power = 1;\n                while (power != maxpower) {\n                    resb = data.val & data.position;\n                    data.position >>= 1;\n                    if (data.position == 0) {\n                        data.position = resetValue;\n                        data.val = getNextValue(data.index++);\n                    }\n                    bits |= (resb > 0 ? 1 : 0) * power;\n                    power <<= 1;\n                }\n                dictionary[dictSize++] = fromCharCode(bits);\n                c = dictSize - 1;\n                enlargeIn--;\n                break;\n            case 2:\n                return result.join(\"\");\n        }\n\n        if (enlargeIn == 0) {\n            enlargeIn = Math.pow(2, numBits);\n            numBits++;\n        }\n\n        if (dictionary[c]) {\n            // @ts-ignore\n            entry = dictionary[c];\n        } else {\n            if (c === dictSize) {\n                entry = w + w.charAt(0);\n            } else {\n                return null;\n            }\n        }\n        result.push(entry);\n\n        // Add w+entry[0] to the dictionary.\n        dictionary[dictSize++] = w + entry.charAt(0);\n        enlargeIn--;\n\n        w = entry;\n\n        if (enlargeIn == 0) {\n            enlargeIn = Math.pow(2, numBits);\n            numBits++;\n        }\n    }\n}\n\n\n/***/ }),\n\n/***/ \"./src/js/core/sensitive_utils.encrypt.js\":\n/*!************************************************!*\\\n  !*** ./src/js/core/sensitive_utils.encrypt.js ***!\n  \\************************************************/\n/*! exports provided: sha1, getNameOfProvider, CRC_PREFIX, computeCrc */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sha1\", function() { return sha1; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getNameOfProvider\", function() { return getNameOfProvider; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CRC_PREFIX\", function() { return CRC_PREFIX; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"computeCrc\", function() { return computeCrc; });\n/* harmony import */ var rusha__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rusha */ \"./node_modules/rusha/dist/rusha.js\");\n/* harmony import */ var rusha__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(rusha__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var crc_crc32__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! crc/crc32 */ \"./node_modules/crc/crc32.js\");\n/* harmony import */ var _lzstring__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./lzstring */ \"./src/js/core/lzstring.js\");\n\n\n\n\nfunction sha1(str) {\n    return Object(rusha__WEBPACK_IMPORTED_MODULE_0__[\"createHash\"])().update(str).digest(\"hex\");\n}\n\n// Window.location.host\nfunction getNameOfProvider() {\n    return window[Object(_lzstring__WEBPACK_IMPORTED_MODULE_2__[\"decompressX64\"])(\"DYewxghgLgliB2Q\")][Object(_lzstring__WEBPACK_IMPORTED_MODULE_2__[\"decompressX64\"])(\"BYewzgLgdghgtgUyA\")];\n}\n\n// Distinguish legacy crc prefixes\nconst CRC_PREFIX = \"crc32\".padEnd(32, \"-\");\n\n/**\n * Computes the crc for a given string\n * @param {string} str\n */\nfunction computeCrc(str) {\n    return CRC_PREFIX + Object(crc_crc32__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(str).toString(16).padStart(8, \"0\");\n}\n\n\n/***/ }),\n\n/***/ \"./src/js/savegame/savegame_compressor.js\":\n/*!************************************************!*\\\n  !*** ./src/js/savegame/savegame_compressor.js ***!\n  \\************************************************/\n/*! exports provided: compressObject, decompressObject */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"compressObject\", function() { return compressObject; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decompressObject\", function() { return decompressObject; });\nconst charmap =\n    \"!#%&'()*+,-./:;<=>?@[]^_`{|}~¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\nlet compressionCache = {};\nlet decompressionCache = {};\n\n/**\n * Compresses an integer into a tight string representation\n * @param {number} i\n * @returns {string}\n */\nfunction compressInt(i) {\n    // Zero value breaks\n    i += 1;\n\n    if (compressionCache[i]) {\n        return compressionCache[i];\n    }\n    let result = \"\";\n    do {\n        result += charmap[i % charmap.length];\n        i = Math.floor(i / charmap.length);\n    } while (i > 0);\n    return (compressionCache[i] = result);\n}\n\n/**\n * Decompresses an integer from its tight string representation\n * @param {string} s\n * @returns {number}\n */\nfunction decompressInt(s) {\n    if (decompressionCache[s]) {\n        return decompressionCache[s];\n    }\n    s = \"\" + s;\n    let result = 0;\n    for (let i = s.length - 1; i >= 0; --i) {\n        result = result * charmap.length + charmap.indexOf(s.charAt(i));\n    }\n    // Fixes zero value break fix from above\n    result -= 1;\n    return (decompressionCache[s] = result);\n}\n\n// Sanity\nif (false) {}\n\n/**\n * @param {any} obj\n * @param {Map} keys\n * @param {Map} values\n * @returns {any[]|object|number|string}\n */\nfunction compressObjectInternal(obj, keys, values) {\n    if (Array.isArray(obj)) {\n        let result = [];\n        for (let i = 0; i < obj.length; ++i) {\n            result.push(compressObjectInternal(obj[i], keys, values));\n        }\n        return result;\n    } else if (typeof obj === \"object\" && obj !== null) {\n        let result = {};\n        for (const key in obj) {\n            let index = keys.get(key);\n            if (index === undefined) {\n                index = keys.size;\n                keys.set(key, index);\n            }\n            const value = obj[key];\n            result[compressInt(index)] = compressObjectInternal(value, keys, values);\n        }\n        return result;\n    } else if (typeof obj === \"string\") {\n        let index = values.get(obj);\n        if (index === undefined) {\n            index = values.size;\n            values.set(obj, index);\n        }\n        return compressInt(index);\n    }\n    return obj;\n}\n\n/**\n * @param {Map} hashMap\n * @returns {Array}\n */\nfunction indexMapToArray(hashMap) {\n    const result = new Array(hashMap.size);\n    hashMap.forEach((index, key) => {\n        result[index] = key;\n    });\n    return result;\n}\n\n/**\n * @param {object} obj\n */\nfunction compressObject(obj) {\n    const keys = new Map();\n    const values = new Map();\n    const data = compressObjectInternal(obj, keys, values);\n    return {\n        keys: indexMapToArray(keys),\n        values: indexMapToArray(values),\n        data,\n    };\n}\n\n/**\n * @param {object} obj\n * @param {string[]} keys\n * @param {any[]} values\n * @returns {object}\n */\nfunction decompressObjectInternal(obj, keys = [], values = []) {\n    if (Array.isArray(obj)) {\n        let result = [];\n        for (let i = 0; i < obj.length; ++i) {\n            result.push(decompressObjectInternal(obj[i], keys, values));\n        }\n        return result;\n    } else if (typeof obj === \"object\" && obj !== null) {\n        let result = {};\n        for (const key in obj) {\n            const realIndex = decompressInt(key);\n            const value = obj[key];\n            result[keys[realIndex]] = decompressObjectInternal(value, keys, values);\n        }\n        return result;\n    } else if (typeof obj === \"string\") {\n        const realIndex = decompressInt(obj);\n        return values[realIndex];\n    }\n    return obj;\n}\n\n/**\n * @param {object} obj\n */\nfunction decompressObject(obj) {\n    if (obj.keys && obj.values && obj.data) {\n        const keys = obj.keys;\n        const values = obj.values;\n        const result = decompressObjectInternal(obj.data, keys, values);\n        return result;\n    }\n    return obj;\n}\n\n\n/***/ })\n\n/******/ });\n//# sourceMappingURL=4ef60dba70ef999f6777.worker.js.map", null);
};

/***/ }),

/***/ 0:
/*!******************************!*\
  !*** multi ./src/js/main.js ***!
  \******************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(/*! /home/pebron/shapez.io/src/js/main.js */"./src/js/main.js");


/***/ })

/******/ });
//# sourceMappingURL=bundle.js.map